[AUTOMATION] fix: optimize guard hook entry scan#238
Conversation
Greptile SummaryThis PR optimizes Guard hook detection in Claude settings. The main changes are:
Confidence Score: 4/5This is close, but the cleanup fallback should be fixed before merging.
Important Files Changed
Reviews (1): Last reviewed commit: "fix: optimize guard hook entry scan" | Re-trigger Greptile |
| func isGuardHookEntry(entry any) bool { | ||
| return isGuardHookCommand(fmt.Sprintf("%v", entry)) | ||
| if command, ok := entry.(string); ok { | ||
| return isGuardHookCommand(command) | ||
| } | ||
| return walkHookCommands([]any{entry}, func(command string) bool { | ||
| return isGuardHookCommand(command) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Keep flat hook removal This fallback is used when an entry is not a normal matcher group or does not have a nested
hooks array. Before this change, a flat entry like {"type":"command","command":"/usr/local/bin/kontext hook --agent claude --mode observe"} was removed because the formatted entry contained the guard command. Now the map is wrapped and passed to walkHookCommands, which only checks nested group["hooks"], so uninstall can leave that Guard hook behind and install can append a duplicate canonical hook.
| func isGuardHookEntry(entry any) bool { | |
| return isGuardHookCommand(fmt.Sprintf("%v", entry)) | |
| if command, ok := entry.(string); ok { | |
| return isGuardHookCommand(command) | |
| } | |
| return walkHookCommands([]any{entry}, func(command string) bool { | |
| return isGuardHookCommand(command) | |
| }) | |
| } | |
| func isGuardHookEntry(entry any) bool { | |
| if command, ok := entry.(string); ok { | |
| return isGuardHookCommand(command) | |
| } | |
| if isGuardHookObject(entry) { | |
| return true | |
| } | |
| return walkHookCommands([]any{entry}, func(command string) bool { | |
| return isGuardHookCommand(command) | |
| }) | |
| } |

Summary
This optimizes Guard hook entry scans by walking nested hook commands in place and stopping at the first guard match.
Before this, hook status and uninstall fallback paths could format an entire hook entry with fmt.Sprintf and then scan the full string for a match, which did extra allocation and extra text scanning in internal/guard/cli/cli.go.
Now the canonical path walks the real command fields directly and can return as soon as a guard hook is found.
Why
This gives kontext-cli a cheaper maintenance/runtime path for Claude hook inspection:
Claude settings entry
-> walkHookCommands
-> guard hook classification without whole-entry string formatting
This PR does not broaden behavior beyond the optimization scope.
What changed
Optimized guard hook entry detection in internal/guard/cli/cli.go
Removed whole-entry fmt.Sprintf scanning for nested hook detection
Preserved hosted vs Guard hook classification and uninstall behavior
Updated no tests; existing guard CLI tests cover the touched paths
Verification
go test ./internal/guard/cli
go test ./internal/guard/judge -run 'TestStartLlamaServerHealthCheckAndStop|TestStartLlamaServerEarlyExitDoesNotWaitForStopTimeout' -count=1
go test ./...
go vet ./...
git diff --check