fix(setup): avoid null values in Claude settings hooks (GH#955)

When removing all hooks from an event, the key was being set to null
instead of being deleted. Claude Code expects arrays, not null values,
causing startup failures with 'Expected array, but received null'.

Changes:
- removeHookCommand now deletes the event key when no hooks remain
- installClaude cleans up any existing null values from buggy removal
- Added tests for null value prevention and cleanup

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
grip
2026-01-09 23:04:22 -08:00
committed by Steve Yegge
parent ecde3f2fd1
commit b4118e9f60
2 changed files with 106 additions and 2 deletions

View File

@@ -96,6 +96,14 @@ func installClaude(env claudeEnv, project bool, stealth bool) error {
settings["hooks"] = hooks
}
// GH#955: Clean up any null values left by previous buggy removal
// Claude Code expects arrays, not null values
for key, val := range hooks {
if val == nil {
delete(hooks, key)
}
}
command := "bd prime"
if stealth {
command = "bd prime --stealth"
@@ -272,7 +280,8 @@ func removeHookCommand(hooks map[string]interface{}, event, command string) {
}
// Filter out bd prime hooks
var filtered []interface{}
// Initialize as empty slice (not nil) to avoid JSON null serialization
filtered := make([]interface{}, 0, len(eventHooks))
for _, hook := range eventHooks {
hookMap, ok := hook.(map[string]interface{})
if !ok {
@@ -304,7 +313,14 @@ func removeHookCommand(hooks map[string]interface{}, event, command string) {
}
}
hooks[event] = filtered
// GH#955: Delete the key entirely if no hooks remain, rather than
// leaving an empty array. This is cleaner and avoids potential
// issues with empty arrays in settings.
if len(filtered) == 0 {
delete(hooks, event)
} else {
hooks[event] = filtered
}
}
// hasBeadsHooks checks if a settings file has bd prime hooks