setup.sh: merge user hooks instead of overwriting (DPLAN-0234 Strand C fix)
- AIPass bridge entries (bridges/claude.py marker) refreshed on every install; user-wired hooks and custom events preserved - Fixture-verified: customs survive, stale bridge entries replaced no-dupe, fresh-install output shape-identical - Context: full 17/17 hook fire-test passed on fresh Linux Docker install
This commit is contained in:
@@ -11,6 +11,20 @@ PyPI version — not the changelog header.
|
||||
|
||||
## [2026-07-05]
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Installer no longer destroys a user's custom Claude Code hooks (DPLAN-0234
|
||||
Strand C).** setup.sh used to write `settings["hooks"]` wholesale — anyone
|
||||
with their own hooks in `~/.claude/settings.json` lost them on install or
|
||||
re-run. Now it merges: every AIPass bridge entry (identified by the
|
||||
`bridges/claude.py` marker) is refreshed, while user-wired hooks and custom
|
||||
events are preserved. Verified against fixtures: custom hooks survive, stale
|
||||
AIPass entries are replaced without duplicates, and the fresh-install output
|
||||
is shape-identical to before (7 events, 6 UserPromptSubmit + 6 PreCompact
|
||||
entries). Found while fire-testing a fresh install's hooks in Docker — all
|
||||
17 wired hook entries pass on a cold Linux clone (real kernel/navmap/branch
|
||||
prompt bytes, git gate blocks, clean no-ops on empty state).
|
||||
|
||||
### Added
|
||||
|
||||
- **`./aipass` — repo-root cold-clone launcher (DPLAN-0234 Strand B).** The
|
||||
|
||||
@@ -657,7 +657,7 @@ else:
|
||||
# UserPromptSubmit: 5 separate entries (EventType:hook_name) to avoid output merging
|
||||
# PreToolUse, PostToolUse, SubagentStop, Stop, Notification: single aggregate entries
|
||||
# PreCompact: 3 hooks x 2 matchers (manual + auto) = 6 entries
|
||||
settings["hooks"] = {
|
||||
aipass_hooks = {
|
||||
"UserPromptSubmit": [
|
||||
{"hooks": [{"type": "command", "command": f"{bridge} UserPromptSubmit:tier0_kernel"}]},
|
||||
{"hooks": [{"type": "command", "command": f"{bridge} UserPromptSubmit:navmap"}]},
|
||||
@@ -693,6 +693,19 @@ settings["hooks"] = {
|
||||
],
|
||||
}
|
||||
|
||||
# Merge, don't replace (DPLAN-0234 Strand C): refresh every AIPass bridge entry
|
||||
# (identified by the bridges/claude.py marker) but preserve any hooks the user
|
||||
# wired themselves. Re-runs stay idempotent; custom hooks survive reinstall.
|
||||
existing_hooks = settings.get("hooks", {})
|
||||
merged_hooks = {}
|
||||
for event in set(existing_hooks) | set(aipass_hooks):
|
||||
user_entries = [
|
||||
entry for entry in existing_hooks.get(event, [])
|
||||
if "bridges/claude.py" not in json.dumps(entry)
|
||||
]
|
||||
merged_hooks[event] = aipass_hooks.get(event, []) + user_entries
|
||||
settings["hooks"] = merged_hooks
|
||||
|
||||
# Inject AIPASS_HOME into env block so dispatched agents find AIPass
|
||||
env_block = settings.get("env", {})
|
||||
env_block["AIPASS_HOME"] = repo_root
|
||||
|
||||
Reference in New Issue
Block a user