Skip to content

About

Cyclopsctl, a lightweight Cursor task orchestrator CLI. It sequences implement → update agent runs, routes models by complexity, verifies handover advancement, and shows a Rich dashboard. Workflow intelligence stays in Markdown prompts and the native task queue; the orchestrator handles runtime glue.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Cyclopsctl: Cursor Task Orchestrator

GitHub Python License: MIT Built with Cursor Cursor SDK Platform PRs Welcome AI-generated

Run Cursor agents in structured cycles from your terminal. Implement, verify, repeat.

Cyclopsctl is a Python CLI that sequences Cursor agent runs to execute tasks from a PRD end-to-end. You write a product requirements doc (prd.md), run cyclopsctl init, and let the orchestrator drive implement → update → verify cycles until the queue is empty.

Each cycle is one task. The orchestrator picks the task, routes the right model based on complexity, runs a two-phase workflow (implement + update in the same agent session), then verifies the handover file advanced before moving on. It fails closed: if the handover didn't change, the run stops rather than silently repeating the same task.

Who it's for: Solo developers and small teams building with Cursor who want to turn a solid PRD into working software with minimal manual chaining of agent runs. File-based continuity (current-handover-prompt.md, ai-context.md) keeps context intact across sessions.

Live dashboard during a cyclopsctl run

The codebase is 100% AI-generated (Python, docs, and config), built with the same AI-assisted workflow used for Ferrite.


Tips for best results

Write a detailed PRD. The quality of your output depends heavily on the quality of your prd.md. Vague requirements produce vague tasks; detailed requirements produce precise, actionable tasks. We recommend drafting your PRD with Opus before running cyclopsctl: use it interactively to think through features, edge cases, tech stack choices, and scope. The better the PRD, the better the parsed tasks, the better the results. See prd.example.md for a full annotated example showing what to include and why.

Build in phases, not all at once. Don't try to build a large application in a single PRD. Start with a focused v1 scope, build it, validate it, then write a second PRD for the next phase. Phase 2 PRDs are a great place to include bug fixes, refinements, and new features based on what you learned from phase 1. Cyclopsctl supports multi-phase builds natively with tags, so each phase gets its own task queue while keeping previous phase history intact.


How it works

One cycle in detail

You start with a prd.md. cyclopsctl init parses it into a task queue, scores each task for complexity, and prepares the first handover file. cyclopsctl launch then drives the loop: for each task, a new Cursor agent implements it with targeted tests, cyclopsctl runs the project's full verification command once (capped, logged, failures handed back to the same agent), the same agent session handles the update (marks the task done and prepares the next handover), and the orchestrator verifies progress before continuing.

The handover file (current-handover-prompt.md) is the contract between cycles; it carries the task context forward. The orchestrator snapshots it before the update phase and checks that the Task ID and content actually changed. If they didn't, the run fails rather than silently looping.


Quick start

Requires Python 3.10+ and a CURSOR_API_KEY.

1. Install

From GitHub (no clone needed):

pip install "cyclopsctl @ git+https://github.com/OlaProeis/Cyclopsctl.git@v0.1.11"

To track master instead of a release tag, omit @v0.1.11.

Or use the install scripts (after cloning):

# Windows
.\install.ps1

# macOS / Linux
./install.sh

Verify the install:

cyclopsctl --version

Copy .env.example to .env in your project and set CURSOR_API_KEY. Cyclopsctl loads it automatically from the project root.


2. New project

In your project repo, write a prd.md describing what you want to build (see prd.example.md for structure — init copies this annotated example into your project), then:

cyclopsctl init      # parse PRD → task queue → first handover
cyclopsctl launch    # start the implement → update cycles

init scaffolds cyclopsctl.toml, workflow files, and prd.example.md, parses prd.md into the native task queue, runs complexity scoring, and writes the first handover. launch runs preflight checks, shows queue status, lets you confirm options, and starts the cycles.


3. Existing project

cd /path/to/your-project
cyclopsctl init      # idempotent, repairs and catches up as needed
cyclopsctl launch

If the project is already initialized and has pending tasks, launch alone is enough. If you've changed your prd.md, launch detects it and offers to re-parse into a new phase.


What the orchestrator does (and doesn't do)

The orchestrator does The orchestrator does not
Run implement → update cycles with handover verification Plan tasks or edit tasks.json during cycles
init: scaffold, PRD parse, complexity scoring, handover sync Rewrite handover templates
Interactive launch and direct run Choose the next task (the update agent does that)
Route models by complexity score Manage nested task hierarchies
Inject ai-context.md and project facts into implementation prompts Replace the update agent's handover or doc duties
Recover empty-detail run errors on the same agent before retrying Run builds or tests during the agent's implementation (agents run targeted tests)
Run the project's full verification command once per task after implementation, capped and logged, and hand failures back to the agent
Enforce per-phase time budgets and host-aware caps on build/test parallelism and memory
Treat the task queue as the source of truth on resume; recover unclean exits and bridge crashes
Collect durable project facts automatically (.cyclopsctl/memory/facts.jsonl)
Preflight via doctor, Rich dashboard, resume history
bootstrap: explicit PRD re-parse when needed

Task planning and workflow rules live in your prompt files. The update agent marks tasks done, updates docs, and writes the next handover. If the update agent overruns its budget, cyclopsctl finishes that bookkeeping itself rather than re-implementing the task.


Prerequisites

  • Python 3.10+
  • CURSOR_API_KEY — set in your project's .env or as an environment variable (auto-loaded; pass --no-env to skip)
  • cursor-sdk — installed automatically with this package
  • A project with prd.md — cyclopsctl init creates the required workflow files (current-handover-prompt.md, update-handover-prompt.md, ai-context.md) when they're missing

On Windows, cyclopsctl bootstraps the Cursor SDK bridge automatically (managed_sdk_bridge) for launch, run, init, bootstrap, models, and analyze-complexity. This avoids Python 3.11 crashes when cursor-sdk auto-launches its local bridge (os.get_blocking / pipe discovery).

Task state lives under .cyclopsctl/tasks/ (with a tasks.json.lock sidecar so the agent's set-status and the orchestrator never interleave writes) and per-tag complexity reports under .cyclopsctl/reports/<tag>/. Use cyclopsctl tasks to inspect and manage the queue.


CLI reference

Commands

Command Description
cyclopsctl Default: interactive launcher (launch)
cyclopsctl launch Preflight + Rich menu, spawn run
cyclopsctl init Scaffold cyclopsctl.toml, workflow stubs, .gitignore
cyclopsctl bootstrap PRD → parse → analyze → sync handover
cyclopsctl analyze-complexity Score pending tasks on the active tag (no PRD parse)
cyclopsctl run Run N verified implement → update cycles
cyclopsctl doctor / check Preflight diagnostics: API key, SDK bridge, queue/handover drift, stale workflow templates, missing [resources] on heavy build systems, oversized ai-context.md
cyclopsctl tasks Task queue CRUD: list, list pending / done, show, next, set-status, tags, use-tag
cyclopsctl memory Project facts: add --task-id N --text "…", list [--task N] [--tag T]
cyclopsctl status Crash-recovery state (pid, phase, tag) and run history summary
cyclopsctl models List Cursor models and routing preset availability

Shared flags on most commands: --no-env, --config, --project-root (defaults to cwd), --plain.

init

Flag Description
--project-root PATH Target repository (default: cwd)
--profile NAME Merge named profile defaults into seeded cyclopsctl.toml
--skip-templates Write config only; skip workflow stubs
--dry-run List paths that would be written
--force PATH / --force-all Overwrite existing scaffold files
--attach Attach to existing queue without PRD (brownfield)
--yes Skip interactive confirms

bootstrap

Flag Description
--from-prd PATH PRD file (default: prd.md)
--with-workflow Generate PRD-aware ai-context.md, handover template, docs/index.md
--sync-handover-only Regenerate handover from existing tasks
--skip-analyze Skip complexity analysis after parse-prd
--append Append tasks when re-parsing PRD

analyze-complexity

Score pending tasks via Cursor SDK and write .cyclopsctl/reports/<tag>/complexity-report.json (mirrored to .cyclopsctl/reports/complexity-report.json), so a new phase never inherits the previous phase's scores through colliding ids. Does not parse a PRD — use after a new phase tag when tasks lack complexity, or when init refuses with PRD changed — run cyclopsctl launch.

cyclopsctl tasks use-tag phase-2   # if needed
cyclopsctl analyze-complexity
Flag Description
--project-root PATH Target repository (default: cwd)
--tag NAME Tag to analyze (default: active tag)
--analyze-model MODEL Override analyze model (default: auto)
--skip-if-exists Skip when complexity report already exists

By default this command re-runs analysis and replaces a stale report (e.g. from a prior phase). See docs/cli/analyze-complexity-cli.md.

run

Flag Description
--config FILE Load cyclopsctl.toml
--cycles N Number of cycles to run
--project-root PATH Target repository (default: cwd)
--profile NAME Merge profile defaults (CLI overrides profile)
--tag NAME Task tag context
--task-source MODE handover (default) or sequential
--task-id N Pin the next cycle to a specific pending task
--strict-handover Fail when handover Task ID differs from selected task
--resume Skip tasks from run history that the queue also says are done (see Recovery)
--fresh Force first-prompt bootstrap; ignore ready handover
--plain Disable Rich dashboard; plain text logs
--retry-on off Disable default transient retry (SDK blips, same-agent continue, and fresh-agent retry on empty-detail run errors)
--implementation-timeout-minutes N / --update-timeout-minutes N Wall-clock budget per phase (defaults 120 / 20; 0 disables)
--idle-timeout-minutes N / --test-timeout-minutes N Idle budget (default 30) and advisory per-test-command budget rendered into the handover (default 15)
--max-build-jobs N / --max-test-threads N Cap build parallelism (CARGO_BUILD_JOBS, MAKEFLAGS, GOFLAGS, …) and test threads for everything the agent runs
--max-memory-gb GB Hard memory ceiling for the whole agent process tree (Windows Job Object; default 65 % of RAM)
--verification-run-by WHO Who runs the full verification command once per cycle: orchestrator (default), agent, or off
--verification-command CMD / --verification-timeout-minutes N Override the handover's ## Verification command and its wall-clock limit (default 30)
--no-state / --no-history Disable state.json / run-history.json persistence
--dry-run Resolve task and model without starting an agent

See cyclopsctl.toml.example for full TOML options including [routing] rules, [profile.*] presets, [tasks] parse/analyze models, [budget], [resources], [verification], [memory], and task_source.

Run budgets and resource caps

Big projects have test suites and builds that run for 30–60 minutes. Cyclopsctl is built so that is survivable:

  • Per-phase budgets ([budget]): the implementation run gets 120 min and the update run 20 min by default. A watchdog cancels the agent on breach, leaves the task pending, records a runtime fact so the next attempt knows, and never retries that attempt.
  • Tool-aware idle detection: the idle budget (30 min) only counts time with no agent activity and no tool call in flight. A long cargo test or VM build inside a tool call is not "idle"; only the wall-clock deadline bounds it.
  • Cyclopsctl runs the full suite, not the agent ([verification]): agents are told to run only targeted tests for the files they touched. After the implementation phase cyclopsctl runs the project's verification command (## Verification block or [verification] command) once, as a low-priority process inside the memory cap, with output in .cyclopsctl/logs/verify-cycle-N.log. A red run goes back to the same agent as a bounded fix follow-up (max_fix_attempts, default 1); a suite that stays red is recorded as a runtime fact and the cycle continues (on_failure = "stop" to halt instead). run_by = "agent" restores the old behaviour.
  • Update phase is bookkeeping only. Tests and builds never belong in the update phase. If the update agent overruns its budget anyway, cyclopsctl marks the task done and writes the next handover itself instead of failing the cycle. launch, init and doctor detect an update-handover-prompt.md or ai-context.md from an older template (still telling the agent to run the suite, pick the "lowest numeric" task, or rewrite the handover) and launch refreshes them automatically, keeping <file>.bak and preserving everything below the rules blocks of ai-context.md.
  • Bridge timeout follows the budget: on Windows the SDK bridge RPC timeout is raised to the longest phase deadline + 15 min automatically, so the transport never gives up before the budget does (explicit CYCLOPSCTL_BRIDGE_TIMEOUT_SECONDS wins).
  • Host-aware resource caps ([resources]): before the agent is spawned, cyclopsctl exports CARGO_BUILD_JOBS, RUST_TEST_THREADS, MAKEFLAGS, CMAKE_BUILD_PARALLEL_LEVEL, GOFLAGS, PYTEST_XDIST_AUTO_NUM_WORKERS and NODE_OPTIONS. Defaults are derived from the machine: build jobs = min(half the CPUs, RAM / 3 GB) and never above the project's own .cargo/config.toml [build] jobs; 2 test threads; 4 GB Node heap. Operator-exported values win. On Windows max_memory_gb defaults to 65 % of RAM (hosts with ≥ 8 GB) and adds a hard Job Object ceiling over bridge → agent → every child, plus a per-process cap of half that, so a runaway linker dies instead of the host swapping. doctor warns when a Cargo/CMake/Make/Go project has no explicit [resources] section.
  • Bridge deaths are treated as host pressure, not network blips. When the local cursor-sdk-bridge dies mid-run (ReadError/WinError 10054), cyclopsctl kills the orphaned agent/build tree it left behind (cargo, rustc, link.exe…), waits for free RAM to recover before relaunching, prefixes the retry prompt with a resume note when the previous attempt already left edits on disk, and stops with a clear message after the second death in one cycle instead of starting a third build on the same box.
  • Durable logs under .cyclopsctl/logs/: cycles.jsonl (default cycle_log), cyclopsctl.log (orchestrator INFO+), bridge.log (bridge stderr / exit codes) and the per-cycle verification logs, so an overnight failure is diagnosable after the dashboard scrollback is gone.

The effective host, budgets, caps and verification owner are logged at run start and rendered into the handover's Test & Resource Budget block so the agent does not override them. Details: docs/runtime/run-budget.md, docs/runtime/resource-caps.md, docs/runtime/orchestrator-verification.md.

Project memory

Durable facts about the project (.cyclopsctl/memory/facts.jsonl) are injected into implementation prompts alongside ai-context.md, preferring facts recorded for the same task. No human step is needed:

  • the update agent adds facts with cyclopsctl memory add --task-id N --text "…" (deduplicated);
  • after the update phase, if no fact was added and the task is complex enough, one short follow-up on the same agent asks for 1–3 facts ([memory] nudge);
  • the orchestrator records runtime facts itself: budget breaches, productive run drops, premium-model fallbacks, handover-restore violations.

Inspect with cyclopsctl memory list [--task N] [--tag runtime]. See docs/workflow/memory-automation.md.

tasks

Inspect and update the native queue under .cyclopsctl/tasks/. Run from the project root or pass --project-root.

Command Description
cyclopsctl tasks list All tasks: Rich table with ID, title, status, complexity, dependencies
cyclopsctl tasks list pending Non-done tasks
cyclopsctl tasks list done Completed tasks only
cyclopsctl tasks list <status> Filter by status (in-progress, review, cancelled, blocked, deferred)
cyclopsctl tasks show <id> Full task record (--format json for scripts)
cyclopsctl tasks next Lowest in-progress task (work a crashed cycle left behind), else lowest pending task whose dependencies are done; explains why when nothing is eligible
cyclopsctl tasks set-status --id=<id> --status=done Update status
cyclopsctl tasks tags List tags/phases with total / done / pending counts
cyclopsctl tasks use-tag <name> Switch the active tag context

Shared flags: --project-root, --tag, --format json|plain. Full reference: docs/tasks/tasks-cli.md.

Tags, phases, and profiles

  • Tags group tasks by phase. Pass --tag NAME on run / launch, or set tag = "..." in cyclopsctl.toml. Inspect and switch with cyclopsctl tasks tags / cyclopsctl tasks use-tag <name>.
  • Multi-phase PRDs: each PRD/phase maps to its own tag. Start the next phase with cyclopsctl launch --prd prd-phase2.md — launch parses into a fresh tag, runs complexity scoring (even when a prior-phase report exists), syncs a clean handover, and keeps previous phases as history. If tasks on a new tag have no complexity scores, run cyclopsctl analyze-complexity (do not use init after a PRD change on a mature project). See docs/cli/launch-prd-change.md and docs/cli/analyze-complexity-cli.md.
  • Profiles: named [profile.solo-default] tables in cyclopsctl.toml; seed via cyclopsctl init --profile NAME. CLI flags override profile values.

Model routing

Reads .cyclopsctl/reports/complexity-report.json:

Complexity score Default model
1–5 Composer (composer_tier: standard or fast)
6–8 Grok 4.7 (grok_tier: standard or fast; default standard / not-fast)
9–10 Fable 5.1 high-thinking (falls back to Grok if Fable is disabled or unavailable)

Configure via [routing] in cyclopsctl.toml (composer_tier, grok_tier, fable_enabled, score-band [[routing.rules]]), CLI flags (--composer-tier, --grok-tier, --no-fable), or interactive cyclopsctl launch prompts. Full guide: docs/runtime/model-routing.md. Inspect presets with cyclopsctl models.

Transient recovery: by default, empty-detail run errors (infrastructure drops with no SDK message) trigger one same-agent continue on the live handle before a fresh-agent cycle retry. Cyclopsctl fingerprints the git worktree at cycle start; if the tree changed before the drop, the agent was mid-work (work is already on disk) and the fresh-agent retry is suppressed instead of re-running the whole implementation. Fresh retries for empty-detail errors are capped at one — see docs/runtime/transient-retry.md.

Recovery, resume, and run history

The task queue (.cyclopsctl/tasks/tasks.json) is the source of truth. History and state files are caches that cyclopsctl reconciles against it; you never edit them by hand.

  • Run history is scoped by tag. run-history.json records which tasks a verified cycle finished for which phase. Task ids restart at 1 on every PRD, so a record from an earlier phase can never skip work in the next one. Legacy untagged records are intersected with the queue and stamped at startup.
  • --resume skips only what the queue confirms. An id in history is skipped only when tasks show says the task is done on the active tag; otherwise the task runs and the stale id is dropped. Ids are written per verified cycle, after the store says done. launch turns --resume on by itself only when the history record is tagged for the active phase.
  • Nothing runnable ≠ phase complete. If pending tasks remain but none is runnable (excluded by resume, or blocked on dependencies), the run stops with TaskQueueBlockedError (exit 2) naming the excluded and blocked ids. It never reports "Task queue empty" while work remains.
  • Unclean exits are recovered as interrupted. state.json records the orchestrator pid. On the next launch a running record whose process is gone becomes interrupted, and the task — which was set to in-progress at cycle start — is picked up first by tasks next and handover selection.
  • Ctrl+C cancels the in-flight run and closes the agent (exit 130); a second Ctrl+C hard-kills the whole bridge process tree.

cyclopsctl status shows both files. Details: docs/runtime/run-history.md, docs/runtime/crash-recovery-state.md, docs/tasks/empty-queue-completion.md.

Exit codes

Code Meaning
0 Completed cycles, empty queue stop, or successful doctor/status
1 Startup / configuration failure
2 Agent run failure, handover verification failure, budget breach, or queue blocked with pending tasks
130 Interrupted (Ctrl+C): agent closed, state preserved

Handover file conventions

File Role
current-handover-prompt.md Next implementation task; must include # Task ID: <n>; rewritten by the update agent each cycle
update-handover-prompt.md Fixed template; passed through unchanged by the orchestrator. Bookkeeping only: must not run tests or builds (doctor flags it if it does)
ai-context.md Prepended to implementation prompts; carries phase rules and project context for agents
.cyclopsctl/memory/facts.jsonl Durable project facts, injected into implementation prompts; maintained by agents and the orchestrator
.cursor/skills/cyclopsctl/SKILL.md Cursor agent skill for ad-hoc task/CLI help outside runs; installed on init, commit in your repo

See ai-context.md in this repo for the canonical rule set.


Project layout

Cyclopsctl/
├── src/cyclopsctl/            # Python package
├── install.ps1 / install.sh   # Global install scripts
├── docs/                      # Feature-level technical docs (see docs/index.md)
│   ├── guides/                # User guides and technical brief
│   ├── setup/                 # Install, init, config, bootstrap
│   ├── workflow/              # Handover and workflow file generation
│   ├── tasks/                 # Native task queue and PRD parse
│   ├── runtime/               # Run loop, agents, observability
│   ├── cli/                   # CLI command reference
│   ├── testing/               # Regression and readiness docs
│   └── prd.md                 # Product requirements (this repo)
├── prompts/                   # Bootstrap templates
├── .cursor/skills/cyclopsctl/ # Operator skill (committed here; copied to target repos on init)
├── cyclopsctl.toml.example
├── ai-context.md              # Agent phase rules
└── current-handover-prompt.md # Next implementation task (update agent rewrites)

Module-level documentation: docs/index.md. Agent-oriented overview: docs/guides/technical-brief.md.


Development and testing

pip install -e ".[dev]"
python -m pytest

Windows: if pip install -e . fails with WinError 32 (cyclopsctl.exe in use), close terminals running cyclopsctl and run:

Get-Process cyclopsctl -ErrorAction SilentlyContinue | Stop-Process -Force
pip install -e .

While the exe is locked, use python -m cyclopsctl … to run the editable build. See docs/setup/package-distribution.md.

For manual validation on a fresh directory or an existing repository, follow docs/guides/testing-guide.md. It covers greenfield init → launch, brownfield attach/continue scenarios, and automated pytest regression.


Design philosophy

  1. Orchestrate runs, not plans. Sequence agent cycles, routing, and verification; task content lives in prompts and the native queue.
  2. Scenario-first adoption. init and launch are the default path; run and flags are there for automation and power users.
  3. File-based continuity. Prompts, handovers, and ai-context.md are the workflow contract between sessions.
  4. Two-phase discipline. Implementation agents implement and run targeted tests; cyclopsctl runs the full suite once; update agents mark done and advance handovers.
  5. The host sets the limits, not the tool. Build jobs, test threads and memory caps are derived from the machine cyclopsctl runs on and from the project's own build config; explicit [resources] values override them.
  6. Fail closed on stuck handovers. Better to stop than to silently loop the same task.
  7. The queue is the truth. History and state files are caches; when they disagree with tasks.json, cyclopsctl heals them and never declares a phase complete while tasks remain.

Full product requirements: docs/prd.md.


License

MIT, see LICENSE.

About

Cyclopsctl, a lightweight Cursor task orchestrator CLI. It sequences implement → update agent runs, routes models by complexity, verifies handover advancement, and shows a Rich dashboard. Workflow intelligence stays in Markdown prompts and the native task queue; the orchestrator handles runtime glue.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages