@stdd/cli
v0.3.0
Published
Spec + Test Driven Development — a markdown-first methodology kit for teams building software with AI coding agents
Maintainers
Readme
→ the docs edit IS the spec — reviewed before any code
→ tests gate behavior; visuals are design-first
→ working artifacts are never committed
→ one agent-neutral source, compiled per agent
→ mechanics over discipline — what can be checked, CI checksWhy
AI coding agents amplify a quiet failure mode: committed working artifacts. Plans and spec files written for one change land in the repo, go stale, and keep winning code search — an agent greps the tree, finds a convincing month-old spec that a newer doc contradicts, and confidently builds the wrong thing. Popular SDD frameworks make this worse by design: change folders and archives accumulate authoritative-looking text with no machine-readable authority.
stdd inverts the model. Your permanent documentation tree is the single source of truth. The edit to that tree is the spec — it comes first, in a reviewable diff, before the failing test; the failing test comes before the implementation. Everything ephemeral lives where ephemeral things belong: rationale in the PR description, history in git, deferred designs as dated project-log records. What can be verified mechanically, CI verifies; the rest is a written contract you review against — not folklore.
The loop
flowchart LR
A["Classify<br/>the change"] --> B{"Behavior<br/>change?"}
B -- "implementation-only" --> E["Implement"]
B -- "yes" --> C["Read docs →<br/><b>edit docs = the spec</b><br/>(first reviewable diff)"]
C --> D["Failing<br/>test"]
D --> E
E --> F["Verify"]
F --> G["PR evidence<br/>(CI-checked)"]
C -. "frontend visual work:<br/>design-first — build, review<br/>screenshots, then test behavior" .-> EWhere knowledge lives
One truth inside the tree; everything ephemeral outside it — so an agent
grepping the repo can only find the present. The one dated exception, the
project log, is marked machine-readably (authority: non-canonical
frontmatter) and the generated agent instructions forbid searching it
unless the user explicitly asks for history or deferred work.
flowchart TD
CH(["a change"])
subgraph TREE["in the repository — what agents grep"]
DOCS["<b>docs/</b> — canonical truth<br/>present tense, single source"]
LOG["docs/project/ — dated records<br/>deferred designs, decisions<br/><i>non-canonical</i>"]
STDD[".stdd/ — method + playbooks"]
end
subgraph OUT["outside the tree — explicit access only"]
PR["PR description<br/>rationale, alternatives, scope"]
GIT["git history<br/>what changed and why"]
PAD["session scratchpad<br/>plans, task lists"]
end
CH -- "durable rules" --> DOCS
CH -- "deferred design" --> LOG
CH -- "rationale" --> PR
CH -- "history" --> GIT
CH -- "plans, sequencing" --> PADSee it in action
You: We need admins to adjust an order's price before invoicing.
AI: [stdd-brainstorming] One question: order-level or per-item? …
Agreed: order-level signed adjustment, review-status only, reason
required, customer notified.
AI: Commit 1 — docs/domain/pricing.md: "Manual Price Adjustment" rules.
(the spec, reviewable on its own)
AI: Commit 2 — failing integration test for the new rules.
AI: Commit 3 — implementation; test green; verification output attached.
PR body:
Docs updated first: docs/domain/pricing.md
Decisions and alternatives: per-item adjustment rejected because …
CI: stdd check ✓ no committed working artifacts, no temporal narrative
stdd check-pr ✓ evidence line names the changed docsQuick start
cd your-project
npx @stdd/cli init --tools claude,codexOr install globally — the command is called stdd:
npm install -g @stdd/cli
stdd init --tools claude,codexstdd init installs .stdd/ (the method contract + playbooks + config),
generates Claude Code skills, and prints the section to add to your
AGENTS.md for Codex and any other agent that reads it. Everything it
generates is recorded with content hashes in .stdd/manifest.json, so
check and doctor detect hand edits and stale copies of any generated
file — not just version drift.
Not sure where an existing repo stands? Get a report in seconds:
$ npx @stdd/cli doctor
✗ 6 committed working artifacts may mislead coding agents
✗ 2 canonical docs contain temporal narrative
✓ generated files match stdd v0.3.0
✗ AGENTS.md has no STDD section — paste .stdd/AGENTS-snippet.mdThen wire the guards into CI. On GitHub, generate the canonical workflow:
$ npx @stdd/cli init --ci githubIt writes .github/workflows/stdd.yml: stdd check for tree invariants,
and stdd check-pr --base against the PR body fetched live from the
API. Do not read the body from github.event.pull_request.body — that
payload is frozen at trigger time, so a body-only fix is never re-validated
and a re-run replays the stale text. stdd doctor flags workflows using
that form without an edited trigger.
Commands
| Command | What it does |
| --- | --- |
| stdd init [dir] [--tools claude,codex] [--ci github] [--hooks] | Install .stdd/ and compile playbooks per agent; --ci github writes the canonical workflow; --hooks writes a user-owned pre-push hook running stdd check |
| stdd doctor [dir] [--readiness] | Adoption health report: setup, canonical docs, misleading artifacts, drift, worktree readiness — exits 1 on findings; --readiness runs only the config-declared readiness checks |
| stdd check [dir] | CI guard: no committed working artifacts, no temporal narrative in canonical docs, no stale or hand-edited generated files |
| stdd evidence --base <ref> | Draft the evidence line from the actual diff: prints a finished Docs updated first: line when canonical docs changed; otherwise the remaining sentinel templates go to stderr and it exits nonzero |
| stdd check-pr <file\|-> [--base <ref>] [--pr <n\|.>] | CI guard: PR body carries exactly one non-empty docs evidence line; with --base, claimed doc paths are verified against the actual git diff; --pr fetches and validates the live PR body against its own base and head |
| stdd status [--json] | Next-step oracle: where in the loop this checkout is (git diff, ledger, forge) and the concrete next step |
| stdd docs <decision> [paths…] [--reason <why>] | Record the docs decision (updated-first, checked, not-applicable) in the session ledger when it is made |
| stdd red -- <cmd> / stdd verify -- <cmd> | Run the command, record {cmd, exit, excerpt} in the ledger, pass the exit code through; red asserts genuine-red via the config's redPattern |
| stdd note <text> | Record free-form handoff context in the ledger |
| stdd slice new --frozen <globs> --allowed <globs> | Declare a delegated slice's scope and snapshot the checkout baseline (head + dirty-file hashes) into the ledger |
| stdd scope | Postflight check against the slice baseline: session-introduced changes to frozen paths or outside allowed paths fail; inherited dirt is reported separately, never blamed |
All checks are configured in .stdd/config.json (glob patterns for
forbidden artifacts, canonical docs, temporal phrases; an optional
readiness.required list of {path, hint} entries declares what a fresh
worktree needs before verification output can be trusted; an optional
redPattern regex teaches stdd red what a genuine test failure looks
like).
The ledger (.stdd/ledger.jsonl) is a per-checkout working artifact —
append-only, gitignored, never committed. It is advisory input, never a
gate: check and check-pr behave exactly as without it, while status
and evidence derive loop state from it instead of reconstructing it from
conversation memory. See "The session ledger and stdd status" in the
method.
What's in the box
| Path | Contents |
| --- | --- |
| method/ | The STDD contract: the loop, the rules, the exceptions |
| playbooks/ | Agent-neutral playbooks: brainstorming, planning, debugging, investigation, worktrees, pr-green, delegate-slice |
| templates/ | PR description and deferred-design templates |
| adapters/ | How playbooks compile per agent |
| cli/ | Zero-dependency Node CLI |
The method in five rules
- Classify first. Behavior changes (anything observable) pass the full loop; implementation-only changes skip the docs step.
- The docs edit is the spec. Missing or stale docs are updated before tests and code, as the first reviewable unit of the change.
- Red before green. A failing test gates every behavior change — except frontend visual work, which is design-first: build, review screenshots, then test only real behavior contracts.
- Working artifacts are never committed. Rationale → PR description; history → git; deferred designs → dated project-log entries.
- Evidence, not claims. Every PR states
Docs updated first:/Docs checked, no change needed:/Docs not applicable:— naming the docs or the reason. CI rejects a missing, duplicated, or bare label, and with--baseverifies the claimed doc paths against the actual diff.
The full contract: method/README.md.
How it compares
vs. OpenSpec — OpenSpec models changes as committed folders that archive into the repo; specs accumulate alongside a separate docs reality. stdd keeps one truth (your docs tree), borrows OpenSpec's best mechanics (delta discipline, drift detection, init/update UX), and rejects the archive.
vs. Superpowers — great process content, but Claude-only and delivered through plugin hooks. stdd rewrites the process agent-neutrally and compiles it for every agent your team runs.
vs. nothing — chat context evaporates; agents build before agreeing on scope; stale plans mislead the next session. stdd fixes the workflow, not the model.
Development
npm ci
npm test # node:test — unit + CLI integration
npm run check # Biome (Rust) — lint + format, CI mode
npm run format # Biome — write fixes
npm run selfcheck # stdd check on this repo (dogfooding)This repository follows its own method: PRs carry a docs evidence line
(enforced by CI via stdd check-pr), and no working artifacts are committed.
