You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(VCOP-13): correct Gemini hook contract and apply review feedback
Reviewer B (functionality + reference audit) flagged two Critical bugs
that pre-dated this branch but were in scope to fix while we're in the
hook code: vibecop's Gemini install wrote a snake_case `before_tool`
string, but Gemini CLI requires PascalCase `BeforeTool` as an array of
matcher groups (same shape as Claude). Vibecop's Gemini stdin parser
read `tool`/`input`/`project`, but real Gemini sends snake_case
`tool_name`/`tool_input`/`cwd` (same as Claude). End-to-end, vibecop on
Gemini was silently broken before this fix.
Verified against
github.com/google-gemini/gemini-cli/blob/main/docs/hooks/reference.md.
- internal/hooks/install.go:
- geminiHooks now uses [BeforeTool array of matcher groups]; install
upsert mirrors Claude.
- uninstallGeminiHooks strips legacy `before_tool` snake_case key on
upgrade.
- copilotSettingsPath honors COPILOT_HOME env override (Reviewer B/H1).
- upsertCodexEntry / upsertCopilotEntry now return a `changed` bool
so installs are idempotent at the file-write level (Reviewer A/H2).
- internal/hooks/hooks.go:
- GeminiCLIPayload uses `tool_name`/`tool_input`/`cwd`/`session_id`.
- normalizeGeminiCLI uses toolInputSummary on the object input.
- Auto-detection branches on hook_event_name for snake_case payloads
(PermissionRequest → Codex, BeforeTool → Gemini, PreToolUse +
turn_id → Codex, else Claude). Removed unused hasAnyKey helper.
- Softened normalizeCodex hook_event_name fallback comment to match
Codex docs language (Reviewer A/M).
- cmd/hook.go: small comment on the unreachable `return nil` (cobra
signature requires it).
- docs/spec.md: corrected Gemini install example block.
- docs/superpowers/specs/...: updated stale Copilot allow shape in the
design doc to match implementation (Reviewer B/H3).
- Tests: gofmt-clean on touched files; new fixtures cover Gemini wire
shape, legacy migration, and the snake_case auto-detection branches;
switched new install tests to t.Setenv (Reviewer A nit).
go test ./... → 157 pass. go vet ./... → clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/superpowers/specs/2026-05-08-per-harness-hook-responses-design.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,7 +55,7 @@ Emit the correct **per-harness JSON response** on stdout so that `approve` actua
55
55
|`codex`, `PreToolUse`| (no stdout — Codex PreToolUse cannot allow; PermissionRequest is the approval channel) | same JSON shape as Claude PreToolUse deny + stderr | (no stdout) |
56
56
|`codex`, `PermissionRequest`|`{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow"}}}`|`{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"deny","message":"…"}}}` + stderr | (no stdout) |
57
57
|`gemini`, `BeforeTool`|`{"decision":"allow","reason":"…"}`|`{"decision":"deny","reason":"…"}` + stderr | (no stdout) |
58
-
|`copilot`, `preToolUse`|`{"permissionDecision":"allow","permissionDecisionReason":"…"}`|`{"permissionDecision":"deny","permissionDecisionReason":"…"}` + stderr | (no stdout) |
58
+
|`copilot`, `preToolUse`|`{"permissionDecision":"allow"}`|`{"permissionDecision":"deny","permissionDecisionReason":"…"}` + stderr | (no stdout) |
59
59
60
60
The `permissionDecisionReason` / `reason` / `message` fields are omitted from JSON when the daemon's reason is empty.
0 commit comments