Repository navigation
Development
| Path | Holds |
|---|---|
.claude-plugin/ |
plugin.json (name, version and the brain MCP server) and marketplace.json
|
skills/ |
method, shape-ticket, adversarial-review, lean-retro
|
hooks/ |
hooks.json and one Python file per hook |
lib/ |
hookkit.py (shared hook plumbing), house_style.py (the slop and attribution lists), leanstats.py and heavy_lock.py (the lane lock) |
bin/ |
The scripts. Linked at the repository root. |
examples/, templates/
|
A starting rules file and the stale pull request workflow |
vscode/ |
The Lean Brain Extension, with its own vitest tests |
docs/ |
remote-lane.md and the specs and plans |
tests/run-all.sh |
Runs every self-test |
bash tests/run-all.shIt does four things:
- Fails on a duplicate key in the plugin JSON files. A text merge can leave two
PostToolUsekeys, which still parses, and the parser keeps only the last. - Fails when
skills,hooks,bin,libor.claude-pluginchanged since the current version's tag. - Lists scripts that have no
--self-test. - Runs every
--self-testit finds, each capped at 300 seconds wheretimeoutorgtimeoutexists.
A script opts in by handling --self-test. The runner finds scripts by that string, so a new hook or script is covered without editing the runner. Exit code 77 from a self-test means this machine cannot run it, and counts as skipped.
CI runs tests/run-all.sh on Ubuntu for every pull request and every push to main. It fetches the tags, since step 2 compares against the version's tag.
run-all.sh does not run the extension's tests. In vscode/:
npm ci
npm testnpm test runs vitest. npm run typecheck runs tsc --noEmit. The extension has its own version in vscode/package.json, separate from the plugin's, and the version check in run-all.sh does not cover vscode/.
Claude Code updates an installed plugin only when its version changes. So:
- A change to
skills/,hooks/,bin/orlib/moves the version in.claude-plugin/plugin.json. - The merge is tagged
v<version>. -
claude plugin update lean-agent@lean-agentpicks it up.
Step 2 of the tests is what enforces step 1.
Hooks and scripts run on Linux and on macOS's stock bash 3.2. heavy.sh uses util-linux flock on Linux and a Python lock helper (lib/heavy_lock.py) elsewhere, and its self-test runs the Python path everywhere and flock on Linux. It reports skipped (77) only when a machine has neither python3 nor flock.
Hooks need Python 3.9 or newer and use only the standard library.
- Read one JSON event on stdin with
hookkit.read_event(). - Run
mainthroughhookkit.run, which turns any internal error into exit 0. - Give it an off switch and a
--self-test. - Register it in
hooks/hooks.json.
A hook that notes should never block. A hook that blocks should name what to change.
Every change arrives as a pull request and a person merges it. Changes to the rules themselves come from the Retro, each with the signal that triggered it and the number that should move.
No agent attribution and no slop in commits, pull requests, issues or releases. The public-text.py hook blocks both. Rewrite the text, do not turn the hook off.
Start
The method
- 4-4a. Scripts, not instructions
- 5-6. Rework and concurrency
- 14a-14h. Field notes from parallel agents
Brain and the test queue
How the plugin runs it
- Shaping a Ticket
- Models and the Cascade
- Adversarial Review
- Review Rules
- Holdout Check
- Measuring a Batch
- Retro
Reference