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, setcore.hooksPath. Done. - Existing
.githooks/pre-commitis byte-identical to the template. No-op. Tell the user it's already installed. - Existing
.githooks/pre-commitdiffers. 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 thenexecs 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 hooknear 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-saveddoes not exist). Remove.githooks/pre-commit. If.githooks/is now empty, remove it too. Rungit config --unset core.hooksPath. - Saved original exists. Restore: rename
.githooks/pre-commit.adr-kit-saved→.githooks/pre-commit. Leavecore.hooksPathset to.githooks. .githooks/pre-commit.backup-<timestamp>exists (from a priorreplaceinstall). Tell the user the backup is there and ask whether to restore that instead. Apply choice.
- No saved original (
- 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
SessionStarthook in its manifest. It auto-registers in every Claude Code session when the plugin is enabled globally, and self-guards (exits silently when nodocs/adr/with ADRs is present). This is the recommended path. - Project-scoped (explicit): Adds the guardian's
SessionStartentry 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.SessionStartalready contains an entry whosecommandcontains the stringadr-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
SessionStarthooks (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[*]whosecommandcontainsadr-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
hooksarray becomes empty after removal, remove the empty array (but keep the parentSessionStartentry 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.hooksPathonly 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 +xthe 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.