[ docs ]

decree-harness.

Generate an agent harness for your codebase in one command. It scans the repo and the decisions your team wrote down, keeps everything in decree.json, and writes code for Claude Code, MCP, TypeScript and Python.

Quickstart

Needs Node 20.12 or newer. Run it from the root of your repository.

shell
# or put the key in .env
export ANTHROPIC_API_KEY=sk-ant-...
npx decree-harness

The wizard scans the repo, asks what the agent is for and which targets to write, plans the harness and generates it. Without an API key, --offline uses a heuristic planner that builds the same kind of spec from the scan alone:

npx decree-harness init --yes --offline --targets all

The planner and generated harness default to claude-opus-5; subagents default to claude-sonnet-5. Change either in decree.json or with --model.

What gets written

decree.jsonThe harness spec. Edit it, then run decree-harness generate.
.decree/profile.json (last scan), manifest.json (hashes of generated files), schema.json
agent/README.mdOverview of the generated harness and how to run each target
agent/harness.mdDesign doc: prompt, every tool, safety flags, planner notes
agent/evals.jsonEval cases
agent/.env.exampleEnv vars the tools need
agent/typescript/Standalone agent on the Anthropic TypeScript SDK: CLI, REPL, evals
agent/python/Standalone agent on the Anthropic Python SDK, with pytest tests
agent/mcp-server/MCP server for Claude Code, Claude Desktop and Cursor
agent/claude-code/CLAUDE.md, .claude/{agents,commands,skills,settings.json}, .mcp.json

Pick targets with --targets typescript,python,mcp,claude-code or all. When the code changes, run decree-harness generate again.

Decisions

init reads the decisions your repo already writes down and stores them in decree.json under decisions. It prints a summary such as Found 14 decisions: 2 live, 11 proposed, 1 superseded.

ADRsdocs/adr, docs/adrs, docs/decisions, doc/adr, adr/, architecture/decisions, decisions/. Title from the first heading, rule from the Decision section, rationale from Context. Accepted becomes live, Proposed stays proposed, Superseded, Deprecated and Rejected become superseded.
Rules filesCLAUDE.md, AGENTS.md (nested ones govern their own directory), .cursor/rules/*.mdc (its globs become governs), .cursorrules, .github/copilot-instructions.md. Rule-like bullets become decisions and start as proposed.
Post-mortemsdocs/postmortems, postmortems/, incidents/. Rule-like bullets under Action items, Lessons, Follow-ups and Prevention. They start as proposed.

Each decision is live, proposed or superseded. Proposed decisions are not served to agents until you confirm them. Superseded ones are kept for history and never served. Its governs globs come from frontmatter, then from repo paths the text mentions, then from the directory of a nested rules file, and otherwise cover the whole repo (**).

decree-harness decisions [--status <s>] [--json]List decisions as a table of id, status, governs and source.
decree-harness decisions extract [--dry-run] [--json]Re-read the repo. Keeps your statuses, adds new decisions and flags ones whose source is gone.
decree-harness decisions for <paths...>Show exactly what get_decisions returns for those files or directories (--proposed to include proposed ones).
decree-harness decisions confirm <id...>Mark proposed decisions live, so the agent follows them.
decree-harness decisions supersede <id> [--by <new-id>]Retire a decision. With --by, supersededBy points to its replacement.
decree-harness decisions reject <id>Retire a decision with no replacement (supersede without --by).

Commands that change decisions regenerate the harness if it was generated before. Pass --no-generate to skip that.

~/acme-orderszsh
$ npx decree-harness decisions for src/payments/refunds/create.ts
get_decisions {"paths":["src/payments/refunds/create.ts"]}
3 decisions govern these paths, most specific first. Follow them, and cite the id when one constrains your change.

[rule-refunds-agents-md-every-refund-must-carry-an-idempotency] Every refund must carry an idempotency key built with `refundKey()`
Rule: Every refund must carry an idempotency key built with `refundKey()`.
Governs: src/payments/refunds/** | Source: src/payments/refunds/AGENTS.md:4

[adr-0014-ledger-writes-go-through-ledgerservice] Ledger writes go through LedgerService
Rule: Write to `ledger_entries` only through `LedgerService`, in the same transaction as the change that caused the entry.
Governs: src/payments/**, src/ledger/** | Source: docs/adr/0014-ledger-writes-go-through-ledgerservice.md

[adr-0021-store-money-as-integer-minor-units] Store money as integer minor units
Rule: Store and pass amounts as integer minor units with a currency code. Never use floating point for money.
Governs: ** | Source: docs/adr/0021-store-money-as-integer-minor-units.md

Example output for an illustrative orders and payments service.

The get_decisions tool

When decree.json has decisions, every generated target gets a read-only get_decisions tool. Its input is { "paths": string[], "include_proposed"?: boolean }. It returns text: at most 8 live decisions whose globs match those paths, most specific first and repo-wide ones last. Superseded decisions are never returned.

The system prompt gets one line: call get_decisions with the files you are about to change, follow what it returns, cite the decision id, and stop if a request conflicts with a live decision. The TypeScript and Python agents and the MCP server read a decisions.json shipped with them. Claude Code calls the MCP tool, or a .claude/skills/decisions script when the MCP target is not generated. The generated CLAUDE.md explains how to look decisions up and lists none of them.

tool call · get_decisionsmcp
// the agent is about to edit a ledger adjustment
get_decisions { "paths": ["src/ledger/adjust.ts"] }

// result (text)
2 decisions govern these paths, most specific first. Follow them, and cite the id when one constrains your change.

[adr-0014-ledger-writes-go-through-ledgerservice] Ledger writes go through LedgerService
Rule: Write to `ledger_entries` only through `LedgerService`, in the same transaction as the change that caused the entry.
Governs: src/payments/**, src/ledger/** | Source: docs/adr/0014-ledger-writes-go-through-ledgerservice.md

[adr-0021-store-money-as-integer-minor-units] Store money as integer minor units
Rule: Store and pass amounts as integer minor units with a currency code. Never use floating point for money.
Governs: ** | Source: docs/adr/0021-store-money-as-integer-minor-units.md

Decisions in decree.json

decree.json lives at the root of your project and holds the tools, guardrails, targets and decisions. It is validated on every load. Print the full schema with decree-harness schema. Each entry in decisions has these fields:

idStable slug, e.g. adr-0003-store-events-in-postgres or rule-claude-md-never-write-raw-sql-outside-src.
titleShort name for the decision.
constraintThe rule an agent must follow, in one to three sentences.
statuslive, proposed or superseded. Only live decisions are served by default.
governsGlobs relative to the repo root. ** covers the whole repo.
sourceWhere it came from, e.g. docs/adr/0003-store-events-in-postgres.md or CLAUDE.md:7.
ownerOptional. Who owns the decision.
supersededByOptional, on a superseded decision: the id of the decision that replaced it.
rationaleOptional. Why the decision was made.

Commands

decree-harness / initInteractive wizard: scan, ask for the goal and targets, plan, generate
scan [--json]Scan only; writes .decree/profile.json
planScan and plan; writes decree.json only
generateRender decree.json into code (--targets, --out, --clean, --dry-run, --json)
refine "<feedback>"Change the harness in plain English, e.g. refine "make it read-only"
chatTalk to the agent in your terminal; tools run locally with approval prompts
run "<prompt>"One-shot run (--json for machine output)
evalRun the eval cases and report pass or fail (tools run in dry-run mode by default)
doctorCheck Node, API key, decree.json, required env vars, generated output
toolsList the tools in decree.json
schemaPrint the JSON schema for decree.json
login --url <dashboard>Connect this machine to your self-hosted dashboard (logout, whoami)
pushSync decree.json to the dashboard; a changed spec becomes a new version

Useful flags: --yes (non-interactive), --offline, --model <id>, --goal "<text>", --no-critique, -C <dir>.

Self-hosted dashboard

The dashboard keeps every version of decree.json and the eval runs against each one. trydecree.com doesn't host it yet, so run it yourself: it is a Next.js app on Supabase.

  1. Create a Supabase project and apply the SQL in supabase/migrations.
  2. In Supabase Auth, set the Site URL to your deployment and add https://<your-domain>/auth/callback as a redirect URL.
  3. Deploy frontend/ to Vercel or any Node host with DECREE_DASHBOARD_ENABLED=true, NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY.

Then point the CLI at it:

shell
npx decree-harness login --url https://decree.acme.dev
npx decree-harness push
npx decree-harness eval --push

In CI, create a token under API tokens on your dashboard and set DECREE_API_URL and DECREE_TOKEN. Tool inputs and outputs never leave your machine.

Safety model

  • Every tool carries readOnly, destructive and requiresApproval flags. Destructive tools always require a human yes (guardrails.approvalMode).
  • File tools are confined to guardrails.allowedPaths, including symlink escapes.
  • Shell tools substitute parameters with POSIX quoting and refuse guardrails.blockedCommands.
  • Values of secret env vars are redacted from tool output before the model sees them.
  • maxTurns and maxCostUsd cap every run.
  • decree-harness never overwrites generated files you edited (tracked in .decree/manifest.json) unless you pass --force.

Questions or a repo where the scan misses something? hello@trydecree.com. The hosted workspace is open to design partners first.