# Hooks Hooks are shell commands that Gemini CLI runs automatically in response to session lifecycle events. For rocketsmith, a `SessionStart` hook injects the dependency status into context so the agent knows whether Java, OpenRocket, and PrusaSlicer are installed before saying a word. > **Claude Code:** Claude Code does not use Gemini-style hooks. Dependency status is surfaced via `rocketsmith_setup`, which the agent calls at session start. The hooks described on this page are Gemini CLI only. ## Structure ``` hooks/ ├── hooks.json ← declares the hook event and calls run-hook.cmd ├── run-hook.cmd ← cross-platform polyglot wrapper (Windows batch + Unix bash) └── session-start ← actual hook logic, named after the event, no file extension ``` This structure follows the convention from [obra/superpowers](https://github.com/obra/superpowers). ## hooks.json Declares which lifecycle events trigger which commands. Must live at `hooks/hooks.json` inside the extension directory — **not** inside `gemini-extension.json` (hooks is not a valid field there). ```json { "hooks": { "SessionStart": [ { "matcher": "startup|clear|compact", "hooks": [ { "type": "command", "command": "\"${extensionPath}/hooks/run-hook.cmd\" session-start", "async": false } ] } ] } } ``` `${extensionPath}` is substituted by Gemini CLI with the path to the installed extension directory. ## run-hook.cmd A polyglot script that runs as both a Windows batch file and a Unix shell script. Its job is to find `bash` on whatever platform the user is on and delegate to the named hook script. - On Unix: the batch block is skipped (`: << 'CMDBLOCK'` hides it from bash); the Unix section at the bottom runs directly - On Windows: `cmd.exe` executes the batch block, which searches for bash in Git for Windows locations and on `PATH` - If no bash is found on Windows, it exits silently — the session still works, just without the hook context The script takes the hook name as its first argument (`session-start`) and passes any remaining args through. ## session-start The actual hook logic. Named after the event it handles, with no file extension (following the superpowers convention — file extensions on hook scripts interfere with some platform auto-detection). What it does: 1. Determines the extension root using `SCRIPT_DIR` (`dirname "$0"`) — more reliable than env var injection 2. Checks for Java (`java` on PATH), the OpenRocket JAR (standard platform paths + `$OPENROCKET_JAR`), and PrusaSlicer (`prusa-slicer`/`PrusaSlicer` on PATH) directly via shell commands 3. Builds a status summary and lists any missing dependencies 4. JSON-escapes the output (backslashes, quotes, newlines) 5. Outputs `{"additionalContext": "..."}` — the format Gemini CLI expects from hooks ## Hook Output Format Gemini CLI hooks **must output valid JSON** to stdout. Plain text output is ignored. The SDK-standard format is: ```json {"additionalContext": "text to inject into agent context"} ``` Debugging output should go to `stderr`, not `stdout`, to avoid corrupting the JSON. The injected text appears in the agent's context as: ``` # rocketsmith dependency status java: installed (/path/to/jvm) openrocket: installed (/path/to/openrocket.jar) prusaslicer: installed (/path/to/prusa-slicer) status: ready — all tools available ``` If any dependency is missing, `status` reads `NOT READY — missing: . Call rocketsmith_setup(action='install') to install.` ## Why a Hook Instead of a Tool Call The previous approach required the agent to call `rocketsmith_setup(action="check")` as its first turn in every session. This had two problems: 1. It cost a turn and added latency before the agent could do any useful work 2. The agent could forget to do it The `SessionStart` hook runs automatically before the agent's first turn, so the dependency status is already in context when the agent starts. `rocketsmith_setup(action="install")` is still a tool for when installation is needed — that requires user confirmation and can take a while. ## Adding New Hooks To add a hook for a different lifecycle event (e.g. `BeforeTool`): 1. Add an entry to `hooks/hooks.json` under the new event key 2. Create a corresponding script in `hooks/` named after the event 3. Make the script executable: `chmod +x hooks/` 4. Ensure the script outputs `{"additionalContext": "..."}` JSON to stdout Available Gemini CLI hook events: `SessionStart`, `SessionEnd`, `BeforeAgent`, `AfterAgent`, `BeforeModel`, `AfterModel`, `BeforeToolSelection`, `BeforeTool`, `AfterTool`, `PreCompress`.