From 630a705bdcc592747eea61e4a11174a155167704 Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Sat, 28 Feb 2026 05:38:11 -0800 Subject: [PATCH] fix(gemini): use AfterTool instead of PostToolUse for Gemini CLI hooks Gemini CLI uses AfterTool as the post-tool hook event name, not PostToolUse (which is Claude Code's event name). The installer was registering the context monitor under PostToolUse for all runtimes, causing Gemini to print "Invalid hook event name" warnings on every run and silently disabling the context monitor. Changes: - install.js: use runtime-aware event name (AfterTool for Gemini, PostToolUse for others) when registering context monitor hook - install.js: uninstall cleans up both PostToolUse and AfterTool entries for backward compatibility with existing installs - gsd-context-monitor.js: runtime-aware hookEventName in output - docs/context-monitor.md: document both event names with Gemini example Closes #750 Co-Authored-By: Claude Opus 4.6 --- bin/install.js | 49 +++++++++++++++++++----------------- docs/context-monitor.md | 27 +++++++++++++++++--- hooks/gsd-context-monitor.js | 4 +-- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/bin/install.js b/bin/install.js index 8a265e643..aa62fd27c 100755 --- a/bin/install.js +++ b/bin/install.js @@ -1403,24 +1403,26 @@ function uninstall(isGlobal, runtime = 'claude') { } } - // Remove GSD hooks from PostToolUse - if (settings.hooks && settings.hooks.PostToolUse) { - const before = settings.hooks.PostToolUse.length; - settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(entry => { - if (entry.hooks && Array.isArray(entry.hooks)) { - const hasGsdHook = entry.hooks.some(h => - h.command && h.command.includes('gsd-context-monitor') - ); - return !hasGsdHook; + // Remove GSD hooks from PostToolUse and AfterTool (Gemini uses AfterTool) + for (const eventName of ['PostToolUse', 'AfterTool']) { + if (settings.hooks && settings.hooks[eventName]) { + const before = settings.hooks[eventName].length; + settings.hooks[eventName] = settings.hooks[eventName].filter(entry => { + if (entry.hooks && Array.isArray(entry.hooks)) { + const hasGsdHook = entry.hooks.some(h => + h.command && h.command.includes('gsd-context-monitor') + ); + return !hasGsdHook; + } + return true; + }); + if (settings.hooks[eventName].length < before) { + settingsModified = true; + console.log(` ${green}✓${reset} Removed context monitor hook from settings`); + } + if (settings.hooks[eventName].length === 0) { + delete settings.hooks[eventName]; } - return true; - }); - if (settings.hooks.PostToolUse.length < before) { - settingsModified = true; - console.log(` ${green}✓${reset} Removed context monitor hook from settings`); - } - if (settings.hooks.PostToolUse.length === 0) { - delete settings.hooks.PostToolUse; } } @@ -2031,7 +2033,8 @@ function install(isGlobal, runtime = 'claude') { } // Configure statusline and hooks in settings.json - // Gemini shares same hook system as Claude Code for now + // Gemini uses AfterTool instead of PostToolUse for post-tool hooks + const postToolEvent = runtime === 'gemini' ? 'AfterTool' : 'PostToolUse'; const settingsPath = path.join(targetDir, 'settings.json'); const settings = cleanupOrphanedHooks(readSettings(settingsPath)); const statuslineCommand = isGlobal @@ -2080,17 +2083,17 @@ function install(isGlobal, runtime = 'claude') { console.log(` ${green}✓${reset} Configured update check hook`); } - // Configure PostToolUse hook for context window monitoring - if (!settings.hooks.PostToolUse) { - settings.hooks.PostToolUse = []; + // Configure post-tool hook for context window monitoring + if (!settings.hooks[postToolEvent]) { + settings.hooks[postToolEvent] = []; } - const hasContextMonitorHook = settings.hooks.PostToolUse.some(entry => + const hasContextMonitorHook = settings.hooks[postToolEvent].some(entry => entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-context-monitor')) ); if (!hasContextMonitorHook) { - settings.hooks.PostToolUse.push({ + settings.hooks[postToolEvent].push({ hooks: [ { type: 'command', diff --git a/docs/context-monitor.md b/docs/context-monitor.md index b312bd834..24272d821 100644 --- a/docs/context-monitor.md +++ b/docs/context-monitor.md @@ -1,6 +1,6 @@ # Context Window Monitor -A PostToolUse hook that warns the agent when context window usage is high. +A post-tool hook (`PostToolUse` for Claude Code, `AfterTool` for Gemini CLI) that warns the agent when context window usage is high. ## Problem @@ -37,7 +37,7 @@ Statusline Hook (gsd-statusline.js) /tmp/claude-ctx-{session_id}.json ^ reads | -Context Monitor (gsd-context-monitor.js, PostToolUse) +Context Monitor (gsd-context-monitor.js, PostToolUse/AfterTool) | injects v additionalContext -> Agent sees warning @@ -63,9 +63,9 @@ GSD's `/gsd:pause-work` command saves execution state. The WARNING message sugge Both hooks are automatically registered during `npx get-shit-done-cc` installation: - **Statusline** (writes bridge file): Registered as `statusLine` in settings.json -- **Context Monitor** (reads bridge file): Registered as `PostToolUse` hook in settings.json +- **Context Monitor** (reads bridge file): Registered as `PostToolUse` hook in settings.json (`AfterTool` for Gemini) -Manual registration in `~/.claude/settings.json`: +Manual registration in `~/.claude/settings.json` (Claude Code): ```json { @@ -88,6 +88,25 @@ Manual registration in `~/.claude/settings.json`: } ``` +For Gemini CLI (`~/.gemini/settings.json`), use `AfterTool` instead of `PostToolUse`: + +```json +{ + "hooks": { + "AfterTool": [ + { + "hooks": [ + { + "type": "command", + "command": "node ~/.gemini/hooks/gsd-context-monitor.js" + } + ] + } + ] + } +} +``` + ## Safety - The hook wraps everything in try/catch and exits silently on error diff --git a/hooks/gsd-context-monitor.js b/hooks/gsd-context-monitor.js index f86ac2f59..cae8fc930 100644 --- a/hooks/gsd-context-monitor.js +++ b/hooks/gsd-context-monitor.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -// Context Monitor - PostToolUse hook +// Context Monitor - PostToolUse/AfterTool hook (Gemini uses AfterTool) // Reads context metrics from the statusline bridge file and injects // warnings when context usage is high. This makes the AGENT aware of // context limits (the statusline only shows the user). @@ -109,7 +109,7 @@ process.stdin.on('end', () => { const output = { hookSpecificOutput: { - hookEventName: "PostToolUse", + hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse", additionalContext: message } };