Problem
Today the adopt flow commits the agent-guard PreToolUse hook wiring into the
tracked .claude/settings.json, while the script it invokes
(.claude/hooks/agent-guard.py) stays gitignored and is only synced in by
/magpie-setup. That split has caused a chain of follow-up fixes:
Both are workarounds for the same root cause: we commit machine-specific hook
wiring for a script we deliberately don't commit. It also enlarges the
always-committed footprint for an opt-in feature.
Proposal
Have /magpie-setup write the hook entry into the gitignored, per-machine
.claude/settings.local.json at the same moment it deposits
agent-guard.py — and stop committing it in .claude/settings.json.
Because the wiring then only ever exists once the script is present:
.claude/settings.local.json is Claude Code's documented per-user override
file and is already the natural home for machine-local settings; adopters
gitignore it (as this repo's .gitignore already does for settings.local).
Scope of the framework change
skills/setup/adopt.md — Step 12 (and the verify step): write/merge the
hook block into .claude/settings.local.json instead of surfacing a
settings.json snippet for the maintainer to commit; drop the -f/guard.
.gitignore template — ensure /.claude/settings.local.json is ignored.
tools/agent-guard/README.md — document the settings.local.json wiring
and the plain command.
- unadopt/verify flows — read/clean
settings.local.json.
- Idempotent merge: don't clobber an adopter's existing
settings.local.json keys.
Context
Raised from Apache JMeter's adoption review. The JMeter PMC reviewer (@vlsi)
independently asked "Can magpie add the hook entry to
.claude/settings.local.json on setup?" — this issue is that request,
generalised. JMeter has already dropped the committed .claude/settings.json
from its adoption PR in anticipation.
If accepted, this supersedes #823 (and retro-justifies removing the #786
guard).
Problem
Today the adopt flow commits the agent-guard PreToolUse hook wiring into the
tracked
.claude/settings.json, while the script it invokes(
.claude/hooks/agent-guard.py) stays gitignored and is only synced in by/magpie-setup. That split has caused a chain of follow-up fixes:the hook command had to be guarded (
[ -f … ] && python3 … || true) tono-op when the script is absent.
(PowerShell, no Git Bash), so it had to be rewritten as an inline
python3 -cexistence check.Both are workarounds for the same root cause: we commit machine-specific hook
wiring for a script we deliberately don't commit. It also enlarges the
always-committed footprint for an opt-in feature.
Proposal
Have
/magpie-setupwrite the hook entry into the gitignored, per-machine.claude/settings.local.jsonat the same moment it depositsagent-guard.py— and stop committing it in.claude/settings.json.Because the wiring then only ever exists once the script is present:
plain
python3 "$CLAUDE_PROJECT_DIR/.claude/hooks/agent-guard.py".python3 "…"runs on POSIX and Windows alike.repo, matching the fact that both the script and its trigger are
per-machine framework artefacts, not project config.
.claude/settings.local.jsonis Claude Code's documented per-user overridefile and is already the natural home for machine-local settings; adopters
gitignore it (as this repo's
.gitignorealready does forsettings.local).Scope of the framework change
skills/setup/adopt.md— Step 12 (and the verify step): write/merge thehook block into
.claude/settings.local.jsoninstead of surfacing asettings.jsonsnippet for the maintainer to commit; drop the-f/guard..gitignoretemplate — ensure/.claude/settings.local.jsonis ignored.tools/agent-guard/README.md— document thesettings.local.jsonwiringand the plain command.
settings.local.json.settings.local.jsonkeys.Context
Raised from Apache JMeter's adoption review. The JMeter PMC reviewer (@vlsi)
independently asked "Can magpie add the hook entry to
.claude/settings.local.jsonon setup?" — this issue is that request,generalised. JMeter has already dropped the committed
.claude/settings.jsonfrom its adoption PR in anticipation.
If accepted, this supersedes #823 (and retro-justifies removing the #786
guard).