cadence-runner 0.19.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cadence_runner-0.19.2/LICENSE +21 -0
- cadence_runner-0.19.2/PKG-INFO +289 -0
- cadence_runner-0.19.2/README.md +263 -0
- cadence_runner-0.19.2/pyproject.toml +94 -0
- cadence_runner-0.19.2/src/cadence/__init__.py +1 -0
- cadence_runner-0.19.2/src/cadence/cli.py +627 -0
- cadence_runner-0.19.2/src/cadence/config.py +227 -0
- cadence_runner-0.19.2/src/cadence/defaults/__init__.py +0 -0
- cadence_runner-0.19.2/src/cadence/defaults/agents/__init__.py +0 -0
- cadence_runner-0.19.2/src/cadence/defaults/agents/implementation.txt +26 -0
- cadence_runner-0.19.2/src/cadence/defaults/agents/quality.txt +37 -0
- cadence_runner-0.19.2/src/cadence/defaults/agents/simplification.txt +50 -0
- cadence_runner-0.19.2/src/cadence/defaults/agents/testing.txt +51 -0
- cadence_runner-0.19.2/src/cadence/defaults/prompts/__init__.py +0 -0
- cadence_runner-0.19.2/src/cadence/defaults/prompts/make_plan.txt +137 -0
- cadence_runner-0.19.2/src/cadence/defaults/prompts/review_first.txt +78 -0
- cadence_runner-0.19.2/src/cadence/defaults/prompts/review_second.txt +63 -0
- cadence_runner-0.19.2/src/cadence/defaults/prompts/task.txt +41 -0
- cadence_runner-0.19.2/src/cadence/executor/__init__.py +0 -0
- cadence_runner-0.19.2/src/cadence/executor/claude_executor.py +368 -0
- cadence_runner-0.19.2/src/cadence/executor/events.py +125 -0
- cadence_runner-0.19.2/src/cadence/executor/process_group.py +38 -0
- cadence_runner-0.19.2/src/cadence/git/__init__.py +10 -0
- cadence_runner-0.19.2/src/cadence/git/backend.py +260 -0
- cadence_runner-0.19.2/src/cadence/git/service.py +142 -0
- cadence_runner-0.19.2/src/cadence/input.py +69 -0
- cadence_runner-0.19.2/src/cadence/plan/__init__.py +27 -0
- cadence_runner-0.19.2/src/cadence/plan/parse.py +141 -0
- cadence_runner-0.19.2/src/cadence/plan/plan.py +15 -0
- cadence_runner-0.19.2/src/cadence/processor/__init__.py +0 -0
- cadence_runner-0.19.2/src/cadence/processor/agents.py +110 -0
- cadence_runner-0.19.2/src/cadence/processor/prompts.py +279 -0
- cadence_runner-0.19.2/src/cadence/processor/runner.py +524 -0
- cadence_runner-0.19.2/src/cadence/processor/signals.py +83 -0
- cadence_runner-0.19.2/src/cadence/progress/__init__.py +1 -0
- cadence_runner-0.19.2/src/cadence/progress/colors.py +40 -0
- cadence_runner-0.19.2/src/cadence/progress/flock.py +26 -0
- cadence_runner-0.19.2/src/cadence/progress/logger.py +254 -0
- cadence_runner-0.19.2/src/cadence/status.py +65 -0
- cadence_runner-0.19.2/tests/__init__.py +0 -0
- cadence_runner-0.19.2/tests/test_agents.py +203 -0
- cadence_runner-0.19.2/tests/test_cli.py +2209 -0
- cadence_runner-0.19.2/tests/test_config.py +359 -0
- cadence_runner-0.19.2/tests/test_events.py +196 -0
- cadence_runner-0.19.2/tests/test_executor.py +881 -0
- cadence_runner-0.19.2/tests/test_git.py +122 -0
- cadence_runner-0.19.2/tests/test_git_service.py +557 -0
- cadence_runner-0.19.2/tests/test_input.py +100 -0
- cadence_runner-0.19.2/tests/test_partial_line.py +45 -0
- cadence_runner-0.19.2/tests/test_plan.py +213 -0
- cadence_runner-0.19.2/tests/test_processor.py +1021 -0
- cadence_runner-0.19.2/tests/test_progress.py +512 -0
- cadence_runner-0.19.2/tests/test_prompts.py +510 -0
- cadence_runner-0.19.2/tests/test_signals.py +159 -0
- cadence_runner-0.19.2/tests/test_status.py +125 -0
- cadence_runner-0.19.2/tests/test_watchdog.py +58 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Drozdetskiy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cadence-runner
|
|
3
|
+
Version: 0.19.2
|
|
4
|
+
Summary: CLI tool for autonomous task execution via Claude Code
|
|
5
|
+
Keywords: claude,claude-code,cli,automation,agent
|
|
6
|
+
Author-Email: Mikhail Drozdetskiy <m.drozdetskiy@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Software Development
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Project-URL: Homepage, https://github.com/Drozdetskiy/cadence
|
|
19
|
+
Project-URL: Repository, https://github.com/Drozdetskiy/cadence
|
|
20
|
+
Project-URL: Issues, https://github.com/Drozdetskiy/cadence/issues
|
|
21
|
+
Requires-Python: >=3.14
|
|
22
|
+
Requires-Dist: typer>=0.9
|
|
23
|
+
Requires-Dist: rich>=13.0
|
|
24
|
+
Requires-Dist: PyYAML>=6.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# cadence
|
|
28
|
+
|
|
29
|
+
Autonomous task-execution pipeline on top of [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
|
|
30
|
+
|
|
31
|
+
`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).
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
| Stage | Trigger | What happens |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| **plan** | `cadence --plan <file>` | Interactive Q&A with Claude, draft review (accept / revise / reject), final plan written to `<file>-plan.md` |
|
|
38
|
+
| **task** | `cadence --task <plan>` | Branch created from plan filename, one `### Task N:` section per iteration, each completed and committed |
|
|
39
|
+
| **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 |
|
|
40
|
+
|
|
41
|
+
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>>>`).
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# from source, editable
|
|
47
|
+
pip install -e .
|
|
48
|
+
|
|
49
|
+
# or with pdm
|
|
50
|
+
pdm install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Requires:
|
|
54
|
+
- Python **3.14+**
|
|
55
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) on `PATH`
|
|
56
|
+
- a git repository (the `--task` and `--review` modes operate on the working tree)
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
Tasks live in their own subdirectory under `cdc-tasks/<NNNN-slug>/` (configurable via `tasks_root`). The free-form description goes into a file named `preprompt`; the generated plan is written next to it as `plan` (the `preprompt` → `plan` mapping is built into `--plan`). A per-task `config.yaml` next to the prompt is auto-discovered.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
cdc-tasks/
|
|
64
|
+
0001-my-feature/
|
|
65
|
+
preprompt # free-form task description (input)
|
|
66
|
+
plan # generated by --plan, consumed by --task
|
|
67
|
+
config.yaml # optional per-task overrides (auto-discovered)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# 1. Create a plan from a free-form task description
|
|
72
|
+
cadence --plan cdc-tasks/0001-my-feature/preprompt
|
|
73
|
+
# → writes cdc-tasks/0001-my-feature/plan
|
|
74
|
+
|
|
75
|
+
# 2. Create a plan and chain straight into implementation
|
|
76
|
+
cadence --plan cdc-tasks/0001-my-feature/preprompt --impl
|
|
77
|
+
|
|
78
|
+
# 3. Execute an existing plan: branch + tasks + review
|
|
79
|
+
cadence --task cdc-tasks/0001-my-feature/plan
|
|
80
|
+
|
|
81
|
+
# 4. Review the current branch only (no plan, no branch creation)
|
|
82
|
+
cadence --review
|
|
83
|
+
cadence --review --base develop # override base branch
|
|
84
|
+
cadence --review --config cdc-tasks/0001-my-feature/config.yaml # per-run overrides
|
|
85
|
+
|
|
86
|
+
# Misc
|
|
87
|
+
cadence --version
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Flag rules:
|
|
91
|
+
- `--plan`, `--task`, `--review` are mutually exclusive.
|
|
92
|
+
- `--impl` requires `--plan` (and is incompatible with `--review`).
|
|
93
|
+
- `--base` is only valid with `--review`. Resolution priority: `--base` > `default_branch` in config (defaults to `main`).
|
|
94
|
+
|
|
95
|
+
### Plan file format
|
|
96
|
+
|
|
97
|
+
Plans are markdown with a strict structure the runner parses on every iteration:
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
# Title
|
|
101
|
+
|
|
102
|
+
## Overview
|
|
103
|
+
…
|
|
104
|
+
|
|
105
|
+
## Context
|
|
106
|
+
- Files involved: …
|
|
107
|
+
|
|
108
|
+
### Task 1: Setup
|
|
109
|
+
- [ ] step one
|
|
110
|
+
- [ ] step two
|
|
111
|
+
- [ ] write tests
|
|
112
|
+
- [ ] run test suite
|
|
113
|
+
|
|
114
|
+
### Task 2: …
|
|
115
|
+
- [ ] …
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `### Task N:` or `### Iteration N:` headers delimit work units.
|
|
119
|
+
- `- [ ]` / `- [x]` checkboxes inside Task sections drive progress.
|
|
120
|
+
- Checkboxes outside Task sections (Overview, Context, Success criteria) do not block completion.
|
|
121
|
+
- After a successful `--task` run the file is renamed in place to `<stem>-completed<ext>` (no commit — plan files are typically gitignored).
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
### Local config: `.cadence/config.yaml`
|
|
126
|
+
|
|
127
|
+
Project-scoped, no global config. Loaded from cwd (or `CADENCE_CONFIG_DIR`). All keys are optional — missing keys fall back to defaults.
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
# Claude executor
|
|
131
|
+
claude_command: claude
|
|
132
|
+
claude_args: "--dangerously-skip-permissions --verbose"
|
|
133
|
+
plan_model: claude-opus-4-7
|
|
134
|
+
task_model: claude-opus-4-7
|
|
135
|
+
review_model: claude-opus-4-7
|
|
136
|
+
|
|
137
|
+
# Iteration / timing
|
|
138
|
+
iteration_delay_ms: 2000
|
|
139
|
+
task_retry_count: 1
|
|
140
|
+
max_iterations: 50
|
|
141
|
+
session_timeout: "0" # "30m", "1h30m", … 0 = disabled
|
|
142
|
+
idle_timeout: "0"
|
|
143
|
+
wait_on_limit: "0" # >0 → retry on rate-limit instead of failing
|
|
144
|
+
|
|
145
|
+
# VCS / paths
|
|
146
|
+
tasks_root: cdc-tasks # root for per-task subdirectories (preprompt/plan/config.yaml)
|
|
147
|
+
default_branch: main # override per-project in local config
|
|
148
|
+
commit_trailer: "" # appended to all cadence-made commits
|
|
149
|
+
commit_format: | # appended to task/review prompts (default shown below)
|
|
150
|
+
Format: <branch-name>. Added: <what>. Changed: <what>. Deleted: <what>. ...
|
|
151
|
+
|
|
152
|
+
# Output
|
|
153
|
+
colors:
|
|
154
|
+
task: "#2e8b57"
|
|
155
|
+
review: "#1a9e9e"
|
|
156
|
+
warn: "#d4930d"
|
|
157
|
+
error: "#cc0000"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
See [`docs/config.md`](docs/config.md) for the full key reference (timeouts, error patterns, color palette).
|
|
161
|
+
|
|
162
|
+
### Per-run overrides: `--config`
|
|
163
|
+
|
|
164
|
+
`--config <path>` loads a YAML file that overrides per-phase models and/or `default_branch`. Each key is optional:
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
plan:
|
|
168
|
+
model: claude-opus-4-7
|
|
169
|
+
task:
|
|
170
|
+
model: claude-opus-4-7
|
|
171
|
+
review:
|
|
172
|
+
model: claude-opus-4-7
|
|
173
|
+
default_branch: develop
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
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.
|
|
177
|
+
|
|
178
|
+
### Commit message format
|
|
179
|
+
|
|
180
|
+
`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.
|
|
181
|
+
|
|
182
|
+
The built-in default produces messages like:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
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.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Pattern: `<branch-name>. Added: <what>. Changed: <what>. Deleted: <what>.` — sections are included only when they apply, one short clause each, English, single line.
|
|
189
|
+
|
|
190
|
+
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):
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
commit_format: |
|
|
194
|
+
Format: <branch-name>. Added: <what>. Changed: <what>. Deleted: <what>.
|
|
195
|
+
Include only the sections that apply. English, single line.
|
|
196
|
+
Each section is one short clause describing the user-visible outcome.
|
|
197
|
+
Author as the user — no Co-Authored-By trailer (unless `commit_trailer` is configured).
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Switch to Conventional Commits:
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
commit_format: |
|
|
204
|
+
Use Conventional Commits: <type>(<scope>): <subject>
|
|
205
|
+
Types: feat, fix, refactor, docs, test, chore.
|
|
206
|
+
Subject is imperative, lowercase, no trailing period, ≤72 chars.
|
|
207
|
+
Example: feat(executor): add idle-timeout retry
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
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.
|
|
211
|
+
|
|
212
|
+
### Customizing prompts and agents
|
|
213
|
+
|
|
214
|
+
`cadence` ships with embedded defaults under `src/cadence/defaults/`. To customize, drop replacements into the project:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
.cadence/
|
|
218
|
+
config.yaml
|
|
219
|
+
prompts/
|
|
220
|
+
make_plan.txt # overrides plan-creation prompt
|
|
221
|
+
task.txt # overrides task-iteration prompt
|
|
222
|
+
review_first.txt # overrides initial review prompt
|
|
223
|
+
review_second.txt # overrides review-loop prompt
|
|
224
|
+
agents/
|
|
225
|
+
quality.txt # custom review agents (referenced as {{agent:quality}})
|
|
226
|
+
implementation.txt
|
|
227
|
+
testing.txt
|
|
228
|
+
simplification.txt
|
|
229
|
+
my-extra-agent.txt # add new agents — auto-discovered
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Per-file fallback: if a local file is empty or contains only `# comments`, the embedded default is used. Agents support optional YAML frontmatter:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
---
|
|
236
|
+
model: sonnet # haiku | sonnet | opus (or full IDs, normalized)
|
|
237
|
+
agent: code-reviewer # subagent type for the Task tool
|
|
238
|
+
---
|
|
239
|
+
Agent prompt body…
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
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.
|
|
243
|
+
|
|
244
|
+
## Runtime controls
|
|
245
|
+
|
|
246
|
+
- **Ctrl+C** — graceful shutdown (twice within 5s force-exits).
|
|
247
|
+
- **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.
|
|
248
|
+
- **Rate limits** — if `wait_on_limit > 0` and Claude output matches `claude_limit_patterns`, cadence sleeps and retries indefinitely until cancellation.
|
|
249
|
+
- **Session / idle timeouts** — kill stuck sessions; review-loop iterations skip the no-commit detection if the previous session timed out.
|
|
250
|
+
|
|
251
|
+
## Project layout
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
src/cadence/
|
|
255
|
+
cli.py Typer entrypoint, mode dispatch, signal handling
|
|
256
|
+
config.py Config dataclass, YAML loading, --config overrides
|
|
257
|
+
status.py Phase / Mode / Signal constants
|
|
258
|
+
input.py Interactive Q&A collector
|
|
259
|
+
executor/ Claude subprocess + JSON-stream parsing
|
|
260
|
+
git/ Service layer over `git` CLI
|
|
261
|
+
plan/ Markdown plan parser, branch-name extraction
|
|
262
|
+
processor/ Runner — orchestrates plan/task/review phases
|
|
263
|
+
progress/ File+stdout logger with colors and flock
|
|
264
|
+
defaults/
|
|
265
|
+
prompts/ Embedded prompt templates
|
|
266
|
+
agents/ Embedded review agents
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Deeper module references live in [`docs/`](docs/): `config.md`, `processor.md`, `executor.md`, `git-and-plans.md`, `progress-and-input.md`.
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
make install # pdm install --dev
|
|
275
|
+
make test # pytest tests/ -v
|
|
276
|
+
make test-cov # with coverage
|
|
277
|
+
make lint # ruff check
|
|
278
|
+
make typecheck # mypy --strict
|
|
279
|
+
make check # lint + typecheck + test
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Conventions:
|
|
283
|
+
- Python 3.14+, `mypy --strict`.
|
|
284
|
+
- `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.
|
|
285
|
+
- 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).
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# cadence
|
|
2
|
+
|
|
3
|
+
Autonomous task-execution pipeline on top of [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
|
|
4
|
+
|
|
5
|
+
`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).
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
| Stage | Trigger | What happens |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| **plan** | `cadence --plan <file>` | Interactive Q&A with Claude, draft review (accept / revise / reject), final plan written to `<file>-plan.md` |
|
|
12
|
+
| **task** | `cadence --task <plan>` | Branch created from plan filename, one `### Task N:` section per iteration, each completed and committed |
|
|
13
|
+
| **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 |
|
|
14
|
+
|
|
15
|
+
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>>>`).
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# from source, editable
|
|
21
|
+
pip install -e .
|
|
22
|
+
|
|
23
|
+
# or with pdm
|
|
24
|
+
pdm install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requires:
|
|
28
|
+
- Python **3.14+**
|
|
29
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) on `PATH`
|
|
30
|
+
- a git repository (the `--task` and `--review` modes operate on the working tree)
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Tasks live in their own subdirectory under `cdc-tasks/<NNNN-slug>/` (configurable via `tasks_root`). The free-form description goes into a file named `preprompt`; the generated plan is written next to it as `plan` (the `preprompt` → `plan` mapping is built into `--plan`). A per-task `config.yaml` next to the prompt is auto-discovered.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
cdc-tasks/
|
|
38
|
+
0001-my-feature/
|
|
39
|
+
preprompt # free-form task description (input)
|
|
40
|
+
plan # generated by --plan, consumed by --task
|
|
41
|
+
config.yaml # optional per-task overrides (auto-discovered)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 1. Create a plan from a free-form task description
|
|
46
|
+
cadence --plan cdc-tasks/0001-my-feature/preprompt
|
|
47
|
+
# → writes cdc-tasks/0001-my-feature/plan
|
|
48
|
+
|
|
49
|
+
# 2. Create a plan and chain straight into implementation
|
|
50
|
+
cadence --plan cdc-tasks/0001-my-feature/preprompt --impl
|
|
51
|
+
|
|
52
|
+
# 3. Execute an existing plan: branch + tasks + review
|
|
53
|
+
cadence --task cdc-tasks/0001-my-feature/plan
|
|
54
|
+
|
|
55
|
+
# 4. Review the current branch only (no plan, no branch creation)
|
|
56
|
+
cadence --review
|
|
57
|
+
cadence --review --base develop # override base branch
|
|
58
|
+
cadence --review --config cdc-tasks/0001-my-feature/config.yaml # per-run overrides
|
|
59
|
+
|
|
60
|
+
# Misc
|
|
61
|
+
cadence --version
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Flag rules:
|
|
65
|
+
- `--plan`, `--task`, `--review` are mutually exclusive.
|
|
66
|
+
- `--impl` requires `--plan` (and is incompatible with `--review`).
|
|
67
|
+
- `--base` is only valid with `--review`. Resolution priority: `--base` > `default_branch` in config (defaults to `main`).
|
|
68
|
+
|
|
69
|
+
### Plan file format
|
|
70
|
+
|
|
71
|
+
Plans are markdown with a strict structure the runner parses on every iteration:
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
# Title
|
|
75
|
+
|
|
76
|
+
## Overview
|
|
77
|
+
…
|
|
78
|
+
|
|
79
|
+
## Context
|
|
80
|
+
- Files involved: …
|
|
81
|
+
|
|
82
|
+
### Task 1: Setup
|
|
83
|
+
- [ ] step one
|
|
84
|
+
- [ ] step two
|
|
85
|
+
- [ ] write tests
|
|
86
|
+
- [ ] run test suite
|
|
87
|
+
|
|
88
|
+
### Task 2: …
|
|
89
|
+
- [ ] …
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `### Task N:` or `### Iteration N:` headers delimit work units.
|
|
93
|
+
- `- [ ]` / `- [x]` checkboxes inside Task sections drive progress.
|
|
94
|
+
- Checkboxes outside Task sections (Overview, Context, Success criteria) do not block completion.
|
|
95
|
+
- After a successful `--task` run the file is renamed in place to `<stem>-completed<ext>` (no commit — plan files are typically gitignored).
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
### Local config: `.cadence/config.yaml`
|
|
100
|
+
|
|
101
|
+
Project-scoped, no global config. Loaded from cwd (or `CADENCE_CONFIG_DIR`). All keys are optional — missing keys fall back to defaults.
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
# Claude executor
|
|
105
|
+
claude_command: claude
|
|
106
|
+
claude_args: "--dangerously-skip-permissions --verbose"
|
|
107
|
+
plan_model: claude-opus-4-7
|
|
108
|
+
task_model: claude-opus-4-7
|
|
109
|
+
review_model: claude-opus-4-7
|
|
110
|
+
|
|
111
|
+
# Iteration / timing
|
|
112
|
+
iteration_delay_ms: 2000
|
|
113
|
+
task_retry_count: 1
|
|
114
|
+
max_iterations: 50
|
|
115
|
+
session_timeout: "0" # "30m", "1h30m", … 0 = disabled
|
|
116
|
+
idle_timeout: "0"
|
|
117
|
+
wait_on_limit: "0" # >0 → retry on rate-limit instead of failing
|
|
118
|
+
|
|
119
|
+
# VCS / paths
|
|
120
|
+
tasks_root: cdc-tasks # root for per-task subdirectories (preprompt/plan/config.yaml)
|
|
121
|
+
default_branch: main # override per-project in local config
|
|
122
|
+
commit_trailer: "" # appended to all cadence-made commits
|
|
123
|
+
commit_format: | # appended to task/review prompts (default shown below)
|
|
124
|
+
Format: <branch-name>. Added: <what>. Changed: <what>. Deleted: <what>. ...
|
|
125
|
+
|
|
126
|
+
# Output
|
|
127
|
+
colors:
|
|
128
|
+
task: "#2e8b57"
|
|
129
|
+
review: "#1a9e9e"
|
|
130
|
+
warn: "#d4930d"
|
|
131
|
+
error: "#cc0000"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
See [`docs/config.md`](docs/config.md) for the full key reference (timeouts, error patterns, color palette).
|
|
135
|
+
|
|
136
|
+
### Per-run overrides: `--config`
|
|
137
|
+
|
|
138
|
+
`--config <path>` loads a YAML file that overrides per-phase models and/or `default_branch`. Each key is optional:
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
plan:
|
|
142
|
+
model: claude-opus-4-7
|
|
143
|
+
task:
|
|
144
|
+
model: claude-opus-4-7
|
|
145
|
+
review:
|
|
146
|
+
model: claude-opus-4-7
|
|
147
|
+
default_branch: develop
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
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.
|
|
151
|
+
|
|
152
|
+
### Commit message format
|
|
153
|
+
|
|
154
|
+
`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.
|
|
155
|
+
|
|
156
|
+
The built-in default produces messages like:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
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.
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Pattern: `<branch-name>. Added: <what>. Changed: <what>. Deleted: <what>.` — sections are included only when they apply, one short clause each, English, single line.
|
|
163
|
+
|
|
164
|
+
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):
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
commit_format: |
|
|
168
|
+
Format: <branch-name>. Added: <what>. Changed: <what>. Deleted: <what>.
|
|
169
|
+
Include only the sections that apply. English, single line.
|
|
170
|
+
Each section is one short clause describing the user-visible outcome.
|
|
171
|
+
Author as the user — no Co-Authored-By trailer (unless `commit_trailer` is configured).
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Switch to Conventional Commits:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
commit_format: |
|
|
178
|
+
Use Conventional Commits: <type>(<scope>): <subject>
|
|
179
|
+
Types: feat, fix, refactor, docs, test, chore.
|
|
180
|
+
Subject is imperative, lowercase, no trailing period, ≤72 chars.
|
|
181
|
+
Example: feat(executor): add idle-timeout retry
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
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.
|
|
185
|
+
|
|
186
|
+
### Customizing prompts and agents
|
|
187
|
+
|
|
188
|
+
`cadence` ships with embedded defaults under `src/cadence/defaults/`. To customize, drop replacements into the project:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
.cadence/
|
|
192
|
+
config.yaml
|
|
193
|
+
prompts/
|
|
194
|
+
make_plan.txt # overrides plan-creation prompt
|
|
195
|
+
task.txt # overrides task-iteration prompt
|
|
196
|
+
review_first.txt # overrides initial review prompt
|
|
197
|
+
review_second.txt # overrides review-loop prompt
|
|
198
|
+
agents/
|
|
199
|
+
quality.txt # custom review agents (referenced as {{agent:quality}})
|
|
200
|
+
implementation.txt
|
|
201
|
+
testing.txt
|
|
202
|
+
simplification.txt
|
|
203
|
+
my-extra-agent.txt # add new agents — auto-discovered
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Per-file fallback: if a local file is empty or contains only `# comments`, the embedded default is used. Agents support optional YAML frontmatter:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
---
|
|
210
|
+
model: sonnet # haiku | sonnet | opus (or full IDs, normalized)
|
|
211
|
+
agent: code-reviewer # subagent type for the Task tool
|
|
212
|
+
---
|
|
213
|
+
Agent prompt body…
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
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.
|
|
217
|
+
|
|
218
|
+
## Runtime controls
|
|
219
|
+
|
|
220
|
+
- **Ctrl+C** — graceful shutdown (twice within 5s force-exits).
|
|
221
|
+
- **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.
|
|
222
|
+
- **Rate limits** — if `wait_on_limit > 0` and Claude output matches `claude_limit_patterns`, cadence sleeps and retries indefinitely until cancellation.
|
|
223
|
+
- **Session / idle timeouts** — kill stuck sessions; review-loop iterations skip the no-commit detection if the previous session timed out.
|
|
224
|
+
|
|
225
|
+
## Project layout
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
src/cadence/
|
|
229
|
+
cli.py Typer entrypoint, mode dispatch, signal handling
|
|
230
|
+
config.py Config dataclass, YAML loading, --config overrides
|
|
231
|
+
status.py Phase / Mode / Signal constants
|
|
232
|
+
input.py Interactive Q&A collector
|
|
233
|
+
executor/ Claude subprocess + JSON-stream parsing
|
|
234
|
+
git/ Service layer over `git` CLI
|
|
235
|
+
plan/ Markdown plan parser, branch-name extraction
|
|
236
|
+
processor/ Runner — orchestrates plan/task/review phases
|
|
237
|
+
progress/ File+stdout logger with colors and flock
|
|
238
|
+
defaults/
|
|
239
|
+
prompts/ Embedded prompt templates
|
|
240
|
+
agents/ Embedded review agents
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Deeper module references live in [`docs/`](docs/): `config.md`, `processor.md`, `executor.md`, `git-and-plans.md`, `progress-and-input.md`.
|
|
244
|
+
|
|
245
|
+
## Development
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
make install # pdm install --dev
|
|
249
|
+
make test # pytest tests/ -v
|
|
250
|
+
make test-cov # with coverage
|
|
251
|
+
make lint # ruff check
|
|
252
|
+
make typecheck # mypy --strict
|
|
253
|
+
make check # lint + typecheck + test
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Conventions:
|
|
257
|
+
- Python 3.14+, `mypy --strict`.
|
|
258
|
+
- `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.
|
|
259
|
+
- 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).
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cadence-runner"
|
|
3
|
+
dynamic = []
|
|
4
|
+
description = "CLI tool for autonomous task execution via Claude Code"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Mikhail Drozdetskiy", email = "m.drozdetskiy@gmail.com" },
|
|
8
|
+
]
|
|
9
|
+
keywords = [
|
|
10
|
+
"claude",
|
|
11
|
+
"claude-code",
|
|
12
|
+
"cli",
|
|
13
|
+
"automation",
|
|
14
|
+
"agent",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Topic :: Software Development",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
requires-python = ">=3.14"
|
|
29
|
+
dependencies = [
|
|
30
|
+
"typer>=0.9",
|
|
31
|
+
"rich>=13.0",
|
|
32
|
+
"PyYAML>=6.0",
|
|
33
|
+
]
|
|
34
|
+
version = "0.19.2"
|
|
35
|
+
|
|
36
|
+
[project.license]
|
|
37
|
+
text = "MIT"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/Drozdetskiy/cadence"
|
|
41
|
+
Repository = "https://github.com/Drozdetskiy/cadence"
|
|
42
|
+
Issues = "https://github.com/Drozdetskiy/cadence/issues"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
cadence = "cadence.cli:app"
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
requires = [
|
|
49
|
+
"pdm-backend",
|
|
50
|
+
]
|
|
51
|
+
build-backend = "pdm.backend"
|
|
52
|
+
|
|
53
|
+
[tool.pdm.version]
|
|
54
|
+
source = "file"
|
|
55
|
+
path = "src/cadence/__init__.py"
|
|
56
|
+
|
|
57
|
+
[tool.pdm.dev-dependencies]
|
|
58
|
+
dev = [
|
|
59
|
+
"pytest>=8.0",
|
|
60
|
+
"ruff>=0.4",
|
|
61
|
+
"mypy>=1.10",
|
|
62
|
+
"pytest-cov>=5.0",
|
|
63
|
+
"pytest-mock>=3.14",
|
|
64
|
+
"types-PyYAML",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
target-version = "py314"
|
|
69
|
+
line-length = 100
|
|
70
|
+
src = [
|
|
71
|
+
"src",
|
|
72
|
+
"tests",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = [
|
|
77
|
+
"E",
|
|
78
|
+
"F",
|
|
79
|
+
"I",
|
|
80
|
+
"UP",
|
|
81
|
+
"B",
|
|
82
|
+
"SIM",
|
|
83
|
+
"RUF",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.mypy]
|
|
87
|
+
strict = true
|
|
88
|
+
python_version = "3.14"
|
|
89
|
+
mypy_path = "src"
|
|
90
|
+
|
|
91
|
+
[tool.pytest.ini_options]
|
|
92
|
+
testpaths = [
|
|
93
|
+
"tests",
|
|
94
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.19.2"
|