🐎 Rein
Keep your Claude Code agents lean and in control.
A Claude Code plugin that turns agent work into a bounded, measured, independently-reviewed flow — and tells you what it actually cost, per model.
💸 The problem, measured
Optimizing agent cost by "making the model write less" does nothing. Over real Claude Code transcripts:
| Where the tokens go | Share |
|---|---|
🔴 cache_read — context re-read every turn |
~90% |
🟠 cache_write |
~10% |
| 🟢 output — what a compressor would attack | ~0.3% |
| ⚪ fresh input | ~0% |
Cost ≈ turns × context size. One agent that ran 241 turns, re-reading ~234k of context on every single turn, was most of a 112M-token run.
And Claude Code has no native eviction of stale tool results — only /compact, which is
lossy and breaks the prompt cache. A long agent's context only grows.
Measured effect of this kit: 241 turns → 26 · Opus 100% → 0% · ~7× less context per turn.
🔁 The flow
flowchart TD
A["🧭 /rein:rein-plan<br/><i>Why · Scope · Decisions · Tasks</i>"]
B{{"rein next<br/><i>deterministic gate</i>"}}
C["⚙️ Implement<br/><i>bounded loop of FRESH agents</i><br/><i>context resets each step</i>"]
D{{"Verify<br/><i>ask the plan, not the agent</i>"}}
E["🔍 /rein:rein-audit<br/><i>whole change · 5 axes</i>"]
F["✅ merge"]
G["✋ escalate to you"]
X["🛑 stop"]
A -->|you confirm| B
B -->|ready| C
C --> D
D -->|contradiction| X
D -->|consistent| E
E -->|"APPROVED + gate green"| F
E -->|"needs your judgement"| G
E -->|"CHANGES_REQUESTED · max 3"| C
style A fill:#1e3a5f,stroke:#4a90d9,color:#fff
style C fill:#1e4620,stroke:#4caf50,color:#fff
style E fill:#5f1e3a,stroke:#d94a90,color:#fff
style F fill:#2d4a1e,stroke:#8bc34a,color:#fff
style X fill:#4a1e1e,stroke:#d94a4a,color:#fff
style G fill:#4a3a1e,stroke:#d9a94a,color:#fff
Three roles, and no agent approves its own implementation. Every run works in its own git worktree; unapproved work is never merged.
🚀 Quickstart
claude plugin marketplace add luisfarfan/rein-agentic-kit
claude plugin install rein@rein-agentic-kit --scope user
Restart Claude Code, then run inside it:
/rein:rein-ping → is it wired here, and what did it detect?
/rein:rein-setup → probe the optional tools · confirm to provision them
ping reports the detected stack, the resolved commands and where each one
came from. If it got everything right, you configure nothing.
Everything runs inside Claude Code — there is nothing to add to your
$PATH. Claude Code puts the plugin'sbin/on the path of its own sessions, which is where the skills, the commands and the agents callrein. In a plain terminalreinwill not be found, and that is expected: the CLI exists to be called by the flow, and the two commands a person runs by hand have slash commands above.
Want rein in your own terminal too?
The plugin's bin/ is only on the path of Claude Code's own sessions — a plugin
cannot add anything to your shell. For rein dashboard, rein ledger or
rein doctor from a terminal, install the CLI the way every tool this kit
integrates ships (serena and graphify via uv, codegraph and openspec via npm):
uv tool install rein-agentic-kit # or: pipx install rein-agentic-kit
Same code, same version, zero dependencies. The two channels cannot drift: a test fails the build if their version strings ever disagree.
To update:
claude plugin marketplace update rein-agentic-kitthenclaude plugin update rein@rein-agentic-kit— qualified with the marketplace, or it reportsPlugin "rein" not found.
Then, per change:
/rein:rein-discover → think it through, until the unknowns that matter are closed
/rein:rein-plan → you confirm the plan
/rein:rein-apply → read the verdict
You come back when it finished, not before.
🧰 What you get
Skills — the three roles
🔭 /rein:rein-discover |
Think a problem through before committing to a shape — investigate, ask, produce understanding. Writes no plan, implements nothing |
🎭 /rein:rein-role |
Assign this session's role: planner · implementer · reviewer |
🧭 /rein:rein-plan |
Plan into verifiable tasks — dry-run and explicit confirmation before writing |
🔨 /rein:rein-step |
Exactly one task. Max 3 attempts, max 5 failed commands, no self-approval |
🔁 /rein:rein-steps |
Bounded loop of step, stopping on verifiable signals only |
🔍 /rein:rein-audit |
The independent gate: mechanical checks first, then five-axis judgement |
♾️ /rein:rein-apply |
Runs all three end-to-end in an isolated worktree |
CLI — the deterministic half
rein doctor # 🩺 start here: stack, resolved commands, and the source of each
# plus a "version : <verdict> -- <reason>" line -- up-to-date / stale /
# unknown against the marketplace clone, with fix commands when stale
# --json: {"version", "pluginRoot", "project", "plan", "verifyState",
# "ledger", "staleness"} -- staleness is {"verdict", "reason",
# "installedVersion", "availableVersion"}
rein setup # probe the optional tools · --install provisions what is missing
rein detect # stack + commands, with the source of each
rein tasks # the plan, parsed
rein context # detect + plan in ONE round-trip — what the loop's first agent runs
rein verify # actually RUN each resolved command and report the truth — an inference is not a fact
rein gate # ✅ did they PASS — 0 green · 1 the code is wrong · 126 the environment could not run them
# --plan: run each TASK's own Verification before implementers are paid.
# Failing is fine (the work does not exist yet); proving NOTHING is not —
# a missing test module or a selector matching zero tests exits non-zero
rein plan-check # mechanical findings on a drafted plan's own text — what a regex can
# honestly decide; the semantic call belongs to /rein:rein-plan's critique
# always exits 0 (D5: never a silent skip, never a hard stop) — the
# judgement there is the planner's. ✅ `--gate` is for a RUNNER: exits 1
# on a BLOCKING finding. `--tasks T001,T002` scopes it — a finding pinned
# to a task nobody will run cannot stop it, while one with no task id is
# plan-level and always counts
rein role <name> # ✅ one role's operating profile (planner|implementer|reviewer) as
# markdown — the same section /rein:rein-role reads, for any agent
# that cannot invoke a Claude Code skill
rein next # ✅ the gate: is there a task to claim, and may it be
rein close T001 # tick a checkbox deterministically — no agent hand-edits the plan
rein review # record / check a verdict bound to a code state
rein token-report# what a run really cost, per agent and per model, plus its wall clock,
# agent-minutes and their ratio (1.00x = nothing overlapped)
rein event <name># record a skill invocation — an EVENT, never folded into a run total
rein event task <id> <started|verified|blocked|merged>
# record a task TRANSITION as it happens — rejects any other word
# by name, without writing
rein state # per-task transitions folded from the event log + plan; given a
# workspace, folds every member into one table
rein ledger # history across projects, with deltas vs a marked baseline
# --json: {"runs": [...], "events_by_project": {...}} -- runs are
# unchanged row objects; events (D3) are counted separately, never
# folded into a run total
rein baseline # mark the run everything is compared against
rein dashboard # 📊 serve it all as a local page
rein workspace # discover .rein/workspace.json and report each member repo's
# branch and head; one explanatory line, exit 0, when none is found
rein linear list # issues from Linear, filtered (--repo / --priority /
# --max-priority / --state / --flow / --label / --parent) and
# ordered urgent-first. Linear numbers priority 1=urgent…4=low
# and reserves 0 for "none", so unprioritised sorts LAST.
# The 14 index cards are excluded unless --groupers
rein linear show # one issue with its prose, --json for the parsed record
rein linear comment
# leave a comment: --body TEXT, or --body-file PATH (`-` for
# stdin, which is what multi-line markdown wants). It exists so
# nothing has to reach for the raw GraphQL endpoint with your
# API key. There is deliberately no bare `linear state`: moving
# to Done belongs to `rein land`, behind its merge check.
# All three need REIN_LINEAR_API_KEY
rein intake # take an issue: Beads issue in the owning repo (only when its
# flow.config.json says tracker.kind: "beads"), branch named
# the way Linear already named it, Backlog → In Progress, and a
# comment saying where the work went. Re-running resumes; it
# never files a second Beads issue for one bug
rein land # close it out: Beads closed, Linear → Done, both carrying the
# merge commit. Refuses when the base branch holds no commit
# naming the issue — a squash merge keeps the id, not the sha
The rule that separates them: if it's a parse, a script does it. If it's a judgement, an agent does it. Every fact an agent doesn't have to rediscover is turns you don't pay for.
⚖️ The three levers
1️⃣ Bounded loop of fresh agents. Each task is at most N short, fresh agents handing off a
compact ledger (progress / remaining / filesTouched / verification) — never one agent
running 200+ turns. Context resets at every boundary, so spend stops growing without a ceiling.
2️⃣ Per-agent model routing. Mechanical → Haiku · code → Sonnet · the review gate → Opus. On a subscription this doesn't lower the bill — it frees the scarce Opus quota, which is the limit you actually hit.
3️⃣ Verifiable signals, not model judgement. A loop that stops when the model feels finished
has no gate. rein next answers "is there work to claim" from the plan; rein review check
answers "does this approval still apply" from content hashes. Neither asks a model anything.
❌ Deliberately not included
Tried and discarded with data, not taste:
- Output compressors — they attack the 0.3%.
- Multi-provider API swarms — the saving doesn't apply on a subscription, and more agents means more contexts re-read.
- Cross-session memory tools — orthogonal to the actual cost driver.
- Cache-aware proxies — no-op on already-cached traffic.
- Persona prompts ("you are a hexagonal architecture expert") — the same model with the same weights. Constraints that can be violated are useful; job titles are not.
⚙️ Configure
Everything is optional. Drop a flow.config.json at your project root to override:
{
"commands": {
"test": "uv run pytest -q",
"testOne": "uv run pytest -q {target}",
"lint": "uv run ruff check .",
"typecheck": "uv run mypy ."
},
"models": { "aux": "haiku", "impl": "sonnet", "review": "opus" },
"limits": { "maxTaskSteps": 8, "maxReviewRounds": 3 }
}
Precedence: flow.config.json › task runner (justfile / Makefile / Taskfile / mise) ›
autodetection. A project that already declares how it is built is never second-guessed.
🧱 Supported stacks
| Stack | Detected by | Verification |
|---|---|---|
| 🐍 Python | pyproject.toml, uv / poetry |
pytest · ruff · mypy |
| 🟨 Node / TS | package.json, pnpm / npm / yarn / bun |
vitest or jest · eslint · tsc |
| 🦀 Rust | Cargo.toml |
cargo test · clippy · check |
| 🐹 Go | go.mod |
go test · vet |
| 🎨 Frontend (Next, Vite, Astro, SvelteKit, Nuxt, Remix) | dependencies | rendered verification — unit tests alone don't catch "the tests pass but the UI is broken" |
| ☁️ Serverless / infra | serverless.yml, sst.config.ts, *.tf |
plan / validate — never deploy |
| 📦 Monorepo | sub-projects one or two levels down, no root manifest | two or more candidates are reported — it never picks one for you; exactly one candidate is not a choice, so it is resolved directly, reporting that project's own stack with its path carried (cd <dir> && ...) |
A resolved command is an inference until something runs it.
rein verifyexecutes each one and reports what happened, distinguishing "the binary is missing" (a setup problem) from "the suite ran and failed" (a code problem) — conflating them sends you to the wrong file. The loop runs it at Prepare: a broken gate found there costs nothing, the same one found at review costs a whole run.
Optional tools (serena, codegraph, graphify, openspec) are probed, never
required: if one is absent the flow degrades, it does not break. rein setup reports
what is missing and installs it on request — and distinguishes installed from usable,
because graphify without an index and a just-registered MCP server are both present and
inert.
No retrieval speedup is claimed. With codegraph present, the loop's implementers are taught
codegraph query/callers/callees/node/impact(plussyncto refresh the index after their own edits) as the one owner of "what is this / who touches it / what breaks if I change it"; with codegraph absent, serena'sget_symbols_overview/find_symbol/find_referencing_symbolscover the same ground as a fallback. Whether either reduces the turns an agent spends orienting is unmeasured, and the one control available points the other way. See docs/decisions.md D2.
One owner per question. A tool is dropped from the prompts for having no exclusive question — not for being bad:
flowchart LR
Q1["❓ what is this?<br/>who touches it?<br/>what breaks if I change it?"] --> CG["🔎 codegraph<br/><i>query · callers · callees</i><br/><i>node · impact</i>"]
Q2["❓ edit this symbol<br/>type errors, no build"] --> SE["✏️ serena<br/><i>replace_symbol_body</i><br/><i>get_diagnostics_for_file</i>"]
Q3["❓ docs · papers · images"] --> GR["📚 graphify<br/><i>/graphify skill</i><br/><i>outside the loop</i>"]
style CG fill:#1e4620,stroke:#4caf50,color:#fff
style SE fill:#1e3a5f,stroke:#4a90d9,color:#fff
style GR fill:#4a3a1e,stroke:#d9a94a,color:#fff
Measured on this repo, same question, same index cost (~1.5s, no LLM, no API key for either):
asked "where is it decided that a command is not invocable?", codegraph returns
NOT_INVOCABLE_EXIT_CODES = (126,127) with its file and line in 258 tokens; the graph
tool it replaced returned 546 tokens of an unrelated example config, because a JSON key
happened to match. Confident noise is worse for an agent than no answer — it cannot tell the
two apart, and it pays for the difference on every later turn.
📊 Measure
rein token-report && rein dashboard
Reads Claude Code's own JSONL transcripts (including cache_read) and breaks a run down per
agent and per model. Every run is summarized into ~/.claude/rein/runs.jsonl, so history
survives transcript rotation.
On the word "savings": this measures consumption. A saving needs a baseline. Mark one with
rein baseline mark and every later run in that project gets a signed delta. Without one, the
honest numbers are the three that predict cost — turns/agent, ctx_max/turn,
% of tokens on Opus.
🙃 Honest limitations
Things a README usually hides:
- The reviewer is calibrated hard. Across 9 runs of this repo, 8 used all 3 rounds, and the
LAST round produced a real BLOCKING finding in 4 of them. Review is ~52% of a run's wall
clock and it earns it — which is why there is no
--fastthat reviews less. - A stalled agent burns wall-clock. One run spent 3.4 hours almost entirely in silent API retries.
- Runs are serial. Every run measured so far reports an overlap of
1.00x— tasks that declare no dependency can now run concurrently, but the plans written here have been chains, so the parallel path has yet to fire on real work. discoverwas considered and rejected — a per-run version of it was measured in the origin project and did not move the needle. See docs/decisions.md.
🗺️ Roadmap
| Phase | Scope | |
|---|---|---|
| 0 | Plugin plumbing · token-report · ledger · stack detection |
✅ findings |
| 1 | Config-driven core loop · tasks.md adapter — measured |
✅ |
| 2 | Stack-aware verification policy | ✅ |
| 3 | Local dashboard · per-agent model config | ✅ |
| 4 | Compose real browser verification | ✅ |
| 5 | Docs · a second distribution channel (PyPI) · release automation | ✅ |
| 6 | A second real-world project, and the parallel path on a plan that has one | ⏳ |
📄 License
MIT — see LICENSE.
Release files for rein-agentic-kit 0.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rein_agentic_kit-0.15.0.tar.gz | 409.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rein_agentic_kit-0.15.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 641.8 kB
Release files / rein_agentic_kit-0.15.0.tar.gz
| Download URL | rein_agentic_kit-0.15.0.tar.gz |
|---|---|
| Size | 409.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6f0b17a997ecac46f9d6496146e8e307d0b80b21bf44a92c1e457e1465fac518
|
|
BLAKE2b-256 checksum How to use checksums |
e51c7512e7e542f7991033e5dd91b222ff08233b511ca3576491552eab9e2dc8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logRelease files / rein_agentic_kit-0.15.0-py3-none-any.whl
| Download URL | rein_agentic_kit-0.15.0-py3-none-any.whl |
|---|---|
| Size | 232.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7d78f2e6bd802095f99053d5ad4061034213e0791170cce522215a8c085286d3
|
|
BLAKE2b-256 checksum How to use checksums |
a6757cac05fd665d6c97a91796f7bdfc719dd84ae61432e4cc4bf5274576486d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency log