Summary
sqz init is not idempotent. Re-running it (e.g. reinstall + re-init) appends a fresh set of hook entries to the Claude Code settings file (~/.claude/settings.json with --global) instead of detecting sqz's existing hooks and replacing/skipping them. Each run stacks another copy, so the hooks fire multiple times per event — hook claude runs N times on every Bash tool call, resume N times on SessionStart, hook precompact N times on PreCompact.
Observed
Across repeated init/reinstall cycles on Windows, the entries in ~/.claude/settings.json accumulated 1 → 2 → 3 identical copies of each hook (PreCompact, PreToolUse with "matcher": "Bash", and SessionStart with "matcher": "compact"). Example after several runs (executable path abbreviated):
"PreToolUse": [
{ "hooks": [ { "command": ".../sqz/bin/sqz.exe hook claude", "type": "command" } ], "matcher": "Bash" },
{ "hooks": [ { "command": ".../sqz/bin/sqz.exe hook claude", "type": "command" } ], "matcher": "Bash" },
{ "hooks": [ { "command": ".../sqz/bin/sqz.exe hook claude", "type": "command" } ], "matcher": "Bash" }
]
The same triplication appeared under PreCompact and SessionStart. The only way to get back to a single copy was to hand-edit the file.
Impact
Expected
sqz init should be idempotent. It should detect its own existing hook entries (match on the command being the sqz executable + subcommand) and replace or skip them, so re-running init leaves exactly one entry per hook event regardless of how many times it runs.
Possibly related — uninstall may not remove the hooks (needs confirmation)
After running sqz uninstall, the sqz hook entries appeared to still be present in ~/.claude/settings.json (and duplicated). I'm inferring this from the resulting file state rather than having watched uninstall execute, so please verify. If confirmed, uninstall should remove all sqz-owned hook entries from the settings file using the same matching logic.
Environment
- OS: Windows 11
- Claude Code global settings:
~/.claude/settings.json
- sqz: 1.0.9
- Trigger: running
sqz init --global more than once (commonly via reinstall + re-init)
Suggested fix
Key sqz's hook entries by a stable identifier (the sqz executable path and/or subcommand) and upsert rather than append: before adding the current hook for an event, remove any existing sqz-owned entry for that event. Apply the same matching in uninstall to strip them. This makes both init and uninstall safe to run repeatedly.
Relationship to #20
Distinct bug, same general area (sqz's read/modify/write of settings.json). #20 is about the path format of the written command (backslash path fails under Git Bash). This issue is about duplicate accumulation / idempotency of the write itself. They can be fixed independently.
Summary
sqz initis not idempotent. Re-running it (e.g. reinstall + re-init) appends a fresh set of hook entries to the Claude Code settings file (~/.claude/settings.jsonwith--global) instead of detecting sqz's existing hooks and replacing/skipping them. Each run stacks another copy, so the hooks fire multiple times per event —hook clauderuns N times on every Bash tool call,resumeN times onSessionStart,hook precompactN times onPreCompact.Observed
Across repeated init/reinstall cycles on Windows, the entries in
~/.claude/settings.jsonaccumulated 1 → 2 → 3 identical copies of each hook (PreCompact,PreToolUsewith"matcher": "Bash", andSessionStartwith"matcher": "compact"). Example after several runs (executable path abbreviated):The same triplication appeared under
PreCompactandSessionStart. The only way to get back to a single copy was to hand-edit the file.Impact
resume, redundantprecompact— wasted work and confusing behaviour.command not foundline appears once per copy).Expected
sqz initshould be idempotent. It should detect its own existing hook entries (match on the command being the sqz executable + subcommand) and replace or skip them, so re-runninginitleaves exactly one entry per hook event regardless of how many times it runs.Possibly related —
uninstallmay not remove the hooks (needs confirmation)After running
sqz uninstall, the sqz hook entries appeared to still be present in~/.claude/settings.json(and duplicated). I'm inferring this from the resulting file state rather than having watcheduninstallexecute, so please verify. If confirmed,uninstallshould remove all sqz-owned hook entries from the settings file using the same matching logic.Environment
~/.claude/settings.jsonsqz init --globalmore than once (commonly via reinstall + re-init)Suggested fix
Key sqz's hook entries by a stable identifier (the sqz executable path and/or subcommand) and upsert rather than append: before adding the current hook for an event, remove any existing sqz-owned entry for that event. Apply the same matching in
uninstallto strip them. This makes bothinitanduninstallsafe to run repeatedly.Relationship to #20
Distinct bug, same general area (sqz's read/modify/write of
settings.json). #20 is about the path format of the written command (backslash path fails under Git Bash). This issue is about duplicate accumulation / idempotency of the write itself. They can be fixed independently.