Skip to content

fix(hooks): remove only Coucou's own nb-hook entries in the Claude paths - #944

Open
rahul05ranjan wants to merge 1 commit into
Louis-CFM:mainfrom
rahul05ranjan:fix/claude-hook-removal-own-entries
Open

rahul05ranjan wants to merge 1 commit into
Louis-CFM:mainfrom
rahul05ranjan:fix/claude-hook-removal-own-entries

Conversation

@rahul05ranjan

Copy link
Copy Markdown

Fixes #854.

What was wrong

The Claude Code install and uninstall paths (HookServer.swift) removed a whole hook group from ~/.claude/settings.json when any command inside it contained the substrings coucou or NotchBuddy:

  • buildHooksData() (~L1481) — the per-event cleanup before reinstalling;
  • uninstallClaudeHooks() (~L1506);
  • the App Store variants buildHooksData(claudeURL:) (~L1719) and uninstallClaudeHooksAppStore(claudeURL:) (~L1682).

So a user hook like ~/dev/coucou/scripts/lint.sh (likely for contributors to this repo), or any user hook sharing a matcher group with one of ours, was deleted on every install or uninstall. The Gemini path never had this problem: removeNbHookEntries (~L2011) removes only the entries whose command contains nb-hook, and drops a group only when every entry in it was ours.

The fix

All four Claude sites now call the same removeNbHookEntries the Gemini and Codex paths already use. Both builds' own commands always contain nb-hook (<supportDir>/nb-hook non-App Store, <~/.claude>/coucou/nb-hook App Store, incl. the --ask and status-line variants), and nb-hook is already the marker the rest of the file uses for "this one is ours" (statusLineInstalled, codexHooksInstalled, the Codex and Gemini cleanup). The doc comment on removeNbHookEntries now says it serves both formats.

Also added NotchBuddy/Tests/ClaudeHookRemovalTests.swift, a standalone script in the HermesConfigMergerTests.swift style:

swift NotchBuddy/Tests/ClaudeHookRemovalTests.swift

It keeps a copy of removeNbHookEntries plus the per-event install cleanup in sync, and covers: a user ~/dev/coucou/... group surviving install, no duplicate nb-hook entries on reinstall, echo coucou surviving uninstall while the nb-hook entry goes, coucou-only groups dropped entirely, a /opt/NotchBuddy/... path that is not our hook surviving uninstall, and the legacy flat format.

Verification

No Swift toolchain on this machine (arm64 Linux CI box), so:

scenario before after
install with a ~/dev/coucou/scripts/lint.sh group user group deleted user group kept, fresh nb-hook group appended
uninstall of a matcher holding echo coucou + our nb-hook whole matcher deleted echo coucou kept, nb-hook entry removed
uninstall of a coucou-only event key key removed key removed (unchanged)
reinstall twice one plain nb-hook group one plain nb-hook group, one AskUserQuestion group, no duplicates
user hook at /opt/NotchBuddy/something-else deleted kept
user hook named ~/dev/coucou/... on macOS App Store paths (lowercase coucou) deleted kept
  • Read-back of the live flows on HEAD before the change: grep -n 'NotchBuddy.*removeAll\|coucou.*removeAll' NotchBuddy/Sources/App/HookServer.swift found the four group-level sites and nothing else; the status-line path already used cmd.contains("nb-hook") ("not ours — leave unchanged"), which is the behavior this PR extends to the group paths.
  • I could not run swift NotchBuddy/Tests/ClaudeHookRemovalTests.swift locally (no toolchain); the script follows the same copy-and-assert pattern as the existing HermesConfigMergerTests.swift, so it runs the same way.

Two deliberate non-changes: the detection reads (hooksNeedUpdate, claudeHooksInstalled) still match the broad substrings — they only answer "are our hooks installed?" and removing nothing; and a user hook whose command itself contains nb-hook would still be treated as ours, exactly as in the Gemini and Codex paths today.

Install and uninstall removed a whole Claude Code hook group when any
command in it mentioned coucou or NotchBuddy, so a user hook like
~/dev/coucou/scripts/lint.sh was deleted. Both paths now use the Gemini
path's removeNbHookEntries, which removes only nb-hook commands and
drops a group only when every entry in it was ours.

Fixes Louis-CFM#854

Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Installing/uninstalling hooks removes user hooks whose command contains "coucou"

1 participant