| name | init |
| description | Initialize ADR Kit in a project. Use for adr-kit init, first-time ADR setup, architecture audit, managed guidance, and the pre-commit gate. |
| argument-hint | [no arguments] |
| disable-model-invocation | true |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep","Task"] |
adr-kit init
This is the adopt mode of /adr-kit:setup (spec R19: one entry point,
four modes). Invoked directly or via /adr-kit:setup adopt, the procedure
is identical -- this file owns it.
$ARGUMENTS is reserved for future init options and should normally be empty.
Reject unknown arguments instead of guessing.
You are running the one-shot project bootstrap for adr-kit. Your job is to take a project that has either no ADRs or only legacy-shaped ADRs and:
- Write the project's instruction layout with
scripts/setup-project.py, which owns every file in it.
New ADRs use MADR unless docs/adr/.adr-kit.json selects
template.profile: "nygard" or "canonical".
- Discover decision-shaped artefacts in the source and documentation, create a starter work queue of Proposed ADRs, and confirm each decision independently.
- Install the pre-commit hook so future commits are guarded against ADR drift.
- Lint everything to confirm the resulting ADR set passes the four verification gates.
This is a deep, one-shot operation. Do not skip steps. Do interact with the user — batched approval beats silent autopilot every time.
Step 0 — Verify Python 3
adr-kit requires Python 3.10 or later. Before doing anything else, verify it is available.
Detection
Run these checks in order:
python3 --version 2>/dev/null || python --version 2>/dev/null || py --version 2>/dev/null
Parse the output. Acceptable: any Python 3.10+ version string. Not acceptable: Python 2.x, Python 3.9 or earlier, command not found, or no output.
If Python 3 is found: print [adr-kit] Python OK: <version> and continue to Step 1.
If Python 3 is NOT found: offer installation. Detect the platform:
uname -s 2>/dev/null || echo "windows"
Then present platform-specific installation guidance:
macOS — Python not found
brew install python3
xcode-select --install
Ask the user: Run 'brew install python3' now? (Y/n)
- If yes: run
brew install python3 via Bash. After it completes, verify again with python3 --version. If successful: confirm and continue.
- If no: print the install instructions and tell the user to re-run
/adr-kit:init after installing.
Linux (Debian/Ubuntu)
sudo apt-get update && sudo apt-get install -y python3 python3-pip
Ask: Run this command now? (Y/n)
- If yes: run it. Verify after. Continue if successful.
Linux (Red Hat/Fedora/CentOS)
sudo dnf install python3
Linux (Arch)
sudo pacman -S python
For generic Linux, detect the distro from /etc/os-release and present the matching command. If uncertain, present all three and ask the user to choose.
Windows — Python not found
Present these options in order:
Option A (recommended): Windows Package Manager (winget)
winget install Python.Python.3.12
Ask: Run this in a terminal now? (Y/n) — if yes, run it via PowerShell.
Option B: Microsoft Store
Open: ms-windows-store://pdp/?ProductId=9NCVDN91XZQP
(or search "Python 3" in the Microsoft Store)
Option C: python.org installer
Download from: https://www.python.org/downloads/windows/
Instruct: Check "Add Python to PATH" during installation.
After any Windows installation, verify:
python --version
IMPORTANT for Windows users: After installation, a new terminal session may be needed to pick up the updated PATH. Tell the user to close and reopen the terminal, then re-run /adr-kit:init.
All platforms — After successful installation
- Verify the installed version:
python3 --version (or python --version on Windows).
- Confirm the version is 3.10+. If it is 3.9 or earlier, warn:
[adr-kit] WARN: Python 3.9 or earlier detected. adr-kit requires 3.10+. Some features will not work.
- Print:
[adr-kit] Python 3.x.y installed. Continuing setup...
- Continue to Step 1.
If the user declines installation and Python is unavailable: print the error below and stop.
[adr-kit] ERROR: Python 3 is required but not installed.
Install Python 3.10+ from https://www.python.org/downloads/ and re-run /adr-kit:init.
Step 1 — Project hookup
1a. Write the instruction layout
Do not hand-write the instruction block or the guide. scripts/setup-project.py
owns the layout and is the only thing that knows all of it: CLAUDE.md,
AGENTS.md, .github/copilot-instructions.md and .adr-kit/ADR-guide.md.
Describing an older layout in prose here is how the three clients drifted apart
-- the Codex and Copilot init skills delegate to this command while this file
still wrote a guide under .claude/ and a stub block the same repository
classifies as legacy.
Preview, then apply:
python3 "$ADR_KIT/scripts/setup-project.py" --client claude-code-cli --project-root . --dry-run
python3 "$ADR_KIT/scripts/setup-project.py" --client claude-code-cli --project-root .
Two properties the command guarantees, and this skill must not undermine:
- Idempotent. Re-running on a current project changes nothing.
- Bounded. It writes only inside its managed markers, so user content around
them stays byte-exact, and it never touches
.adr-kit/ADR-guide.local.md.
A v0.11 footprint stops this step. If CLAUDE.md carries an inline
## ADR Kit Rules section, do not run the writer. Tell the user to run
/adr-kit:upgrade, which performs that migration explicitly. Silently rewriting
a v0.11 footprint is the one thing setup may never do.
Relay the command's per-change output, and say plainly when there was nothing to
do -- a no-op is the expected result of a second run.
Step 1b — The signer: propose, never assume
Do this before Step 3, because that is where bin/adr new first runs and it
refuses to write an unsigned Status History entry. Discovering that refusal
halfway through a batch of reconstructed ADRs is the worst moment to meet it.
python3 "$ADR_KIT/bin/adr" signer --suggest --adr-dir docs/adr
Read-only: it finds candidates and writes nothing. It looks at the signed-in
GitHub account (gh api user, when the CLI is available) and at
git config user.name, ranks them, and shows each with its source — a proposal
the user cannot trace is one they cannot judge, and this value lands in an
immutable history.
- Candidates found — show them and ask which to adopt, or let the user type a
different name. Then write it:
python3 "$ADR_KIT/bin/adr" signer --set "User: <chosen>".
- Already configured — say so and move on. Do not overwrite it.
- Nothing found — ask for the name outright rather than guessing.
Bot and CI identities are deliberately not offered. github-actions[bot],
runner, a bare user: those are configured values that name a machine, and R8
asks for evidence of which human accepted a decision.
The value is machine-local by design (docs/adr/.adr-kit.local.json, gitignored)
because writing one person's name into the tracked config would sign every
teammate's acceptances.
Step 2 — Candidate discovery
Run bin/adr-discover to scan the project. Use --output to drop the result next to the existing ADRs:
$ADR_KIT/bin/adr-discover --root . --output docs/adr/.adr-kit-init-candidates.json
($ADR_KIT is the plugin path resolved in step 1a.)
The output is a JSON file with:
tooling-* candidates (build system, CI, runtime markers)
deps-* candidates (one per dependency manifest found)
doc-quote-* candidates (one per documentation file containing decision-narrative phrases, with up to 5 example snippets)
history-* candidates from the git log: commit subjects that announce a
change of direction, the files rewritten far more often than the rest, and
the order in which tooling and dependencies arrived.
Every candidate carries source, and the difference matters. source: "tree" is a fact about a file that exists. source: "history" is a claim
someone typed once, in a hurry, possibly about a decision that was reversed
three commits later — those candidates carry a why_this_is_weak note saying
so. Never write an ADR from a history candidate without confirming it against
the code as it stands now. Do surface them: the why of an existing codebase
lives in its history, which is exactly what a working-tree scan cannot see.
history.available: false means the scan could not read a history (no git, no
commits, not a repository). Say so plainly rather than presenting a tree-only
scan as complete.
Open the file. Tell the user the headline counts: <N total candidates: <X> tooling, <Y> dependency, <Z> documented, <H> from history>.
Step 3 — Deep LLM curation
Read every candidate's evidence file(s) (or relevant section), plus the project's README.md, root AGENTS.md/CLAUDE.md, and the existing docs/adr/ADR-*.md files. Your job: decide for each candidate whether it warrants an ADR and dedupe against ADRs that already exist.
For each candidate, classify:
keep — material architectural decision; needs an ADR.
merge with ADR-NNN — already documented in an existing ADR; no new file needed.
drop — not architecturally significant (e.g., a casual doc phrase that didn't actually drive a decision).
Process candidates in batches of 5–10. For each batch:
-
Print a numbered list with: candidate id, your proposed classification, your reasoning (one sentence), and (for keep) the proposed ADR title and decision_type.
-
Ask the user: Approve all [yes] | reject specific (list ids) | drop classification suggestion (id → drop) | refine title/type (id → ...).
-
Apply the user's choices. For every keep candidate, create the record with
the lifecycle command, named explicitly:
$ADR_KIT/bin/adr new "<Title>" --adr-dir docs/adr
Do not write the file with the Write tool. bin/adr new allocates the next
number, honours template.profile, writes Status: Proposed, <today>, and
regenerates all three indexes inside one transaction. A hand-written file
gets none of that, and the index it leaves behind is stale from the moment
it lands.
Include an ## Enforcement block when the rule is declarative,
llm_judge: true when it is not, or omit the section when no code surface
exists.
When the decision is already shipped, record that. An init scan is
reconstructing decisions the code already implements, so set
documents_shipped: true and list the concrete evidence in verified_in
(the candidate's evidence_files are exactly that). Use:
$ADR_KIT/bin/adr document ADR-NNN --verified-in <path> [--verified-in <path>]
This is not a shortcut to acceptance; it is the honest metadata, and it is
what makes step 3b's supported path available at all.
-
Explain and select grill depth per candidate:
- compact confirmation only when chosen decision, rationale,
alternatives, and consequences each have direct cited evidence;
- deep grill when any of those fields, ownership, or conflict resolution
is missing or inferred.
Resolve duplicates and conflicts by merging, linking, rejecting, or
escalating before acceptance.
-
Confirm each ADR separately. A batch response may select candidates but
cannot accept multiple decisions. On interruption, retain valid Proposed
records, Open Questions, a consistent index, and one
/adr-kit:grill ADR-NNN resume command per unfinished record.
Do not race ahead. Wait for the user's response per batch.
Step 3b — Acceptance, and what "never silently accept" actually forbids
Reconstructing a decision the code already implements is documentation, not
authorisation: nobody is being asked to approve a new direction, they are being
asked to confirm that the record matches what already ships. That is why the
kit has an auto-accept path at all, and init is what it exists for.
What "never silently accept" forbids is the silently, not the accepting. So:
-
Show the user the batch of records and what each one documents.
-
Ask once, explicitly, for that batch:
Accept these <N> as documentation of shipped behaviour? [yes / grill first / list ids to hold back]
-
On yes, accept each through the supported path:
$ADR_KIT/bin/adr accept ADR-NNN --auto --auto-mode auto --repo-root .
--auto is not a bypass. It re-runs every acceptance gate and additionally
demands documents_shipped: true, at least one verified_in pointer, no
unresolved Open Questions, and a quality score above the configured
threshold. A record that fails any of those refuses to accept and tells you
which one. That refusal is the point: it is the difference between "the user
approved this batch" and "the record is good enough to stand".
-
Anything held back, or anything --auto refused, stays Proposed and gets
/adr-kit:grill ADR-NNN.
Never accept without step 2's question, and never accept a record the user has
not seen. Batch approval selects which records; each still passes its own
gates on its own.
When all candidates are processed, summarise: <created N new ADRs, accepted A as shipped documentation, held B for grilling, merged M, dropped K>.
Step 3c — Verify the index rather than assuming it
Every lifecycle command regenerates the indexes in its own transaction, so
after a clean run this passes. Run it anyway, because "should have" is not a
guarantee and a stale index misinforms every later reader and every agent:
$ADR_KIT/bin/adr-index docs/adr --check
Exit 0 means the three generated artefacts match the ADRs. Init does not
finish while this is non-zero. On failure, run $ADR_KIT/bin/adr-index docs/adr to regenerate, then re-run the check and say what was stale — a
record written outside the lifecycle command is the usual cause, and the user
should know it happened.
Step 4 — Hook installation
Run the install-hooks skill (or do its work inline if delegating is awkward):
- Resolve the plugin's
templates/githooks/pre-commit path (same plugin path as step 1a).
- Read it. Write to project
.githooks/pre-commit. Make it executable (chmod +x .githooks/pre-commit).
- If the project already has a
.githooks/pre-commit, do NOT overwrite. Read both. Tell the user the existing hook content. Ask: prepend adr-kit check before existing hook | replace | abort install. On prepend, write a wrapper that runs adr-kit then exec's the original (saved as .githooks/pre-commit.adr-kit-saved).
- Run
git config core.hooksPath .githooks once. Confirm with git config --get core.hooksPath.
4a — Choose the judge backend (ADR-017)
The declarative Enforcement gate is always-on, deterministic and free. The LLM pass is on by default and needs one decision from the user: which model reviews their commits.
Print this notice first, verbatim. Do not shorten the cost paragraph and do not name a specific model — no model is pinned any more.
[adr-kit] The pre-commit judge runs two passes.
Declarative gate always on, regex and globs, no model, no cost.
LLM pass on by default, for ADRs you mark `llm_judge: true`.
What the LLM pass costs: ONE model call per llm_judge ADR, on every commit that
touches its scope, every time. The calls are deliberately isolated from each
other -- a shared prompt let one ADR's text flip another ADR's verdict -- so the
cost is LINEAR in the number of opted-in ADRs and is not amortised across them.
No ADR carries `llm_judge: true` until an author adds it, so a fresh project
pays nothing until someone opts a decision in.
Where that call goes: the CLI of the agent you already use (ADR-036 reduced the
judge to this host backend; an operator can still point one run elsewhere with
`ADR_KIT_LLM_CMD` or `--llm-cmd`, which is an environment fact and never
repository configuration). No model flag is passed, so your own configured
model answers, and no extra credential is needed beyond the agent you are
already signed in to. An unavailable backend degrades to declarative-only and
never blocks a commit.
Then ask: LLM pass? [1] on (host CLI) [2] off (1)
Apply the answer with the kit's own command — do not hand-write the JSON, because the command validates what it wrote and refuses an incomplete choice. Resolve the plugin path first if it is not already in scope from step 1a:
ADR_KIT=$(ls -d ~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/*/ | sort -V | tail -1)
Ignore the local file in version control (idempotent):
grep -q "\.adr-kit\.local\.json" .gitignore 2>/dev/null || echo "docs/adr/.adr-kit.local.json" >> .gitignore
Then ask one more question (default No): Enable per-commit ADR-suggest nudges (a second model call per commit, advisory only)? (y/N) and merge {"suggest": {"enabled": <true|false>}}.
Confirm by showing the resolved configuration rather than by asserting it:
"$ADR_KIT/bin/adr-judge" --adr-dir docs/adr --show-config
Print the final one-liner: Pre-commit ADR judge installed (declarative gate always-on; LLM pass on <backend>). Disable a single commit with ADR_KIT_HOOK_DISABLE=1 git commit ...; skip the LLM pass for one commit with ADR_KIT_NO_LLM=1 git commit ...; change the backend later with /adr-kit:judge settings; remove permanently with /adr-kit:install-hooks --uninstall.
Step 5 — Final lint
Run the kit's lint binary on the freshly populated ADR directory:
$ADR_KIT/bin/adr-lint docs/adr/
Report the result: <N PASS, M ADVISORY, K FAIL>. If any FAIL: list them with the gate name and ask the user whether to fix now (re-invoke adr-generator for the offending ADR) or defer to a follow-up.
Step 5b — Guardian setup (new in v0.18.0)
After the hook and LLM opt-in are configured, offer the ADR Guardian.
Print this notice:
[adr-guardian] The ADR Guardian is a SessionStart staleness detector (v0.18.0).
It checks ADR health at session start and nudges when a sweep is due:
- cheap tier (drift + retire + lint): daily, free
- LLM tier (suggest + audit): bi-weekly, asks before spending (~$0.10–0.30)
Two registration paths:
A. Plugin-level (default, recommended): auto-registers when the adr-kit plugin is enabled.
The guardian self-guards (no-ops silently in non-ADR projects).
B. Project-scoped: adds a SessionStart entry to .claude/settings.json (explicit, contained).
Ask two questions (default A/No):
Enable guardian? (Y/n) — Write {"guardian": {"enabled": true}} into docs/adr/.adr-kit.json (merge if present). If No: write {"guardian": {"enabled": false}} and skip the rest of this step.
Register project-scoped SessionStart hook in .claude/settings.json? (y/N) — If yes, follow the instructions in skills/install-hooks/SKILL.md under "Adding the project-scoped guardian hook". If no, remind the user the plugin-level hook is used by default.
Add .adr-kit-state.json to the project's .gitignore (idempotent):
grep -q "\.adr-kit-state\.json" .gitignore 2>/dev/null || echo "docs/adr/.adr-kit-state.json" >> .gitignore
Confirm: [adr-guardian] enabled (<path>); state file gitignored.
Step 6 — Generate standalone validation scripts (optional)
Ask the user: "Generate standalone validation scripts for CI/CD pipelines? (Y/n)"
If yes:
ADR_KIT=$(ls -d ~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/*/ | sort -V | tail -1)
"$ADR_KIT/bin/adr-generate-scripts" --lang shell --output .generated/
This generates shell scripts in .generated/<ADR-NNN>/validate.sh that validate code against ADR Enforcement rules without requiring adr-kit. Useful for CI/CD pipelines that run outside Claude Code.
If the user says no (or presses Enter on the default), skip silently. If .generated/ already contains scripts from a previous run, the generator overwrites them — this is idempotent.
Wrap-up
Print a final summary in this exact shape:
adr-kit init complete:
- layout: <files setup-project.py wrote> (created | refreshed | unchanged)
- audit: <N> candidates → <X> kept, <Y> merged, <Z> dropped
- ADRs: <N> created, <M> already present
- hook: installed (or already present + reason)
- llm: backend <host|off>, suggest <enabled|disabled>
- guardian: <enabled|disabled> (plugin-level | project-scoped | disabled)
- lint: <P> PASS, <A> ADVISORY, <F> FAIL
- scripts: generated | skipped (user declined)
Suggest a first commit staging exactly what was written: git add docs/adr/ .adr-kit/ CLAUDE.md AGENTS.md .githooks/pre-commit && git commit -m "chore(adr-kit): bootstrap". Do not run the commit yourself; let the user inspect first.
Constraints
- Do not skip steps. Each step has an idempotency story; running them in order produces a consistent project state.
- Batch selection, individual gates. Do not silently mass-generate or
accept ADRs. Five to ten candidates may be triaged per round; the user
approves a batch, and every record still passes its own acceptance gates on
its own through
bin/adr accept --auto. A record the user has not seen is
never accepted, and a record that fails a gate is never accepted regardless
of what the batch answer was.
- Reconstruction is documentation, not authorisation. Init records
decisions the code already implements, which is why
--auto exists and why
it demands documents_shipped: true and a verified_in pointer before it
will run. A decision that is not already shipped is a new decision: it goes
through /adr-kit:grill and ordinary acceptance, never --auto.
- Resolve the plugin path dynamically. The kit lives at
~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/<version>/. Use the ls | sort -V | tail -1 resolver above; do not hardcode a version.
- Preserve user CLAUDE.md content. Only the marked stub block (or a v0.11-style
## ADR Kit Rules section being replaced) may be modified. Everything else stays byte-exact.
- Re-runnable. A second invocation on a project where init already succeeded should detect the existing footprint and either skip or refresh, not regenerate.