CLI tool for autonomous task execution via Claude Code
Project description
cadence
Autonomous task-execution pipeline on top of Claude Code.
Inspired by the ralphex project.
cadence drives Claude through a structured loop: plan → branch → iterative implementation → multi-agent code review → review-loop until clean. It is a thin orchestrator — Claude does the work, cadence keeps it on rails (signals, retries, idle/session timeouts, break/resume, per-phase models, git integration).
What it does
| Subcommand | What happens |
|---|---|
cadence init <name> |
Scaffolds a new branch + <tasks_root>/<name>/init (plus config.yaml recording the parent branch when not on default_branch). No Claude. |
cadence run |
Infers the current branch's task directory and dispatches to the next phase (run plan if only init exists, run task if plan exists, "already completed" if plan-completed exists). |
cadence run plan |
Branch-bound plan creation: reads <tasks_root>/<branch>/init and writes plan next to it. |
cadence run task |
Branch-bound task execution: reads <tasks_root>/<branch>/plan and runs the full pipeline (branch already exists). |
cadence plan <path> |
Path-bound plan creation from an arbitrary file: interactive Q&A with Claude, draft review, final plan written to <path>-plan.md (or plan next to init). |
cadence task <path> |
Path-bound task execution on an arbitrary plan: branch created from plan filename, one ### Task N: section per iteration, each completed and committed. |
cadence review [--base <branch>] |
Review-only of the current branch: first pass launches 4 parallel agents (quality, implementation, testing, simplification); subsequent passes loop on critical/major findings until no commits are produced. |
cadence squash |
Squashes all branch commits into one with a Claude-authored message summarizing the diff against the default branch. |
cadence chain <path> |
Reads an ordered list of task names from a file and runs each task end-to-end (plan → task → squash) on its own branch. Pre-flight checks every listed task has a directory + init file; fails fast and leaves the repo on the failing task's branch. |
Compose pipelines with shell && — e.g. cadence run && cadence run && cadence squash to take a fresh task from init through plan, task, and squash.
Phases communicate with the runner via signal markers (e.g. <<<CADENCE:PLAN_READY>>>, <<<CADENCE:ALL_TASKS_DONE>>>, <<<CADENCE:REVIEW_DONE>>>, <<<CADENCE:QUESTION>>>, <<<CADENCE:TASK_FAILED>>>).
Installation
# via Homebrew (macOS)
brew tap drozdetskiy/cadence
brew install drozdetskiy/cadence/cadence # tap-qualified name avoids the homebrew-core `cadence` (Flow smart-contract language) clash
# via PyPI
pip install cadence-runner
# from source (for development)
pdm install
The PyPI distribution is named cadence-runner; the CLI binary is cadence regardless of how you installed it.
Requires:
- Python 3.14+ (Homebrew formula pulls this in automatically)
- Claude Code on
PATH - a git repository (the
task,run,review,squash, andchainsubcommands operate on the working tree)
Note: the cadence init <name> subcommand and the file named init inside the task directory are different things — init (subcommand) scaffolds a task, and init (file) is the free-form description Claude reads. The file name is configurable via init_prompt_name in .cadence/config.yaml.
Usage
Tasks live in their own subdirectory under cdc-tasks/<NNNN-slug>/ (configurable via tasks_root). The free-form description goes into a file named init; the generated plan is written next to it as plan (the init → plan mapping is configurable via init_prompt_name). A per-task config.yaml next to the prompt is auto-discovered.
cdc-tasks/
0001-my-feature/
init # free-form task description (input)
plan # generated by `cadence run plan`, consumed by `cadence run task`
config.yaml # optional per-task overrides (auto-discovered)
# New task end-to-end (branch-bound, with auto-detect)
cadence init 0001-my-feature # scaffolds branch + cdc-tasks/0001-my-feature/init
$EDITOR cdc-tasks/0001-my-feature/init # write the task description
cadence run && cadence run && cadence squash # auto-detect: init → plan, plan → task, then squash
# Same flow, explicit phases
cadence run plan # init → plan
cadence run task # plan → task pipeline (branch + tasks + review)
cadence squash # one final commit summarizing the branch
# Plan from a third-party file (path-bound)
cadence plan path/to/spec.md # writes path/to/spec-plan.md (or `plan` next to `init`)
# Task on an existing plan (path-bound, creates the branch)
cadence task path/to/plan.md
# Review the current branch only (no plan, no branch creation)
cadence review
cadence review --base develop # override base branch
# Per-run overrides apply globally to any subcommand
cadence --config cdc-tasks/0001-my-feature/config.yaml run
# Chain of tasks (sequential plan → task → squash per task, each on its own branch)
cadence chain tasks.txt
# Misc
cadence --version
cadence --help
cadence run --help
Migration from the old flag-style CLI:
| Old | New |
|---|---|
cadence --plan <path> |
cadence plan <path> |
cadence --task <path> |
cadence task <path> |
cadence --review |
cadence review |
cadence --review --base X |
cadence review --base X |
cadence --task-init <name> |
cadence init <name> |
cadence --run |
cadence run |
cadence --run --impl |
cadence run (auto-detect) or explicit cadence run plan && cadence run task |
cadence --plan <path> --impl |
cadence plan <path> && cadence task <derived-plan> |
cadence --squash |
cadence squash |
cadence --task <p> --squash |
cadence task <p> && cadence squash |
cadence --chain <path> |
cadence chain <path> |
Plan file format
Plans are markdown with a strict structure the runner parses on every iteration:
# Title
## Overview
…
## Context
- Files involved: …
### Task 1: Setup
- [ ] step one
- [ ] step two
- [ ] write tests
- [ ] run test suite
### Task 2: …
- [ ] …
### Task N:or### Iteration N:headers delimit work units.- [ ]/- [x]checkboxes inside Task sections drive progress.- Checkboxes outside Task sections (Overview, Context, Success criteria) do not block completion.
- After a successful
task/run taskrun the file is renamed in place to<stem>-completed<ext>(no commit — plan files are typically gitignored).
Configuration
Local config: .cadence/config.yaml
Project-scoped, no global config. Loaded from cwd (or CADENCE_CONFIG_DIR). All keys are optional — missing keys fall back to defaults.
# Claude executor
claude_command: claude
claude_args: "--dangerously-skip-permissions --verbose"
plan_model: claude-opus-4-7
task_model: claude-opus-4-7
review_model: claude-opus-4-7
# Iteration / timing
iteration_delay_ms: 2000
task_retry_count: 1
max_iterations: 50
session_timeout: "0" # "30m", "1h30m", … 0 = disabled
idle_timeout: "0"
wait_on_limit: "0" # >0 → retry on rate-limit instead of failing
# VCS / paths
tasks_root: cdc-tasks # root for per-task subdirectories (init/plan/config.yaml)
default_branch: main # override per-project in local config
commit_trailer: "" # appended to all cadence-made commits
commit_format: | # appended to task/review prompts (default shown below)
Format: a single line `<branch-name>. <Clause>: <what>.` where `<Clause>`
is `Added`, `Changed`, or `Deleted`. Clauses joined by `. ` (period + space);
items inside one clause joined by `; ` (semicolon + space). ...
# Output
colors:
task: "#2e8b57"
review: "#1a9e9e"
warn: "#d4930d"
error: "#cc0000"
See docs/config.md for the full key reference (timeouts, error patterns, color palette).
Per-run overrides: --config
The global --config <path> (placed before the subcommand: cadence --config X.yaml run) loads a YAML file that overrides per-phase models and/or default_branch. Each key is optional:
plan:
model: claude-opus-4-7
task:
model: claude-opus-4-7
review:
model: claude-opus-4-7
default_branch: develop
If --config is omitted, cadence auto-discovers config.yaml next to the plan/task file — typically cdc-tasks/<NNNN-slug>/config.yaml (no parent walk). For review (no plan/task file) auto-discovery is skipped — only an explicit --config is honored. An explicit path that does not exist is a hard error; an auto-discovered missing file is silently ignored. YAML parse errors are always fatal.
Commit message format
commit_format is appended verbatim to every task and review prompt, telling Claude how to write the commit message. Plan creation does not commit, so the format is not added there.
The built-in default produces messages like:
0014-no-plan-commit-on-start. Changed: cadence no longer auto-commits the plan file when starting a task. Deleted: now-unused commit_plan_file; file_has_changes helpers.
Shape: a single line <branch-name>. <Clause>: <what>. where <Clause> is Added, Changed, or Deleted. Multiple clauses are joined by . (period + space); multiple items inside one clause are joined by ; (semicolon + space). Include only the clauses that apply.
Override from .cadence/config.yaml with any free-form text. Example of a tighter restatement (the shipped default also includes Good/Bad examples and guidance about implementation details belonging in the diff — see Config.commit_format in src/cadence/config.py for the verbatim text):
commit_format: |
Format: a single line `<branch-name>. <Clause>: <what>.` where `<Clause>` is
`Added`, `Changed`, or `Deleted`. Clauses joined by `. ` (period + space);
items inside one clause joined by `; ` (semicolon + space). English.
Each clause is one short item describing the user-visible outcome.
Author as the user — no Co-Authored-By trailer (unless `commit_trailer` is configured).
Switch to Conventional Commits:
commit_format: |
Use Conventional Commits: <type>(<scope>): <subject>
Types: feat, fix, refactor, docs, test, chore.
Subject is imperative, lowercase, no trailing period, ≤72 chars.
Example: feat(executor): add idle-timeout retry
If you need finer control than a free-form block (e.g. different wording per phase), drop a custom task.txt / review_first.txt / review_second.txt under .cadence/prompts/ — the format block is appended to whatever prompt body you supply.
Customizing prompts and agents
cadence ships with embedded defaults under src/cadence/defaults/. To customize, drop replacements into the project:
.cadence/
config.yaml
prompts/
make_plan.txt # overrides plan-creation prompt
task.txt # overrides task-iteration prompt
review_first.txt # overrides initial review prompt
review_second.txt # overrides review-loop prompt
agents/
quality.txt # custom review agents (referenced as {{agent:quality}})
implementation.txt
testing.txt
simplification.txt
my-extra-agent.txt # add new agents — auto-discovered
Per-file fallback: if a local file is empty or contains only # comments, the embedded default is used. Agents support optional YAML frontmatter:
---
model: sonnet # haiku | sonnet | opus (or full IDs, normalized)
agent: code-reviewer # subagent type for the Task tool
---
Agent prompt body…
Prompts can reference agents inline with {{agent:name}}; the runner expands these into full Task tool invocations, with base variables ({{PLAN_FILE}}, {{DEFAULT_BRANCH}}, etc.) substituted into the agent body.
Runtime controls
- Ctrl+C — graceful shutdown (twice within 5s force-exits).
- Ctrl+\ (
SIGQUIT, Unix) — break the current task; the runner kills the active Claude session and prompts to resume or abort. Resume restarts the same task with a fresh session and re-reads the plan file. - Rate limits — if
wait_on_limit > 0and Claude output matchesclaude_limit_patterns, cadence sleeps and retries indefinitely until cancellation. - Session / idle timeouts — kill stuck sessions; review-loop iterations skip the no-commit detection if the previous session timed out.
Project layout
src/cadence/
cli.py Typer entrypoint, subcommand dispatch (init/run/plan/task/review/squash/chain), signal handling
config.py Config dataclass, YAML loading, --config overrides
status.py Phase / Mode / Signal constants
input.py Interactive Q&A collector
executor/ Claude subprocess + JSON-stream parsing
git/ Service layer over `git` CLI
plan/ Markdown plan parser, branch-name extraction
processor/ Runner — orchestrates plan/task/review phases
progress/ File+stdout logger with colors and flock
defaults/
prompts/ Embedded prompt templates
agents/ Embedded review agents
Deeper module references live in docs/: config.md, processor.md, executor.md, git-and-plans.md, progress-and-input.md.
Development
make install # pdm install --dev
make test # pytest tests/ -v
make test-cov # with coverage
make lint # ruff check
make typecheck # mypy --strict
make check # lint + typecheck + test
Conventions:
- Python 3.14+,
mypy --strict. Protocol-based interfaces for allRunnerdependencies (Executor, Logger, InputCollector, GitChecker) — tests mock these directly, never the real Claude CLI or a real git repo.- Signals are literal strings of the form
<<<CADENCE:NAME>>>and matched against raw non-JSON CLI output only (so the same literal inside stream-json events does not trigger false positives).
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cadence_runner-0.21.0.tar.gz.
File metadata
- Download URL: cadence_runner-0.21.0.tar.gz
- Upload date:
- Size: 92.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.26.8 CPython/3.14.4 Darwin/25.4.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e9f59d1b692c8f2e84d23c23c78ab8556a999d423c7e9d9d927f769d549b1d9
|
|
| MD5 |
f58e6320a97523cca8c14e07ce7944e2
|
|
| BLAKE2b-256 |
7945386d91576bcacd592c32219782bc611313aac6434153f5bf6253675113f0
|
File details
Details for the file cadence_runner-0.21.0-py3-none-any.whl.
File metadata
- Download URL: cadence_runner-0.21.0-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: pdm/2.26.8 CPython/3.14.4 Darwin/25.4.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b38f85f891fdea8aace7f0e6466f6fb39ac54a281bb57073d47f2b03fb89d47f
|
|
| MD5 |
d0a8a6ae40744fd3860cde260088d58e
|
|
| BLAKE2b-256 |
9c6596eb307c11d09fb4345182d3d54725bdcea11596fa17cdd26bec9f64b292
|