| name | install-hooks |
| description | Install or uninstall the adr-kit pre-commit hook in the current project. Copies templates/githooks/pre-commit into .githooks/pre-commit, makes it executable, and runs `git config core.hooksPath .githooks`. Idempotent. Used internally by /adr-kit:init and /adr-kit:upgrade; also exposed standalone for users who want to add or remove the hook independently. |
| argument-hint | [--uninstall] |
| disable-model-invocation | true |
| allowed-tools | ["Read","Write","Edit","Bash"] |
adr-kit install-hooks
You install or uninstall the adr-kit pre-commit hook in the project the user is currently in (pwd should be the project root).
Default behaviour: install
Step 1 — Resolve the plugin's hook template
ADR_KIT=$(ls -d ~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/*/ | sort -V | tail -1)
TEMPLATE="$ADR_KIT/templates/githooks/pre-commit"
If $TEMPLATE does not exist or is empty, exit with an error: the plugin install is broken; tell the user to reinstall via /plugin install adr-kit@rvdbreemen-adr-kit.
Step 2 — Detect existing hooks
Three cases:
- No
.githooks/pre-commit. Mkdir .githooks/, copy the template, chmod +x, set core.hooksPath. Done.
- Existing
.githooks/pre-commit is byte-identical to the template. No-op. Tell the user it's already installed.
- Existing
.githooks/pre-commit differs. Read both. Show the user the existing content (truncated to first 30 lines). Ask:
prepend adr-kit before existing — write a wrapper script that calls adr-kit then execs the original. Save the original as .githooks/pre-commit.adr-kit-saved.
replace — overwrite. Save the old hook as .githooks/pre-commit.backup-<timestamp> for safety.
abort — do nothing.
Apply the user's choice.
Step 3 — Activate
Run git config core.hooksPath .githooks. Confirm with git config --get core.hooksPath. If the project already has core.hooksPath set to a different directory, do NOT overwrite — tell the user, ask whether to change the config or to symlink the hook into the existing hooks dir.
Step 4 — Confirm
Print:
Pre-commit ADR judge installed.
- hook: .githooks/pre-commit (mode 755)
- core.hooksPath: .githooks
- disable a single commit: ADR_KIT_HOOK_DISABLE=1 git commit ...
- remove permanently: /adr-kit:install-hooks --uninstall
- LLM pass is opt-in (off by default). Enable per-project: judge.llm_enabled:true in docs/adr/.adr-kit.json. Enable one commit: ADR_KIT_LLM=1 git commit ...
- ADR-suggest is opt-in too: suggest.enabled:true, or ADR_KIT_SUGGEST=1 for one commit.
Uninstall behaviour: --uninstall
When invoked as /adr-kit:install-hooks --uninstall:
- Read
.githooks/pre-commit. Verify it is the adr-kit hook (look for the line # adr-kit pre-commit hook near the top). If it is NOT the adr-kit hook, refuse — tell the user the hook in place isn't ours and we won't touch it.
- Three paths:
- No saved original (
.githooks/pre-commit.adr-kit-saved does not exist). Remove .githooks/pre-commit. If .githooks/ is now empty, remove it too. Run git config --unset core.hooksPath.
- Saved original exists. Restore: rename
.githooks/pre-commit.adr-kit-saved → .githooks/pre-commit. Leave core.hooksPath set to .githooks.
.githooks/pre-commit.backup-<timestamp> exists (from a prior replace install). Tell the user the backup is there and ask whether to restore that instead. Apply choice.
- Confirm with one line:
Pre-commit ADR judge uninstalled. (restored prior hook | removed .githooks/ | unset core.hooksPath)
Guardian (SessionStart) hook — project-scoped install
The ADR Guardian has two registration paths (spec §7):
- Plugin-level (default, frictionless): The adr-kit plugin declares a
SessionStart hook in its manifest. It auto-registers in every Claude Code session when the plugin is enabled globally, and self-guards (exits silently when no docs/adr/ with ADRs is present). This is the recommended path.
- Project-scoped (explicit): Adds the guardian's
SessionStart entry directly to the project's .claude/settings.json. Contained to this project only. Use when the user wants explicit per-project control or does not enable the plugin globally.
Adding the project-scoped guardian hook
When the user asks to add the guardian hook to the project's .claude/settings.json:
- Read
.claude/settings.json (create it as {} if missing).
- Check if
hooks.SessionStart already contains an entry whose command contains the string adr-guardian. If yes: print "Already installed" and stop.
- If no SessionStart array exists: create it. Parse the JSON structurally — never do text substitution on a JSON file.
- Append the following hook object to
hooks.SessionStart[0].hooks (create the array path if absent):
{
"type": "command",
"command": "ADR_KIT=$(ls -d ~/.claude/plugins/cache/rvdbreemen-adr-kit/adr-kit/*/ 2>/dev/null | sort -V | tail -1) && [ -n \"$ADR_KIT\" ] && _PY=$(command -v python3 || command -v python || command -v py) && [ -n \"$_PY\" ] && \"$_PY\" \"$ADR_KIT/bin/adr-guardian\" check 2>/dev/null || true",
"timeout": 10,
"statusMessage": "Checking ADR health..."
}
- Write the updated JSON back. The existing
SessionStart hooks (if any) are preserved byte-for-byte — only the new entry is appended.
- Confirm: "Guardian hook added to .claude/settings.json. It will fire at the next session start."
Removing the project-scoped guardian hook
When the user asks to remove the guardian hook from .claude/settings.json:
- Read
.claude/settings.json. Parse as JSON.
- Find entries in
hooks.SessionStart[*].hooks[*] whose command contains adr-guardian. If none found: print "Guardian hook not found in .claude/settings.json" and stop.
- Remove exactly those entries. Leave all sibling hooks untouched.
- If the containing
hooks array becomes empty after removal, remove the empty array (but keep the parent SessionStart entry if other entries exist in other groups).
- Write the updated JSON back.
- Confirm: "Guardian hook removed from .claude/settings.json."
Safety invariant: Never remove or modify any hook entry that does not contain adr-guardian in its command. This is the cozempic lesson — a remove that also clobbers unrelated hooks is worse than not removing at all.
Adding .adr-kit-state.json to .gitignore
After adding the guardian hook (project-scoped or as part of /adr-kit:init):
echo "docs/adr/.adr-kit-state.json" >> .gitignore
Verify it is not already present first (idempotent). The state file is per-machine and must never be committed.
Constraints
- Never silently overwrite a pre-existing user hook. Always detect, show, ask.
- Set
core.hooksPath only when needed. If the user already has it pointing at a custom dir, propose a symlink rather than redirecting their config.
- Idempotent in both directions. Re-running install when already installed is a no-op. Re-running uninstall when nothing is installed is a no-op (with a warning).
chmod +x the hook after writing — without the executable bit, git silently ignores it.
- Do not commit the hook. The hook lives in
.githooks/, which the project may or may not have in .gitignore. Don't add it to git here; the user decides whether to track it.