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.
The codebase is 100% AI-generated (Python, docs, and config), built with the same AI-assisted workflow used for Ferrite.
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.
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.
Requires Python 3.10+ and a CURSOR_API_KEY.
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.shVerify the install:
cyclopsctl --versionCopy .env.example to .env in your project and set CURSOR_API_KEY. Cyclopsctl loads it automatically from the project root.
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 cyclesinit 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.
cd /path/to/your-project
cyclopsctl init # idempotent, repairs and catches up as needed
cyclopsctl launchIf 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.
| 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.
- Python 3.10+
CURSOR_API_KEY— set in your project's.envor as an environment variable (auto-loaded; pass--no-envto skip)cursor-sdk— installed automatically with this package- A project with
prd.md—cyclopsctl initcreates 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.
| 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.
| 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 |
| 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 |
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.
| 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.
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 testor 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 (## Verificationblock 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,initanddoctordetect anupdate-handover-prompt.mdorai-context.mdfrom an older template (still telling the agent to run the suite, pick the "lowest numeric" task, or rewrite the handover) andlaunchrefreshes them automatically, keeping<file>.bakand preserving everything below the rules blocks ofai-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_SECONDSwins). - Host-aware resource caps (
[resources]): before the agent is spawned, cyclopsctl exportsCARGO_BUILD_JOBS,RUST_TEST_THREADS,MAKEFLAGS,CMAKE_BUILD_PARALLEL_LEVEL,GOFLAGS,PYTEST_XDIST_AUTO_NUM_WORKERSandNODE_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 Windowsmax_memory_gbdefaults 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.doctorwarns 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-bridgedies 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(defaultcycle_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.
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.
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 group tasks by phase. Pass
--tag NAMEonrun/launch, or settag = "..."incyclopsctl.toml. Inspect and switch withcyclopsctl 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, runcyclopsctl analyze-complexity(do not useinitafter a PRD change on a mature project). Seedocs/cli/launch-prd-change.mdanddocs/cli/analyze-complexity-cli.md. - Profiles: named
[profile.solo-default]tables incyclopsctl.toml; seed viacyclopsctl init --profile NAME. CLI flags override profile values.
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.
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.jsonrecords 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. --resumeskips only what the queue confirms. An id in history is skipped only whentasks showsays the task isdoneon the active tag; otherwise the task runs and the stale id is dropped. Ids are written per verified cycle, after the store saysdone.launchturns--resumeon 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(exit2) naming the excluded and blocked ids. It never reports "Task queue empty" while work remains. - Unclean exits are recovered as interrupted.
state.jsonrecords the orchestrator pid. On the next launch arunningrecord whose process is gone becomesinterrupted, and the task — which was set toin-progressat cycle start — is picked up first bytasks nextand 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.
| 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 |
| 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.
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.
pip install -e ".[dev]"
python -m pytestWindows: 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.
- Orchestrate runs, not plans. Sequence agent cycles, routing, and verification; task content lives in prompts and the native queue.
- Scenario-first adoption.
initandlaunchare the default path;runand flags are there for automation and power users. - File-based continuity. Prompts, handovers, and
ai-context.mdare the workflow contract between sessions. - Two-phase discipline. Implementation agents implement and run targeted tests; cyclopsctl runs the full suite once; update agents mark done and advance handovers.
- 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. - Fail closed on stuck handovers. Better to stop than to silently loop the same task.
- 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.
MIT, see LICENSE.

