Metadata-Version: 2.1
Name: cadence-runner
Version: 0.20.0
Summary: CLI tool for autonomous task execution via Claude Code
Keywords: claude,claude-code,cli,automation,agent
Author-Email: Mikhail Drozdetskiy <m.drozdetskiy@gmail.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Project-URL: Homepage, https://github.com/Drozdetskiy/cadence
Project-URL: Repository, https://github.com/Drozdetskiy/cadence
Project-URL: Issues, https://github.com/Drozdetskiy/cadence/issues
Requires-Python: >=3.14
Requires-Dist: typer>=0.9
Requires-Dist: rich>=13.0
Requires-Dist: PyYAML>=6.0
Description-Content-Type: text/markdown

# cadence

[![PyPI version](https://img.shields.io/pypi/v/cadence-runner.svg)](https://pypi.org/project/cadence-runner/) [![Python versions](https://img.shields.io/pypi/pyversions/cadence-runner.svg)](https://pypi.org/project/cadence-runner/) [![License](https://img.shields.io/pypi/l/cadence-runner.svg)](LICENSE) [![CI](https://github.com/Drozdetskiy/cadence/actions/workflows/ci.yml/badge.svg)](https://github.com/Drozdetskiy/cadence/actions/workflows/ci.yml) [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)

Autonomous task-execution pipeline on top of [Claude Code](https://docs.anthropic.com/en/docs/claude-code).

Inspired by the [ralphex](https://ralphex.com/) 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

| Stage | Trigger | What happens |
|---|---|---|
| **plan** | `cadence --plan <file>` | Interactive Q&A with Claude, draft review (accept / revise / reject), final plan written to `<file>-plan.md` |
| **task** | `cadence --task <plan>` | Branch created from plan filename, one `### Task N:` section per iteration, each completed and committed |
| **review** | implicit after `--task`, or `cadence --review` | First pass launches 4 parallel agents (quality, implementation, testing, simplification); subsequent passes loop on critical/major findings until no commits are produced |
| **run** | `cadence --run` | Infers the task directory from the current branch and dispatches to plan creation, plan execution (with `--impl`), or a "already completed" notice — no path argument needed |

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

```bash
# via Homebrew (macOS)
brew tap Drozdetskiy/cadence
brew install cadence

# 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](https://docs.anthropic.com/en/docs/claude-code) on `PATH`
- a git repository (the `--task` and `--review` modes operate on the working tree)

## 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 built into `--plan`, and the prompt-file name 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 --plan, consumed by --task
    config.yaml        # optional per-task overrides (auto-discovered)
```

```bash
# 1. Scaffold a new task: branch + cdc-tasks/<name>/init (+ config.yaml if not on default_branch)
cadence --task-init 0001-my-feature

# 2. Create a plan from a free-form task description
cadence --plan cdc-tasks/0001-my-feature/init
#   → writes cdc-tasks/0001-my-feature/plan

# 3. Create a plan and chain straight into implementation
cadence --plan cdc-tasks/0001-my-feature/init --impl

# 4. Execute an existing plan: branch + tasks + review
cadence --task cdc-tasks/0001-my-feature/plan

# 5. Run the next step for the current branch (auto-detects init/plan/plan-completed)
cadence --run             # creates a plan if only init exists, prints next step otherwise
cadence --run --impl      # chains init → plan → task end-to-end on the current branch

# 6. Review the current branch only (no plan, no branch creation)
cadence --review
cadence --review --base develop                                 # override base branch
cadence --review --config cdc-tasks/0001-my-feature/config.yaml # per-run overrides

# Misc
cadence --version
```

Flag rules:
- `--plan`, `--task`, `--review`, `--task-init`, `--run` are mutually exclusive.
- `--impl` requires `--plan` or `--run` (and is incompatible with `--review` and `--task-init`).
- `--run` accepts `--impl` and `--config` but not `--base`.
- `--base` is only valid with `--review`. Resolution priority: `--base` > `default_branch` in config (defaults to `main`).

### Plan file format

Plans are markdown with a strict structure the runner parses on every iteration:

```markdown
# 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 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.

```yaml
# 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: subject line `<branch-name>.`, then a blank line, then a body
  with one clause per line — `Added: <what>`, `Changed: <what>`,
  `Deleted: <what>`. Include only the lines that apply. ...

# Output
colors:
  task: "#2e8b57"
  review: "#1a9e9e"
  warn: "#d4930d"
  error: "#cc0000"
```

See [`docs/config.md`](docs/config.md) for the full key reference (timeouts, error patterns, color palette).

### Per-run overrides: `--config`

`--config <path>` loads a YAML file that overrides per-phase models and/or `default_branch`. Each key is optional:

```yaml
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 subject. 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: subject line `<branch-name>.`, a blank line, then one short clause per body line — `Added: <what>`, `Changed: <what>`, `Deleted: <what>`. Include only the lines that apply. The subject + blank line + body shape lets GitHub auto-fill the PR title from the subject and the PR description from the body.

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):

```yaml
commit_format: |
  Format: subject line `<branch-name>.`, then a blank line, then a body with
  one clause per line — `Added: <what>`, `Changed: <what>`, `Deleted: <what>`.
  Include only the lines that apply. English.
  Each body line is one short clause describing the user-visible outcome.
  Author as the user — no Co-Authored-By trailer (unless `commit_trailer` is configured).
```

Switch to Conventional Commits:

```yaml
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 > 0` and Claude output matches `claude_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, mode dispatch, 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/`](docs/): `config.md`, `processor.md`, `executor.md`, `git-and-plans.md`, `progress-and-input.md`.

## Development

```bash
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 all `Runner` dependencies (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`](LICENSE).