@warren-dean/code-kit
v0.26.2
Published
Keeps AI coding agents inside your project's rules: lanes, layer rules, protected paths and proof before finish, enforced by Claude Code hooks and in CI
Maintainers
Readme
code-kit
A Claude Code plugin that keeps AI coding agents inside a project's engineering rules. You write the rules once, per project, in .claude/code-kit.json. The plugin's hooks check them on every file edit, shell command and finish, for the main session and every subagent. A rule Claude would otherwise only be asked to follow becomes one a hook refuses to let it break, or, for shell commands, reports straight after for the agent to undo. code-kit verify runs the same rules on a branch, in review and in CI.
It has two parts. The core is the rules and their checks: who may write where, layers, protected files with an audit log, approvals, and proof before finishing. On top of it sits an optional workflow that carries work from idea to merged code: spec an idea into requirements, hand stories to lanes, review each branch against its requirements, and track the build against the spec. You can use the core without the workflow.
Pairs with Context Graph. code-kit decides who may change what, and what has to be proved. Context Graph gives each agent the why behind a file before it edits it: the file's card, its rules and the decisions made on it, kept in git. Each detects the other: Context Graph's cards and slices then carry the file's spec requirement and layer rule. Install both for agents that stay in their lane and know why the code is the way it is.
See it in action at warrendeanlangeveldt.github.io, with a live replay of a real build.
Quick start
You need Claude Code, Node 22 or later, and git. Then, in Claude Code:
/plugin marketplace add warrendeanlangeveldt/code-kit
/plugin install code-kit@code-kit
/code-kit:next/code-kit:next works out where your project stands and takes you through the next step. A blank folder starts with speccing an idea; an existing codebase starts with drafting its rules. Nothing is enforced until you approve the rules it drafts. In a project without .claude/code-kit.json, the plugin does nothing.
Why
Rules in CLAUDE.md and agent prompts are advice. Over a long session, or across a dozen parallel subagents, advice drifts:
- a backend agent "quickly fixes" a frontend file, and two agents overwrite each other's work;
- a domain module starts importing the database client, and the layering the architecture doc describes stops being true;
- a contract or schema changes with no record of who changed it or why;
- code gets written before anyone checked it against the spec, or a screen before it was designed;
- an agent declares "done" with failing type checks, or with work nobody committed;
- someone force-pushes, commits to
main, adds a dependency, or runs a deploy nobody asked for.
code-kit catches each of these, most at the moment it would happen: the hook refuses the edit or command and tells the agent why and what to do instead. A file a shell command writes is caught straight after the command, and the agent is told to revert it. The rules live in one reviewed file, not in every prompt, so they hold for every agent and every session. They are guard rails for agents, not a sandbox: see What verify proves, and its limits.
What it enforces
Lanes: each lane agent writes only its own paths. The main session (the lead) writes contracts, specs and config, and delegates the rest. Any other subagent is read-only. An edit outside those paths is refused before it happens; a file a shell command writes outside them is reported straight after, for the agent to revert, and the finish check refuses to finish until it's gone.
Layers: each layer depends only on the layers it lists, and never on packages it's denied, for JavaScript and TypeScript imports. An edit that would add a violation is refused before it's written; the rules are checked again after each edit and before finishing, which is what catches a file a shell command wrote. In brownfield code, existing violations can be recorded in a baseline so only new ones block. Imports are found by pattern, not by parsing: see the limits.
Protected paths and the approval log: every committed change to the kit, registers or contracts is appended to
.claude/approval-log.jsonl, in the same commit. Only the lead writes these paths without an approval, and the person reviews them in the pull request. A lane needs an approval: the lead's for whatapprovals.delegategives it, otherwise the person's.Spec-check before code: when the project has specs, a lane can't write code on a branch until it has saved a spec-check report for that branch. The hook checks that the report exists; review checks what it says.
Designs before screens (optional): a screen file can't be written until the design register lists it.
Shell rules, matched against the command's text before it runs:
- no force-push, pushing to or committing on protected branches,
--no-verify, destructive deletes, or piping downloads into a shell; - no new dependency without an approval, for that package and that lane: the lead's, for a story it judged needs it, where dependencies are delegated, otherwise a person's; the approval also covers the manifest and lockfile changes the install makes;
- no forged approvals;
- no commit that weakens a test (deletes it, skips or focuses it, removes cases or assertions, or rewrites a snapshot) without a
testsapproval: the lead's on a lane's branch, after its review, where tests are delegated (the default), and the person's on the lead's own branches; - the project's own blocked commands (deploys, releases) and lane-restricted commands (shared local services).
- no force-push, pushing to or committing on protected branches,
Proof before finishing: before any agent finishes, the plugin runs:
- an ownership audit of every uncommitted change;
- the layer rules;
- the project's checks, for the files that changed.
A lane also has to commit its work first.
The same rules in CI:
code-kit verifychecks a pull request as a whole, including commits made outside Claude Code:- a
<lane>/…branch stays in that lane's paths, apart from manifest and lockfile changes logged under a dependency approval; - every protected change is in the approval log, and the log is append-only;
- no new layer violations, and the baseline doesn't grow;
- no screens without an approved design;
- no secret files (
.env,secrets/, keys and keystores) anywhere in the branch's history, including ones committed and deleted again, and, where gitleaks is installed, no secrets in any commit's content; - no test weakened (deleted, skipped or focused, cut down, or its snapshot rewritten) without the person's
testsapproval in the log; - a fix (a plan story with
**Fixes:**, or a<lane>/fix-…branch) comes with a test that fails without it, as the bug, and passes with it; - the project's checks pass.
- a
A project without .claude/code-kit.json isn't governed at all. An invalid config fails closed: nothing but the config itself can be written until it's fixed.
What verify proves, and its limits
When verify passes on a branch, you can conclude:
- every file the branch changed is one its lane may write, or is logged under an approval;
- every protected change is in the approval log, and the log was only appended to;
- no import found adds a layer violation, and the baseline didn't grow;
- no test was weakened without the person's
testsapproval, and, on a fix, its tests fail without the fix; - the project's checks for the changed files ran and passed, apart from any
verifylists as not run: a check whose tool isn't installed where it ran is skipped, and named, unless the check setsifMissing: "fail", which fails it instead. Set that on every check that matters, and install the project's tools in CI.
It's a check of the branch as it is when it runs, at the commit it names. A merge code-kit makes records that commit, and any checks not run, in the merge commit's message. From the merge queue, it merges exactly the commit the lead's review passed: a branch that moves on after its review leaves the queue to be reviewed again.
Its limits:
- Guard rails, not a sandbox (SECURITY.md). The hooks refuse what they recognise. Shell commands are matched by their text, so a command written to hide what it does can get past a shell rule, and a file a shell command writes is caught after the command, not before. Tools other than Edit, Write, NotebookEdit and Bash, such as an MCP server that writes files, aren't seen by the hooks at all; the finish check and
verifystill see what they changed in git. - Layers are pattern-matched. Imports are found with regular expressions for
import,export … from,require()andimport()with a literal path, in JavaScript and TypeScript files. A computed import isn't seen, and an import written inside a comment or a string can be reported as if it were real. An import resolves to a file through a relative path orimportAliasesonly. Anything else, a workspace package's name included, counts as a package, held only bydenyPackages, so map workspace packages andtsconfigpaths inimportAliases. - Secrets. Without gitleaks, only file names are checked (
.env,secrets/, keys and keystores), not what files contain. The CI workflow init installs doesn't install gitleaks; add it if you rely on the scan. - The finish check stops after three tries. When it has reported the same problem three times without progress, it lets the agent finish and says so, for the lead to take up.
- Review is the lead's judgement. The workflow's skills have the lead review each branch against its requirements, and the merge queue records the commit it reviewed. What the review looked at isn't recorded.
verifyis the mechanical check, and it runs before every merge code-kit makes.
When to use it
Use it when:
- Several agents work on one codebase at once, such as a lead delegating to frontend, backend and data lanes, possibly in separate worktrees. Lanes are what keep them out of each other's files.
- The architecture has layers worth protecting, like domain, services, adapters and UI, or a shared package that mustn't reach into apps.
- Some files need an audit trail: API contracts, schemas, registers, the kit's own config.
- The project has specs or designs that code should follow rather than improvise.
- "Done" has to mean done: checks passing and work committed, not an agent's say-so.
- You're starting from an idea, and want the specification worked out before anyone writes code.
It's overkill for a throwaway script, a spike, or a single-file change. In those, just don't add .claude/code-kit.json: the plugin stays installed and does nothing.
Set it up at the start of a new build, straight from the architecture docs, or bring it into an existing codebase at any point (init works the rules out from the code). Re-run init whenever the shape of the project changes: new apps or packages, moved folders, a changed plan.
Where it runs
In Claude Code, as a plugin. It's installed once per machine and governs only projects that have
.claude/code-kit.json.On every edit, shell command and finish in those projects, through Claude Code hooks:
| Hook | Runs | Enforces | | ---------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------ | |
PreToolUseEdit/Write |hooks/guard-paths.mjs| lanes, protected paths, spec-check and design gates | |PreToolUseBash |hooks/guard-bash.mjs| shell rules, dependencies, approval log and secret scan on commit | |PostToolUseEdit/Write |hooks/post-edit-check.mjs| layer rules and the project'spostEditcommands | |PostToolUseBash |hooks/lane-audit.mjs| ownership of files a shell command wrote, reported for revert | |PreToolUseAgent |hooks/guard-agent.mjs| one agent per lane at a time, across every session | |Stop,SubagentStop|hooks/stop-check.mjs| proof before finishing; a lane's finish frees the lane | |SessionStart,SessionEnd|hooks/session.mjs| the session recorded as the lead or a working session; as it ends, it leads nothing, and its lanes are freed |For the main session and every subagent, including agents in worktrees under
.claude/worktrees/.In front of you, in the terminal and the Desktop app, through the code-kit mod (Claude Code 2.1.287 or later; see In the session). It shows and acts, and never enforces: the hooks above do that everywhere, the mod or not.
In CI, through
code-kit verifyin a GitHub Actions workflow that init can install (templates/github-workflow.yml).Locally, with Node 22 or later. The CLI in
bin/runs the same rules from a terminal. The rules themselves send nothing over the network. Three features do, and only when you use them: delegated dependency approvals read the package from the npm registry or PyPI, the team features (team,intend) talk to GitHub through yourgh, or to the team provider you configure, and a bug report you send goes to code-kit's GitHub through yourgh.
Install
Requirements: Claude Code, Node 22 or later, and git. gitleaks is optional: when it's installed, every commit gets a secret scan, and verify scans every commit on the branch.
The plugin, from this repository's marketplace:
/plugin marketplace add warrendeanlangeveldt/code-kit
/plugin install code-kit@code-kitTo update later, run /plugin marketplace update code-kit and restart Claude Code; sessions started before an update keep the old hooks. To pin it for a whole team, add it to the project's .claude/settings.json:
{
"extraKnownMarketplaces": {
"code-kit": { "source": { "source": "github", "repo": "warrendeanlangeveldt/code-kit" } }
},
"enabledPlugins": { "code-kit@code-kit": true }
}The command line is optional: the skills run it for you in a session. Use it for CI, or from a terminal:
npx @warren-dean/code-kit check # no install
npm i -g @warren-dean/code-kit # or install it, then: code-kit checkFrom a clone, for working on code-kit itself: /plugin marketplace add <path to the clone>.
Its companion, Context Graph, installs the same way. Nothing needs configuring between the two:
/plugin marketplace add warrendeanlangeveldt/context-graph
/plugin install context-graph@context-graphOne command: /code-kit:next
Don't want to remember which skill comes when? Run /code-kit:next, from a blank folder or at any point in a build:
/code-kit:next a booking app for mobile dog groomersIt reads where the project stands and runs the right skill. It carries on to the following step until something needs you: questions to answer, a draft to approve, a pull request to merge, or lanes still building.
| The project has | next runs |
| ---------------------------------------------- | ------------------------------------------------- |
| Nothing | spec-design |
| A spec still being shaped | spec-design, picking up where it stopped |
| A ready spec, existing code, or a draft config | init |
| A companion installed but not set up yet | its setup (Context Graph's init), before any code |
| Design stories ready | design, with you, before their screens are built |
| Stories finished on their branches | review, then dispatch |
| Stories ready | dispatch |
| Stories being built | nothing: it tells you what's running |
| Everything done | a status summary, then offers the next milestone |
code-kit next on the command line shows the same decision without running anything.
Start from an idea
/code-kit:spec-design a booking app for mobile dog groomersspec-design works an idea into a specification with you before any code exists. It asks the questions that decide the build, a few at a time with a recommended answer, and writes every decision down:
- Frame: the problem, the users, the outcome, the constraints and the non-goals.
- Explore: two or three different shapes of solution, the main user journeys, and the riskiest assumptions. You choose.
- Scope: capability areas, and what the first release has to deliver end to end.
- Specify: one spec per area, with numbered, testable requirements covering data, states, rules, permissions, errors and acceptance criteria, and a short threat model with the security requirements that answer it.
- Architecture, principles and plan: the layers, contracts, recipes for each layer's common additions (they become the lane agents' skills), definition of done, and stories split into lanes, with a design story before the screens of each area that has any.
- Check: every journey maps to requirements and every requirement to a story, with no gaps left as "TBD".
It writes docs/brief.md (with the decision log and open questions), docs/specs/NN-<area>.md, docs/architecture.md, docs/standards.md, docs/principles.md and docs/plan.md. Those are exactly what /code-kit:init docs/ turns into lanes, layers and standards, and what lanes spec-check against. Run it again to resume where it left off, to change a decision, or to spec a new feature in an existing codebase.
Requirements are headings (### BOOK-4 Cancel a booking). Stories are ### ST-12 headings in the plan with **Lane:**, **Requirements:**, **Depends on:** and **Status:** lines (templates/plan.md). The lane lead marks the lead's own stories, such as contracts and spikes, which the lead builds itself instead of dispatching; the lane design marks a design story, which the lead designs with you (/code-kit:design) before any screen that depends on it is built; **Requirements:** none marks a story that delivers no requirement. Those two formats are what the build loop below runs on.
Set up a project
/code-kit:init docs/- A new build: init reads the docs in full: the architecture and layer map, the plan or work split, and the principles. It drafts the rules from them.
- An existing codebase (brownfield): the docs can be thin or missing. Init works the layers out from the code: which folders import which, and who works where according to the git history. It also runs each check once and flags any that already fail, so you decide what to do with them.
- Either way: you get a draft (
.claude/code-kit.draft.json), one agent per lane with its project skills (one per recipe, in the same approval), a read-only security reviewer, the design gate for lanes that own screens, a CLAUDE.md section and a list of questions (see The agents and skills it creates). Nothing is enforced until you approve and the draft becomes.claude/code-kit.json.
Existing violations. Brownfield code usually breaks some layer rules already. On approval, baseline --write records them in .claude/code-kit.baseline.json:
- the violations it records don't block;
- new ones, even in the same file, do;
- the baseline only ever shrinks: re-recording drops what's been fixed and refuses anything new.
No specs yet? Leave docs.specs out, and lanes aren't held to a spec-check report. Add it once specs exist.
The agents and skills it creates
code-kit ships no fixed team. Init writes one for each project, from its docs or its code, and re-running init keeps it in step as the project changes.
.claude/
├── code-kit.json # the rules: lanes, layers, protected paths, checks
├── agents/
│ ├── web-engineer.md # one lane agent per lane
│ ├── api-engineer.md
│ ├── db-engineer.md
│ └── security-reviewer.md # reads every sensitive change as an attacker would; owns nothing
└── skills/
└── new-migration/ # project recipes, only the ones you approve
└── SKILL.md
CLAUDE.md # plus a section telling the lead how the rules workWho's who. There are four kinds of actor. The hooks tell the three Claude kinds apart on every call they see; the person works outside them:
| Actor | Who it is | May write |
| ------------------ | ------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| The person | You | Anything; you also approve and merge |
| The lead | The main Claude Code session | Contracts, specs, config, the kit: lead.paths |
| A lane agent | A subagent whose name is a lane's agent in the config, or a session in a .lane worktree | Only its lane's paths, after its spec-check |
| Any other subagent | Explore, Plan, reviewers, anything not mapped to a lane | Nothing: read-only |
Lane agents (.claude/agents/<agent>.md, from templates/agent.md). Each is filled in from the project's docs, or from the code for brownfield projects:
- the paths the lane owns, its layers and what they may depend on;
- the specs it reads;
- the skills it uses: spec-check, plus its project recipes;
- the command that proves its work, and the lane's own rules.
It also carries the working rules: start from the story's branch and spec-check, build end to end with no mocks or placeholders, name requirement IDs in test titles, and commit before finishing.
The config's lanes.<name>.agent must match the agent file's name. That's how the hooks know a subagent is the web lane and not a read-only helper.
Instructions and enforcement are separate. The agent file tells the agent the rules, so it rarely hits them. The hooks enforce them whether or not the agent follows its file:
- writes outside its lane are blocked;
- code before a spec-check report is blocked;
- finishing with failing checks or uncommitted work is blocked.
Editing an agent file can't loosen a rule. And the agent files are kit files (.claude/**), so only the lead changes them, and every change is logged.
Spawned per story. Dispatch starts a fresh instance of the lane's agent for each story, in its own worktree on <lane>/st-<n>. Agents for different lanes run in parallel. Each starts knowing only its agent file and the story's brief, so nothing leaks between stories.
How the spec is followed, step by step (the steps a hook or verify enforces are marked):
- The brief names the story's requirement IDs.
- The agent runs the spec-check skill and saves a requirement table for its branch.
- Enforced: the path guard blocks any code in its lane until that report exists.
- The agent's tests name the requirement IDs;
statuslists the requirements no test names. - Enforced: the stop hook runs the checks before the agent can finish.
- Review compares the branch with the requirements and the report.
- Enforced:
verifychecks the rules again before merging and in CI.
The security reviewer (.claude/agents/security-reviewer.md, from templates/security-reviewer.md) is a reviewer, not a lane: it owns no paths and checks everyone's work. Init writes it whenever the product handles sign-in, personal data, payments, input from outside or secrets, which is nearly always. It only reads (Read, Grep, Glob, and Bash for git diff and the project's audit commands), and the hooks hold it to that like any subagent outside the lanes. spec-design gives each spec a short threat model (what's sensitive, who might attack it, the trust boundaries) and security requirements stories cite. When code-kit security says a story's change touches something sensitive, the review runs the agent on the diff against those threat models: it attacks authentication and authorisation, injection (prompt injection too), data exposure, secrets, cryptography, SSRF and redirects, deserialisation, new dependencies and the privacy targets, and grades each finding blocker, concern or nit, with where, the attack and the fix. A blocker sends the branch back like a failing test.
Project skills (.claude/skills/<recipe>/SKILL.md) are the step-by-step recipes your docs describe, like "add a migration" or "add an endpoint". Init lists the ones it finds and writes them only if you say yes, never as empty stubs. Each is listed in the owning lane agent's "Skills to use". Like the agents, they're kit files: the lead maintains them, and changes are logged.
Kept in step. When the project changes (a new app, a new lane, moved folders), re-run /code-kit:init. It drafts new agents for new lanes and updates the paths of existing ones, keeping anything written by hand. You see the difference and approve it before anything changes.
In the session
With Claude Code 2.1.287 or later, code-kit's mod shows the project in the session, in the terminal (VS Code's terminal included) and the Desktop app. Older versions skip it and keep enforcing everything through the hooks. The mod shows and acts; it never enforces. Mods don't draw in the VS Code extension's chat panel, the Agent SDK or claude -p, and outside a project with .claude/code-kit.json the mod draws nothing.
The band
The band sits above the prompt, and speaks only when it's needed, in at most three lines:
- What needs you: the most urgent thing waiting, with the story it's for ("for ST-4 Booking form (BOOK-1)") and its buttons, and how many more wait ("2 more waiting · /lanes"). d ("Why, and what each choice does") opens it as a card in the pane. Most urgent first: a held call, then the lead's questions, approvals, merges and reviews that are yours, stalled lanes, your brief's open questions, then notes (usage, the plan's pause).
- While the build moves and nothing needs you, one line: the milestone, its bar of requirements tested, and how many agents are at work, with Pause; a paused loop says so, with Resume.
- Nothing while nothing is happening and nothing waits.
With team in the config, what of the team needs you is among what waits: your stale claim (Keep or Release), a claim of yours changing the same files as someone else's, an intent touching what you own or lease (every intent, with Agree and Drop, for the tech lead), and a pull request in a lane you own.
What waits for you there:
- A held call. An agent installing a new package, writing or committing a protected file, or a lane changing the kit, where the approval is yours. Rather than stopping the agent, the call waits while the band asks ("web wants to install dayjs · 1:53"). Approve opens the confirmation and, once you confirm, the same call runs; Refuse, or no answer within
harness.hold.minutes(2 unless set), refuses it as before. What the lead decides (approvals.delegate) is never held for you: the lane asks the lead, which approves it on its merits. Nothing is held underclaude -por the SDK, where nobody can answer. - Approvals agents asked for. Approve… opens a confirmation with a reason prefilled from the request, which you keep or rewrite. When it isn't clear what it's for, Ask about it: the answer comes from a side call over the lead's conversation and code-kit's facts about the request, shown in the pane and never added to the lead's session, and follow-ups carry on the aside. A request waits until it's answered, its file is committed by someone who could, or it's dropped (at most a week); on its card, Not needed drops it.
- Questions from the lead ("The lead asks: which weather provider?"), asked with
code-kit ask: what it can't settle and only you can. Answer… opens it as a card with each of the lead's options and what it does, and Something else… for your own words; your answer is recorded and sent to the lead. A question can hold its story, which then readswaiting on you: nothing dispatches or reviews it, and the stories that depend on it wait, until you answer. - Your brief's open questions, each with who decides and what it holds up (from the table's Owner and Blocks columns; one owned by the lead alone isn't yours, and doesn't show). Answer… sends your answer to the lead, which records it in the brief's decisions, and Ask about it in the answer box explains it first, beside the conversation; Later puts one off until the next session.
- A bug report for code-kit, with
harness.report.on, drafted by an agent code-kit got in the way of. It holds only code-kit's own facts (its version, the rule, what it said, the command), with the project's folders and email addresses taken out. Read it shows the exact text; Send files it on code-kit's GitHub with your owngh(a comment, if it's filed already; withoutgh, you get the link to file it); Discard drops it. - Stories waiting for review, with the loop off: Review asks the lead to run
/code-kit:reviewon the branch, and Merge runsverify, checks included, then merges if it passes. While the loop runs, a lane's finished work is the lead's to review and merge, so it never waits here. - Warnings: a finish check failing in this session, or a story using more than three times the median of the stories finished this session (once three have finished), each with Lanes to open the pane; and background agents paused once the plan's 5-hour use passes
harness.background.pauseAtPercent(80% unless set).
Every decision belongs to the session that raised it: it's shown, asked and answered there, and its answer goes to that session's lead. Another session's band shows only how many wait there ("1 decision in session kim/7f3a91c2, answered there"). Only what's holding work up is a decision: a call Claude Code's own permission check refused (its auto mode, a deny rule) shows on that lane's card in the pane as its activity, and the lead asks you with code-kit ask only if there's no other way. code-kit decisions lists this session's.
Each button has a digit: type it in an empty prompt to press it. A digit presses the button showing it, the same in the band and the pane. Each decision is its own (two approvals are two); the pane's decision view says how many of each kind, then lists a short card for each, five at a time (j and k move), with the selected one in full below.
- Notifications. When something new needs you (an approval, a question from the lead, the lead waiting on you, a step or a merge that's yours), code-kit raises a system notification through your notification channel, once per decision, from the session the decision belongs to: "code-kit: the lead asks you". What was already waiting when the session started isn't announced. It needs Claude Code 2.1.301 or later; turn it off with
notify.on.
The lead loop
The loop keeps the build moving between your prompts, under harness.autonomy. /code-kit:init asks you how hands-off you want it. Until you've chosen, its first step waits in the band ("code-kit can keep the build moving between your prompts") with Keep going, Ask each time and Off, and your answer is recorded as your setting.
- Each step. Once the lead has finished a turn, with the lead idle and nothing typed in the prompt, the loop asks
code-kit nextand prompts the lead to dispatch the ready stories, review a finished one, or build its own. It never prompts the same step twice while nothing has changed, and never a story an agent is still on: one its lane agent is building, or a reviewer is reviewing, waits until that agent finishes ("waiting: ST-10 is still with its agent"). - It reads the lead's reply first. Before prompting again, the loop reads the lead's last reply with a side call over the lead's own conversation. A reply that waits on you (a question, a design to look at, an approval), or a turn you stopped, holds the loop until you answer or speak to the lead: the band says what the lead waits on, and its question is asked in Claude Code's own question dialog. A step the lead didn't take shows in the band as a note, with its reason and Ask again.
- Stories waiting on other work. When the lead says a story isn't due yet because it waits on work still going (a paired story a lane is building, a rebuild, a check running), the loop parks that story and leaves it out of every step, so a step naming it with others prompts only the others. It wakes when its own branch or state moves, or when a story it waits on (one the lead named, or an unfinished one building on it) changes state; that story's commits along the way don't wake it, nor do other lanes'. A story the lead's turn left exactly where it stood is parked too, whatever the reply said. After 30 minutes with nothing moving, the lead is asked once more.
- A step that doesn't move. One the lead was prompted with three times in a row that is still next shows as a note, not a decision: "Merge lead/st-1: still next after 3 prompts to the lead", with Ask again if you want it.
- A lean lead. A lane writes its full report, with its commands and their output, to
.claude/state/reports/<branch>.md, and hands the lead a few lines naming it; the stop hook won't let a lane on a story branch finish without it, or with a hand-back of more than ten lines, and the lead reads the file when it reviews. Lanes don't message the lead while they work. When a milestone finishes, the lead's context is compacted before the next one (harness.compact.milestone: you're asked by default, or it's done at once): the summary keeps what's in flight and what waits on you, and drops the finished stories' detail. - Token use. A long-lived agent rereads its whole context on every request, which is most of what a build costs. So each lane agent builds one story; one past
agents.lanes.contextLimitis told to wrap up, and the lead carries its story on with a fresh agent that reads its report (code-kit worktrees releasefrees the branch). The lead's context is compacted between turns once it's pastcompact.at. The loop reads the lead's replies with Sonnet, and the reviewers run on Sonnet by default. - Designs. A design story is handed to the lead once a session; while you and the lead design it, the loop leaves it to you.
- One session leads. The loop runs in the project's lead session: the one that started when no other led, or the one you chose with Lead from here (or
/code-kit:lead). See Several sessions in one project. - Modes. Pause and Resume stop and start it. Under
propose, each step waits for Go; underoff, the loop does nothing. When every story is done, the band says so and the loop stops. - Choices in Claude Code's dialog. A decision you settle by choosing (how hands-off the loop is, a step to take under
propose, a stale claim, a question from the lead with answers offered) is asked in Claude Code's own question dialog while the lead is idle, once; dismissed, it stays in the band and the pane's Needs you. Approvals, send-backs and settings, which need your reason, stay in the pane. - Stalled lanes. A lane agent with no tool call for
harness.stall.nudgeMinutes(5) is messaged, unless its work is done (it handed back, or its branch is in review or the merge queue). Atstall.restartMinutes(10) the lead is asked to stop it and dispatch its story again, at moststall.maxRestarts(2) times; after that the lead decides how the story goes on (a narrower brief, smaller stories, or handing it back), and the band shows a note. - The reviewer agent, with
harness.agents.reviewer.on. A finished branch is first reviewed in the background by code-kit's reviewer agent, which the lead starts: it runsverifyand the review checklist, read-only, and grades each finding blocker, concern or nit. The lead's review prompt then carries the findings, and a blocker means the branch is sent back. The pane shows each review, running with its clock or what it found. If the reviewer hasn't reported within 15 minutes, or the plan's 5-hour use is pastharness.background.pauseAtPercent, the lead reviews without it. - The merge queue (
code-kit queue). A branch the lead's review passes joins the queue. Branches merge one at a time, in the order they passed, each verified against the base as it is then: the lead merges whereapprovals.delegate.mergeis on underautonomous, otherwise the band shows the head with Merge for you. Who may merge is read from the config committed on the base, so a branch can't grant itself merges. The queue records the commit each review passed, and merges exactly that commit: a branch that moves on after its review leaves the queue (moved) for the lead to review it again. A head that conflicts or fails verify goes back to its lane. A merged lane's worktree is removed when it holds nothing uncommitted. Wherebranches.publishis set, each merge then reaches the remote's base: pushed, or, where the remote only takes pull requests, as one code-kit opens and (where you delegate pushing) merges. A story's branch merges only into the base, through the queue: an agent'sgit mergeof it into any other branch (an integration branch such aslead/next) is refused, since the story would look unmerged and its worktree would never be cleaned up. The pane lists the queue, and lanes whose branch waits in it readqueued. - After a crash. Every agent's tool calls are recorded before and after they run, per story, in
.claude/state/events/(paths, commands, results and commits; never file contents or prompts, and never sent anywhere). When a session or an agent stops mid-story,nextchecks each call left open against git before it chooses any work: a commit or a merge that happened is recorded as done, an edit or a test can simply run again, and a command that can't safely run twice and that git can't settle (a migration, a deploy) goes to the lead to find out, never run again on its own. The fresh agent that carries the story on is briefed from the log (code-kit replay <story> --brief), even when the stopped one wrote no report, and lanes commit after each step whose tests pass, so a crash loses at most one step. A merged story's log goes 30 days after it merges. - The first merge. There's none for you: setting up commits the rules you approved straight onto the base, and in a project set up before that, the lead puts them there itself (
code-kit queue rules) before its first merge.
The pane
/lanes opens it; /lanes again, or Escape, closes it. It stays current within 2 seconds of a commit, a branch change, a plan edit or an agent starting or stopping. It's one page of panels with no tabs, each starting with a status line (its name, its state in a word or two, a count at the right), and a panel with nothing to say takes no room:
- Build: is the build moving? The milestone being built and a bar of its requirements by state, counted from the trail: tested, built but untested, in progress and todo, each its own glyph and colour, with the counts beside it ("Milestone 3: Booking · 9 of 14 tested · 3 untested"). A milestone whose stories are all merged with requirements untested says untested, not done. Beneath it, the loop's next step, with Pause or Resume on p.
- Needs you: everything waiting on you, and only here in the pane, most urgent first. The first is open with its story, what it's for and its buttons; the rest are a line each, and choosing one opens it in its place. Why… shows its full card: what you asked for, the story and its requirements, what was happening, what's already settled, why the lead couldn't settle it, and each option with what it does.
- Lanes: where each story is. Each lane at work is a card, the lead's first: its character, the story, its state, its agent's current tool with a clock (amber once quiet, red when stalled), its tokens, and two small pictures. Its trail: a cell per requirement the story cites, coloured as in the Trail, so you see coverage grow as it moves. Its map: a chip per layer its changed files are in, red while a layer rule or another refusal is unresolved there, amber where a changed file's Context Graph card isn't written or is out of date. A card also says the background review's state, a note from the loop (nudged, restarted, stalled) and, with Context Graph, edits made without understanding. Lanes with nothing at work fold into one line ("resting: api, data, qa"). When the cards don't fit, the lanes are swimlanes on a shared time axis, a line each.
- Queue: the merge queue while it isn't empty: each branch, its story, the commit its review passed and its state, and what it merged or sent back lately, and why.
- Log: the build's latest events, a line each ("14:02 ✓ ST-4 merged · web"), yours in yellow.
Each lane has its own character, from its name: six shapes, then a mark (glasses, a scarf, a belt) for each lane past six, so up to 24 lanes read apart without colour; the lead's wears a crown. While its agent works it plays the kind of work its current tool is (writing, running, reading, thinking); it raises a hand while it waits on you, slumps when stalled, and rests otherwise. It arrives with a story, hands back its work, stumbles when sent back, and cheers when its story merges. Only the characters, the clocks and the rail from the lead to the lanes it dispatched move, and only while work flows, each on the surface's own frame clock; harness.motion off keeps everything still. /lanes characters shows every lane in every pose and move.
Keys, listed on the pane's last line for what's selected: j and k move through what needs you and the lanes; Enter opens what's selected; a digit presses the button showing it. A selected story's acts are buttons on its card, each with its key: a approve, r review, s send back (with your reason), m merge, w watch (its agent's own transcript in the main view, on Claude Code 2.1.301 or later), n nudge and x stop; t and c open its trail and code map. p pauses or resumes the loop, f filters the lanes by name, agent or story, and l opens the Journal.
Views, over the panels; Escape comes back:
- A story, with Enter on its card: its requirements beside its spec-check report, the reviewer's findings by severity (each a way into the diff),
verify's problems, its usage and loop steps, its timeline, and its diff against the base, file by file, with the acts its state allows. - Trail (t on a story, or
/lanes trail): every requirement in the specs as a cell by spec, its glyph and colour saying whether it's done and tested, done but untested, in progress, todo, without a story or removed, with a legend counting each. h/l and j/k move between cells and specs (or pick one from the list), showing the requirement's stories, their branches, the code they changed and the tests that name it. Beneath the cells, the spec's trail drawn as a flow, each requirement to its stories, code and tests, with a missing link dashed in red. - Code map (c on a story, or
/lanes mapfor the whole project's folders by layer): a story's files in a column per layer with the imports between them. A file fills with a lane's colour while that lane reads or edits it, and says whether its why (its Context Graph card) is written, out of date or not written yet. j/k select a file to see its card and what it imports and what imports it, and o opens it in Context Graph. - Journal (l, or
/lanes journal): the build from your request to now in plain sentences, by day: the request and the decisions in the brief, the specification, each story handed out, sent back and merged, each approval and who gave it, each milestone finished, and the loop's steps this session, yours in yellow. - Usage (
/lanes usage): the plan's 5-hour use, each story's and each lane's tokens, and the background agents' share.
The pane has three layouts: inline above the prompt when the terminal can't dock it (at most four lines: the Build bar, a cell per lane coloured by its state, and the first thing waiting), docked beside the transcript (the panels in one column), and wide, docked at 110 columns or more (Build across the top, Lanes on the left, Needs you, Queue and Log on the right). Desktop draws the same panels, with the characters, bars and timelines as SVG.
Other views
- Refusals a code-kit hook gives an agent are drawn as cards: what was refused, the rule, what to do, and Approve… where your approval would allow it. The full text is a press away.
/harnessopens the harness settings: how the lead loop acts, how long a call waiting on your approval is held, when a stalled lane is nudged and restarted, the reviewer agent and its model, the lane agents' model, the use at which background agents pause, and whether agents draft bug reports for code-kit. Changing one asks your reason, writes.claude/code-kit.jsonand logs akitapproval. Lanes, paths and rules still change through/code-kit:init./approvalslists the approvals waiting (one per approval and lane, with every file it covers), the approvals in force with minutes left, and every approval given, newest first: who gave it (you, or the lead within your delegated rules), its reason, and the files and branches it covered, merged or not. A request stops waiting once an approval is given for it, or its file is committed by someone who could; a read-only agent such as the reviewer never asks./verify-branchrunscode-kit verifyon this branch. Neither it nor/approvalscalls the model.
Approving from the band records (approved in the code-kit pane) with your reason, and a merge from it says it was merged by the person from the pane. A settings change is logged as made in the pane. These happen only on your press: the hooks refuse approve --via pane, merge --person and settings set from every agent.
Build
Once init has switched the rules on, the lead runs the build in a loop:
/code-kit:dispatch next # start every ready story on its lane
/code-kit:decide # decide what lanes asked for: approve, offer something better, or bring it to you
/code-kit:review ST-12 # check a finished branch, then merge or send it back
/code-kit:status # where the build stands against the spec- Dispatch picks the stories whose dependencies are done, one per lane: a lane runs one story at a time across the whole project, and the open slice's stories go first (a story's
**Slice:**, the feature it's part of;harness.slices.open). It checks that no open question blocks them and that their contracts exist, then starts each lane agent in its own worktree on the branch<lane>/st-<n>, every free lane in parallel. A second agent for a lane that's running one is refused. Each brief names the story's requirements and acceptance criteria. Lanes spec-check first, build end to end, name requirement IDs in their test titles, and commit. - Decide takes what a lane asked for and couldn't do itself: a package, a migration, a protected file, a UX question. The lead judges it against the story's requirements, the spec, the principles and the architecture, and approves it, tells the lane what to use instead, or brings it to you when it crosses a guardrail. See Who decides what.
- Review runs
verifyon the branch, then checks each requirement: built end to end, proved by a test, matching the spec-check report, and nothing beyond the story. It ends with merge, send back with the list of problems, or a question for you. - Status traces every requirement to its story, branch and tests. It shows what's done, in review, ready and blocked, which requirements no test names, and where the plan's status lines are out of date.
Several sessions in one project
You can open several Claude Code sessions in one project: one building the plan, others auditing, checking merges or looking into something ad hoc. code-kit treats each session as a member of the team, as it treats each developer in a team:
- One lead session. The loop runs there, and the plan's stories are dispatched from it. The first session to start leads; one started while it's open is a working session. When the lead session ends, no other takes over by itself: the next session to start leads, or you choose one with Lead from here or
/code-kit:lead.code-kit whosays which this session is. - Working sessions stay quiet. Their band shows only their own: their refusals and held calls, their lead's questions, their task. No loop prompts, no other session's decisions, dialogs or notifications. Every rule still applies.
- Ad hoc work records a task. A working session records what it's doing before it changes code, with the paths it'll change:
code-kit task "Integration readiness" --touches "src/integrations/**". The task leases those paths: another session's agents writing there are refused, naming the session and its task.code-kit sessionslists the sessions and their tasks;task doneends one, andtask take <session>moves a task whose session ended to yours. - Each in its own worktree. A working session that changes code works in a worktree of its own (
claude --worktree); its writes in the main checkout are refused, so its uncommitted files never block another session's git. Where git does refuse because of another session's uncommitted files, code-kit says whose they are. - One story per lane, across every session. A lane runs one story at a time, whichever session started it (
code-kit lanes).
Spec: docs/specs/23-the-team.md.
Teams (preview)
For several people in one repository, each with their own agents. Not every developer works in a team, so it's off until you set it up: /code-kit:init asks whether others work in the repository, and /code-kit:team-init sets the team up (or changes it, or takes it out) at any time, asking for the tech lead and each lane's owners and showing what it writes first. Everything goes through GitHub; there's no server:
- Owners and claims. A lane's owners are the only ones whose agents work it. Making a story's branch claims the story, through a ref in the shared repository, so two people can't both take it. The lead says what files the story will change (
team claim --lease), and each commit adds what it changed; another person's agent editing a leased file is refused with who holds it. - The tech lead's paths. Only the tech lead's sessions change the plan, specs and contracts. Anyone else raises an intent (
code-kit intend), a discussion in the repository's Intents category, before any code. The tech lead agrees, drops or marks it a duplicate; an agreed one becomes a numbered decision record (code-kit intend record) through a pull request, where every agent reads it. - Since you last looked.
code-kit team sincesays, by person, what was pushed, opened, merged, reviewed, claimed and intended. The Team pane shows it. /teamopens the Team pane: the tech lead, who you are, and what coordinates the team (GitHub, or a hosted provider, and whether it's live). Its tabs: People, each person with their lanes, where they're at work (with a live provider), claims, pull requests and intents; Since you looked; Claims, with Keep and Release on yours, and Release and Reassign for the tech lead; Intents, with Agree and Drop for the tech lead; Drift, when the provider checks for it; and Setup, the team's config and what GitHub still needs.- Stale claims (nothing pushed for three working days) are flagged, never released on their own: the holder keeps or releases it, and the tech lead can reassign it.
- GitHub does its part.
code-kit team codeowners --writewrites CODEOWNERS from the config, and"board": { "owner", "number" }mirrors the plan onto a GitHub Project.
It's a preview: it's tested end to end against a stand-in for GitHub, and has been run once against a real private repository (claims, leases, intents as discussions, decision records, since-you-last-looked and the Project board). Spec: docs/specs/16-teams.md.
Enforce it in CI
The hooks only see Claude Code sessions. For everything else, init offers .github/workflows/code-kit.yml (from templates/github-workflow.yml). It runs verify --no-checks on every pull request, through npx @warren-dean/code-kit@<version>, pinned so CI doesn't change under you: the rules, in seconds, with a 10-minute limit, and a new push cancels the run it replaces. The project's checks aren't run again there, because the merge queue ran them before each merge; the project's full CI runs on its release branches instead, once per release (code-kit release <version> cuts one). code-kit ci check says what in the project's own workflows still runs once per story, and code-kit ci usage what the project spent. The workflow needs no secret. Mark the code-kit check as required on the protected branches, and keep its install step: a fix story's test is run without its fix (REG-1). Add gitleaks to the workflow for the secret scan of commits' content.
verify can't see spec-check reports, because .claude/state/ isn't committed. Review covers those.
Keep it up to date
Re-run /code-kit:init whenever the build moves on: new apps or packages, moved folders, a changed plan, or files unowned keeps listing. It drafts the whole config again, then shows you the difference before anything changes:
+ lane jobs: jobs-engineer, workers/jobs/**
~ lane backend: exclude + workers/jobs/**
~ layer services: mayImport - schemas
Owner backend lane / backend-engineer → jobs lane / jobs-engineer: 14 file(s), e.g. workers/jobs/run.tsLanes never change silently: only when you approve the diff.
code-kit's own instructions to your agents, its section of CLAUDE.md and each lane agent's Always: rules, are kept between markers and rendered from your config, so a newer code-kit can bring them up to date without touching anything you wrote around them. code-kit instructions says where each stands, and --write updates them. When they're behind, code-kit next says so before any other step and the loop asks the lead to update them. A setup from before the markers is merged once, by the lead, keeping your own lines.
The CLI
Run from the project root, as code-kit <command> once installed (npm i -g @warren-dean/code-kit), or npx @warren-dean/code-kit <command>. Add --config .claude/code-kit.draft.json to any command to read a draft instead. In a session, the /code-kit:check skill runs check and unowned for you. next also works in a project without code-kit, or outside a git repository.
code-kit check [--json] # validate and summarise
code-kit who <path>... # owner and layer of each path
code-kit unowned # tracked files nobody may write
code-kit diff --config .claude/code-kit.draft.json # what a draft changes
code-kit baseline [--write] # layer violations in the code as it is
code-kit graph [--depth N] # which folders import which
code-kit verify [--base ref] [--branch name] [--no-checks] [--json] # a branch's changes, for CI and review
code-kit security [--base ref] [--branch name] [--json] # whether a branch needs the security review, and why: sign-in, permissions, outside input, personal data, payments, secrets, dependencies
code-kit status [--base ref] [--json] # stories and requirements against the spec
code-kit next [--base ref] [--json] # the step to take now, from the project's state
code-kit worktrees [--json] | worktrees release <branch> | worktrees prune [--quiet] # the project's worktrees and why each stays; free one for a fresh agent on its story, or remove every one that's done with, keeping their reports (also after each merge and as a session starts)
code-kit worktrees discard <branch> # the person's, from the band: remove a merged worktree that still holds changes, kept as a patch
code-kit instructions [--write] [--json] | instructions show lead|lane [<lane>] # code-kit's part of CLAUDE.md and the lane agents: where each stands, brought up to date, or printed
code-kit standards [--json] [--for <path>...] # the project's standards with any its organisation's provider supplies, and what's wrong with them; --for keeps the ones for those files' layers
code-kit trace <path>... [--json] # what a file is for: requirements, lane, layer rules
code-kit merge <branch> --delegated|--person [--into branch] # merge a branch that passes verify: the lead where approvals.delegate.merge is on, or the person
code-kit publish --delegated|--person # put what's merged into the base onto the remote's, as branches.publish says
code-kit replay <story> [--json|--brief] # a story's state rebuilt from its event log: its phase, last commit, tests, what's left open
code-kit reconcile [--json] # check what a stopped agent left open against git; the lead records what git can't settle with --act
code-kit requests [--json] # approval requests still waiting, numbered, each the lead's or the person's to decide, the approvals in force, and every approval given
code-kit requests drop <n>[,<n>…]|all [--reason "…"] # stop waiting on requests nobody needs answered (the lead, or Not needed in the pane)
code-kit ask "<question>" [--story ST-n] [--blocks] [--why "…"] [--option "Label: what it does"]... # the lead asks you what it can't settle; it waits in your band and the pane's Needs you
code-kit ask [--json] | ask answer <id> "<answer>" | ask withdraw <id> --reason "…" # the questions; your answer (given in chat, recorded by the lead); one settled another way
code-kit report "<title>" --what "…" [--expected …] [--command …] [--refusal …] [--rule …] # with harness.report.on, an agent drafts a bug report for code-kit
code-kit report [--json] | report send|discard <id> # the drafts; sending files it on code-kit's GitHub with your gh, which only you do
code-kit stops [--session id] [--json] # finish checks that refused an agent's last stop and still fail
code-kit sent-back <branch> [--reason "…"] # record a review's send-back: status shows the story as sent back until the branch moves on
code-kit ci check [--json] # what in the project's workflows runs once per story, or without a time limit, and the fix
code-kit ci usage [--days N] [--json] # the project's GitHub Actions runs and minutes, by workflow and trigger
code-kit release <version> # cut release/<version> from the base and push it: the project's full CI runs there once
code-kit decisions [--json] [--all] # what waits on you in this session, and a count in each other one; --all lists every session's
code-kit who # which session this is, whether it leads the project, and its task
code-kit sessions [--json] # the sessions working the project, which one leads, and each one's task
code-kit sessions lead # make this session the lead (the person's own act, as Lead from here)
code-kit task "<what>" --touches "<globs>" # a working session's ad hoc task, leasing the paths it changes; task done ends it, task take <session> moves a stale one here
code-kit lanes [--json] # what each lane is running now, one story each across every session
code-kit lanes release <lane> # free a lane whose agent stopped without finishing
code-kit settings [--json] # the harness settings in force, with their defaults, and who decides each approval
code-kit settings set <key> <value> --reason "…" # change one, as the person (approvals.<name> lead|person among them): written to the config and logged as a kit approval
code-kit queue [--json] # the merge queue: branches whose review passed, in order
code-kit queue add|drop <branch> # the lead queues a branch at the commit its review passed, or takes it out
code-kit queue rules # the lead puts the approved rules onto a base that has none yet (a project set up before 0.8.0)
code-kit queue merge --delegated|--person # merge the head at the commit its review passed, verified against the base as it is now; a conflict or failed verify sends it back to its lane, and a branch that moved since its review leaves the queue to be reviewed again
code-kit loop [--json] # whether the person paused the lead loop; other tools' background work pauses with it
code-kit loop pause|resume # the person's act, as Pause and Resume in the band
code-kit loop lead|release --session <id> [--take] # which session runs the loop: the mod's, and Lead from here in the band
code-kit story <id> [--json] # one story's requirements, spec-check report, diff against the base and verify (without checks), for the drill-down
code-kit timeline [--json] # when each story's work happened: its branch made, last commit, merge, send-backs and approvals
code-kit journal [--json] # the build's lineage: the request, its decisions, the spec, each story handed out, sent back and merged, each approval and who gave it
code-kit team [--json] [--fresh] # the team (preview): tech lead, owners, claims, intents and open pull requests (spec 16)
code-kit team since [--json] [--peek] # what everyone pushed, opened, merged, reviewed, claimed and intended since you last looked
code-kit team init --tech-lead <handle> [--owner <lane>=<handles>] [--stale-days n] [--intents category] [--write] | team init --off [--write] # show, then write, the team's setup; or make the project one developer's again
code-kit team claim <story> [--lease globs] | team release <story> # claim a story for yourself, leasing what it changes; release it (yours, or any as the tech lead)
code-kit team keep <story> | team reassign <story> <handle> # 