capsaicin is a local-first ticket orchestrator for AI-assisted software development. It now supports both an upstream planning loop and a downstream implementation loop, with explicit review and human gates in both flows.
The intended workflow split is:
- implementation loop:
Claude Codeas implementer,Codexas reviewer - planning loop:
Codexas planner,Claude Codeas planning reviewer
The current runtime is built around a local SQLite database, a .capsaicin/ project directory inside your repo, and local CLI adapters for both Claude Code and Codex.
capsaicin helps you:
- initialize a project-local workflow state store
- create tickets and acceptance criteria
- declare ticket dependencies
- optionally run tickets in isolated per-ticket/per-epic git worktrees
- run implementation passes
- run independent review passes
- persist findings, retries, and decisions locally
- stop at explicit human gates instead of auto-approving
- resume interrupted work safely
It is designed to orchestrate the workflow, not replace human judgment.
capsaicin is still in active development and the project is optimized for
fast iteration over stability guarantees.
Current policy:
- backward compatibility is not required between in-flight epics
- legacy behavior and migration preservation are not design constraints unless a ticket explicitly says otherwise
- refactors that improve maintainability, scalability, or architectural clarity are preferred over compatibility-preserving workarounds
The local planning and implementation loops are available now.
Included:
capsaicin initcapsaicin plan newcapsaicin plan draftcapsaicin plan reviewcapsaicin plan revisecapsaicin plan approvecapsaicin plan materializecapsaicin plan defercapsaicin plan unblockcapsaicin plan loopcapsaicin plan statuscapsaicin ticket addcapsaicin ticket depcapsaicin ticket runcapsaicin ticket reviewcapsaicin ticket approvecapsaicin ticket revisecapsaicin ticket completecapsaicin ticket defercapsaicin ticket unblockcapsaicin workspace statuscapsaicin workspace recovercapsaicin workspace cleanupcapsaicin statuscapsaicin resumecapsaicin loopcapsaicin uicapsaicin doctor
Not in scope yet:
- GitHub issue creation
- pull request creation
- hosted sync
- Python
3.11+ git- a git repository to run against
- the local agent CLIs you configure for the roles you want to run, such as
claudeforClaude Codeandcodexfor Codex
The runtime supports independent backend selection for planner,
planning-reviewer, implementer, and reviewer roles. A common pairing is
Claude Code for implementation and plan review, with Codex for ticket
review and planning.
The capsaicin ui command additionally pulls in starlette, jinja2, uvicorn, and python-multipart — all installed automatically via pip install.
capsaicin captures tracked-file diffs using git diff HEAD, so the target repository should be a normal git worktree.
Clone the repo and install it in editable mode:
git clone <your-fork-or-this-repo>
cd capsaicin
python3 -m venv .venv
source .venv/bin/activate
pip install -e .For development tools and tests:
pip install -e .[dev]Verify the CLI:
capsaicin --help
capsaicin ticket --helpWhen you initialize a repo, capsaicin creates a local control directory:
.capsaicin/
projects/<project-slug>/
capsaicin.db
config.toml
activity.log
renders/
exports/
Important files:
capsaicin.db: canonical workflow stateconfig.toml: source-of-truth configurationactivity.log: append-only debug/event log
When [workspace] enabled = true is set in the project config, capsaicin
runs implementation, review, loop, and resume flows inside isolated git
worktrees instead of the operator's active checkout.
By default, those worktrees live under
~/.capsaicin/worktrees/<repo-hash>/. You can override that location with
worktree_root in config.toml.
Operators can inspect and repair workspace state with:
capsaicin workspace status TICKET_IDcapsaicin workspace recover TICKET_IDcapsaicin workspace cleanup TICKET_ID
Inside the repository you want to manage:
capsaicin init --project "My Project" --repo .If you want to start directly in the implementation loop, add a ticket:
capsaicin ticket add \
--title "Add health endpoint" \
--description "Implement /health and cover it with tests." \
--criteria "GET /health returns 200" \
--criteria "Response includes version metadata"Run implementation:
capsaicin ticket runRun review:
capsaicin ticket reviewInspect status:
capsaicin statusIf you want to start from planning instead, create an epic from a problem statement:
capsaicin plan new --problem "Add a complete health-check and diagnostics workflow for the service."
capsaicin plan loop
capsaicin plan statusWhen a plan reaches human-gate, approve it to materialize implementation
tickets:
capsaicin plan approve --rationale "Scope and sequencing look right"After approval, the UI can continue directly into implementation from the
approved epic by showing the materialized ticket queue, readiness state, and a
Continue Implementation action for the next eligible ticket.
If the ticket reaches human-gate, choose one:
capsaicin ticket approve
capsaicin ticket revise --add-finding "Need stronger test coverage"
capsaicin ticket defer --rationale "Waiting on API decision"The normal operator workflow is:
- Initialize a project in the repo.
- Either create a planning epic with
capsaicin plan newor add tickets manually withcapsaicin ticket add. - If you started in planning, run
capsaicin plan loopuntil the epic reacheshuman-gate, then approve it to materialize tickets and continue into implementation from the approved epic UI when ready. - Optionally add or inspect ticket dependencies.
- Run
capsaicin ticket runfor a specific ticket or let it auto-select the next runnablereadyticket. - Run
capsaicin ticket review. - If review fails, the ticket moves to
revise; runcapsaicin ticket runagain. - If review passes or escalates, the ticket moves to
human-gate. - Make a human decision with
approve,revise, ordefer. - Repeat for the next ticket.
If you prefer an in-process loop, use:
capsaicin loopThat command automatically performs:
- implementation
- review
- re-implementation after review failure
- re-review
It still stops at human-gate or blocked. It never auto-approves.
Initialize the current repo for capsaicin:
capsaicin init --project "My Project" --repo .What it does:
- creates
.capsaicin/projects/<slug>/ - writes
config.toml - creates
capsaicin.db - runs schema migrations
- creates
activity.log - inserts initial project/orchestrator rows
Create a ticket inline:
capsaicin ticket add \
--title "Implement auth middleware" \
--description "Add JWT auth to protected routes." \
--criteria "Requests with valid JWT succeed" \
--criteria "Expired JWTs are rejected"Or import from TOML:
capsaicin ticket add --from ticket.tomlExample ticket.toml:
title = "Implement user authentication"
description = """
Add JWT-based authentication middleware.
"""
[[criteria]]
description = "Login endpoint returns a valid JWT"
[[criteria]]
description = "Middleware rejects expired tokens"Add a dependency:
capsaicin ticket dep TICKET_ID --on DEPENDENCY_IDThe dependent ticket will not run until the dependency is done.
Create a planning epic from a problem statement:
capsaicin plan new --problem "Add a health-check and diagnostics workflow"Run the planning draft-review-revise loop automatically:
capsaicin plan loop
capsaicin plan loop EPIC_ID --max-cycles 2This drives planner and planning-reviewer runs until the epic reaches
human-gate or blocked.
Approve a plan and materialize implementation tickets:
capsaicin plan approve
capsaicin plan approve EPIC_ID --rationale "Ready to implement"
capsaicin plan approve EPIC_ID --force--force is for overwriting previously materialized docs that were edited
manually.
In the UI, approved epics also expose the materialized implementation tickets,
their readiness/dependency state, and a Continue Implementation action for
starting the implementation loop on the next eligible ticket in that epic.
Show planning summary or one epic in detail:
capsaicin plan status
capsaicin plan status EPIC_ID
capsaicin plan status EPIC_ID --verboseRun the implementation pipeline:
capsaicin ticket run
capsaicin ticket run TICKET_IDBehavior:
- auto-selects the next runnable
readyticket if no ID is provided - transitions the ticket into implementation
- invokes the implementer adapter
- captures the git diff
- moves the ticket to:
in-reviewwhen changes existhuman-gatewhen the implementation produced no tracked-file diffblockedwhen implementation retries are exhausted
Run the review pipeline:
capsaicin ticket review
capsaicin ticket review TICKET_ID
capsaicin ticket review --allow-driftBehavior:
- reviews tickets in
in-review - checks that the current workspace still matches the implementation diff
- captures a review baseline before invoking the reviewer
- persists findings and acceptance-criteria updates
- moves the ticket to:
revisewhen blocking findings existhuman-gateon pass, escalation, low-confidence pass, or cycle limitblockedwhen review retries are exhausted
Use --allow-drift only when you intentionally changed the workspace after the implementation run and want review to proceed against the new current diff.
Approve a ticket at the human gate:
capsaicin ticket approve
capsaicin ticket approve TICKET_ID
capsaicin ticket approve --rationale "Reviewed manually"
capsaicin ticket approve --forceNotes:
- approval normally verifies that the workspace still matches what was reviewed
--forceoverrides the workspace match check--rationaleis required for some gate reasons such as cycle-limit or reviewer escalation
Send a ticket back for another implementation pass:
capsaicin ticket revise
capsaicin ticket revise TICKET_ID --add-finding "Missing migration rollback"
capsaicin ticket revise TICKET_ID --reset-cyclesUse this when you want to add explicit human feedback or reset the loop state.
Defer or abandon a ticket:
capsaicin ticket defer TICKET_ID --rationale "Waiting on product decision"
capsaicin ticket defer TICKET_ID --abandon --rationale "Out of scope"- normal defer moves the ticket to
blocked --abandonmarks it as effectively finished/abandoned
Return a blocked ticket to ready:
capsaicin ticket unblock TICKET_ID
capsaicin ticket unblock TICKET_ID --reset-cyclesShow a project summary:
capsaicin statusShow one ticket in detail:
capsaicin status --ticket TICKET_ID
capsaicin status --ticket TICKET_ID --verboseUse this often. It is the main operator view into the current workflow state.
Recover from an interrupted run:
capsaicin resumeUse this after:
- a crashed terminal
- a killed agent process
- an interrupted machine/session
capsaicin uses the persisted orchestrator state and prior run records to decide whether to continue, retry, mark failure, or stop for human action.
Inspect or manage isolated worktrees for a ticket:
capsaicin workspace status TICKET_ID
capsaicin workspace recover TICKET_ID
capsaicin workspace cleanup TICKET_IDUse these commands when workspace isolation is enabled and a ticket is blocked by missing worktrees, branch drift, setup failures, or cleanup conflicts.
Run the full implement-review-revise loop automatically:
capsaicin loop
capsaicin loop TICKET_ID
capsaicin loop TICKET_ID --max-cycles 2This is the fastest way to operate once your project is configured and you want the tool to keep driving until a human decision is required.
Launch the local operator web UI:
capsaicin ui
capsaicin ui --port 8080
capsaicin ui --no-openBehavior:
- starts a local HTTP server bound to
127.0.0.1 - picks an available port automatically unless
--portis provided - opens the browser by default;
--no-opensuppresses this - serves a dashboard with queue state, inbox, and activity
- serves a planning dashboard with approved epic detail, materialized implementation tickets, readiness state, and continuity actions
- shows ticket detail with acceptance criteria, findings, diff, and run history
- provides action forms for planning approval, re-materialization, continue implementation, approve, revise, defer, unblock, run, review, and loop directly in the browser
- live updates via server-sent events when ticket or orchestrator state changes
- no authentication, remote access, or multi-user support — this is a single-operator local tool
The UI uses the same shared services as the CLI. Actions taken in the browser produce identical state transitions and persist through the same database.
Ticket statuses:
ready: queued to be workedimplementing: implementation run in progressin-review: awaiting or undergoing reviewrevise: reviewer found blocking issueshuman-gate: waiting for a human decisionpr-ready: approved and ready for your normal PR workflowblocked: cannot proceed automaticallydone: completed or abandoned
Common gate reasons:
review_passedlow_confidence_passreviewer_escalatedcycle_limitempty_implementation
Each initialized project gets a config.toml like this:
[project]
name = "my-project"
repo_path = "/absolute/path/to/repo"
[adapters.implementer]
backend = "claude-code"
command = "claude"
[adapters.reviewer]
backend = "claude-code"
command = "claude"
allowed_tools = ["Read", "Glob", "Grep", "Bash"]
[limits]
max_cycles = 3
max_impl_retries = 2
max_review_retries = 2
timeout_seconds = 300
[reviewer]
mode = "read-only"
[ticket_selection]
order = "created_at"
[paths]
renders_dir = "renders"
exports_dir = "exports"
# Optional workspace isolation. Uncomment to run tickets in isolated git
# worktrees instead of the shared repo checkout.
#
# [workspace]
# enabled = true
# branch_prefix = "capsaicin/"
# auto_cleanup = true
# worktree_root = "/custom/path/for/worktrees"Important points:
config.tomlis the source of truth- repo paths are stored as absolute paths
- shipped defaults may still point core roles at
claude, but planner and planning-reviewer roles can now be configured independently as well - backend selection is role-specific: implementer, reviewer, planner, and planning reviewer can each use different configured backends
- if
[adapters.planner]or[adapters.planning_reviewer]are omitted, they fall back to the implementer and reviewer configs respectively - a common pairing is
Claude Codeimplementer plusCodexreviewer - a common planning pairing is
Codexplanner plusClaude Codereviewer - reviewer runs are intended to be read-only
- the generated config now includes a commented-out
[workspace]section so isolation is discoverable without being enabled by default
If .capsaicin/projects/ contains exactly one project, commands auto-resolve it.
If there are multiple projects, pass --project:
capsaicin status --project my-project
capsaicin ticket run --project my-projectYou can also point commands at a repo explicitly:
capsaicin status --repo /path/to/repo- Commit or stash unrelated work before running a ticket when possible.
- Review
capsaicin statusbefore approving. - Use
ticket review --allow-driftonly when you intentionally want to review the modified workspace, not the original implementation output. - Use
resumeafter interruption instead of guessing what state the tool was in. - Treat
pr-readyas the end of the MVP automation. PR creation and merge are still manual.
Run:
capsaicin init --project "My Project" --repo .Pass --project <slug>.
Check:
config.toml- that
claudeis installed and onPATH - the target repo path in
[project].repo_path .capsaicin/projects/<slug>/activity.log
The current working tree no longer matches the diff captured earlier.
Options:
- review the current state with
capsaicin ticket review --allow-drift - or revert/clean your local changes intentionally
- or use
capsaicin ticket approve --forceonly when you understand the risk
The design and implementation docs live in docs/README.md.
Recommended order: