ScuttleBot

scuttlebot / skills / gemini-relay / hooks / README.md
Source Blame History 243 lines
016a29f… lmata 1 # Gemini Hook Primer
016a29f… lmata 2
016a29f… lmata 3 These hooks are the activity and pre-tool fallback path for a live Gemini tool loop.
016a29f… lmata 4 Continuous IRC-to-terminal input plus `online` / `offline` presence are handled by
016a29f… lmata 5 the compiled `cmd/gemini-relay` broker, which now sits on the shared
016a29f… lmata 6 `pkg/sessionrelay` connector package.
016a29f… lmata 7
016a29f… lmata 8 Upstream Gemini CLI has a richer native hook surface than just tool hooks:
016a29f… lmata 9 `SessionStart`, `SessionEnd`, `BeforeAgent`, `AfterAgent`, `BeforeModel`,
016a29f… lmata 10 `AfterModel`, `BeforeToolSelection`, `BeforeTool`, `AfterTool`, `PreCompress`,
016a29f… lmata 11 and `Notification`. In this repo we intentionally wire `BeforeTool`,
016a29f… lmata 12 `AfterTool`, and `AfterAgent` for the relay hooks, while the broker owns
016a29f… lmata 13 session presence and continuous live operator message injection.
016a29f… lmata 14
016a29f… lmata 15 If you need to add another runtime later, use
016a29f… lmata 16 [`../../scuttlebot-relay/ADDING_AGENTS.md`](../../scuttlebot-relay/ADDING_AGENTS.md)
016a29f… lmata 17 as the shared authoring contract.
016a29f… lmata 18
016a29f… lmata 19 Files in this directory:
016a29f… lmata 20 - `scuttlebot-post.sh`
016a29f… lmata 21 - `scuttlebot-check.sh`
016a29f… lmata 22 - `scuttlebot-after-agent.sh`
016a29f… lmata 23
016a29f… lmata 24 Related launcher:
016a29f… lmata 25 - `../../../cmd/gemini-relay/main.go`
016a29f… lmata 26 - `../scripts/gemini-relay.sh`
016a29f… lmata 27 - `../scripts/install-gemini-relay.sh`
016a29f… lmata 28
016a29f… lmata 29 Source of truth:
016a29f… lmata 30 - the repo copies in this directory and `../scripts/`
016a29f… lmata 31 - not the installed copies under `~/.gemini/` or `~/.local/bin/`
016a29f… lmata 32
016a29f… lmata 33 ## What they do
016a29f… lmata 34
016a29f… lmata 35 `scuttlebot-post.sh`
016a29f… lmata 36 - runs on Gemini CLI `AfterTool`
016a29f… lmata 37 - posts a one-line activity summary into a scuttlebot channel
016a29f… lmata 38 - remains the primary Gemini activity path today, even when launched through `gemini-relay`
016a29f… lmata 39 - returns valid JSON success output (`{}`), which Gemini CLI expects for hook success
016a29f… lmata 40
016a29f… lmata 41 `scuttlebot-check.sh`
016a29f… lmata 42 - runs on Gemini CLI `BeforeTool`
016a29f… lmata 43 - fetches recent channel messages from scuttlebot
016a29f… lmata 44 - ignores bots and agent status nicks
016a29f… lmata 45 - blocks only when a human explicitly mentions this session nick
016a29f… lmata 46 - returns valid JSON success output (`{}`) when no block is needed
016a29f… lmata 47
016a29f… lmata 48 `scuttlebot-after-agent.sh`
016a29f… lmata 49 - runs on Gemini CLI `AfterAgent`
016a29f… lmata 50 - posts the final assistant reply for each completed turn into scuttlebot
016a29f… lmata 51 - normalizes whitespace and splits long replies into IRC-safe lines
016a29f… lmata 52 - returns valid JSON success output (`{}`), which Gemini CLI expects for hook success
016a29f… lmata 53
016a29f… lmata 54 With the broker plus hooks together, you get the current control loop:
016a29f… lmata 55 1. `cmd/gemini-relay` posts `online`.
016a29f… lmata 56 2. The operator mentions the Gemini session nick.
016a29f… lmata 57 3. `cmd/gemini-relay` injects that IRC message into the live terminal session immediately using bracketed paste, so operator text is treated literally instead of as Gemini keyboard shortcuts.
016a29f… lmata 58 4. `scuttlebot-check.sh` still blocks before the next tool action if needed.
016a29f… lmata 59 5. `scuttlebot-post.sh` posts tool activity summaries.
016a29f… lmata 60 6. `scuttlebot-after-agent.sh` posts the final assistant reply when the turn completes.
016a29f… lmata 61
016a29f… lmata 62 This is deliberate:
016a29f… lmata 63 - broker: session lifetime, presence, live input
016a29f… lmata 64 - hooks: pre-tool gate, post-tool activity, and final reply mirroring
016a29f… lmata 65
016a29f… lmata 66 ## Default nick format
016a29f… lmata 67
016a29f… lmata 68 If `SCUTTLEBOT_NICK` is unset, the hooks derive a stable session nick:
016a29f… lmata 69
016a29f… lmata 70 ```text
016a29f… lmata 71 gemini-{basename of cwd}-{session id}
016a29f… lmata 72 ```
016a29f… lmata 73
016a29f… lmata 74 Session id resolution order:
016a29f… lmata 75 1. `SCUTTLEBOT_SESSION_ID`
016a29f… lmata 76 2. `GEMINI_SESSION_ID`
016a29f… lmata 77 3. parent process id (`PPID`)
016a29f… lmata 78
016a29f… lmata 79 Examples:
016a29f… lmata 80 - `gemini-scuttlebot-a1b2c3d4`
016a29f… lmata 81 - `gemini-api-e5f6a7b8`
016a29f… lmata 82
016a29f… lmata 83 ## Required environment
016a29f… lmata 84
016a29f… lmata 85 Required:
016a29f… lmata 86 - `SCUTTLEBOT_URL`
016a29f… lmata 87 - `SCUTTLEBOT_TOKEN`
016a29f… lmata 88 - `SCUTTLEBOT_CHANNEL`
016a29f… lmata 89 - `curl` and `jq` available on `PATH`
016a29f… lmata 90
016a29f… lmata 91 Optional:
016a29f… lmata 92 - `SCUTTLEBOT_NICK`
016a29f… lmata 93 - `SCUTTLEBOT_SESSION_ID`
1d3caa2… lmata 94 - `SCUTTLEBOT_CHANNELS`
1d3caa2… lmata 95 - `SCUTTLEBOT_CHANNEL_STATE_FILE`
016a29f… lmata 96 - `SCUTTLEBOT_TRANSPORT`
016a29f… lmata 97 - `SCUTTLEBOT_IRC_ADDR`
016a29f… lmata 98 - `SCUTTLEBOT_IRC_PASS`
016a29f… lmata 99 - `SCUTTLEBOT_IRC_DELETE_ON_CLOSE`
016a29f… lmata 100 - `SCUTTLEBOT_HOOKS_ENABLED`
016a29f… lmata 101 - `SCUTTLEBOT_INTERRUPT_ON_MESSAGE`
016a29f… lmata 102 - `SCUTTLEBOT_POLL_INTERVAL`
016a29f… lmata 103 - `SCUTTLEBOT_PRESENCE_HEARTBEAT`
016a29f… lmata 104 - `SCUTTLEBOT_AFTER_AGENT_MAX_POSTS`
016a29f… lmata 105 - `SCUTTLEBOT_AFTER_AGENT_CHUNK_WIDTH`
016a29f… lmata 106 - `SCUTTLEBOT_CONFIG_FILE`
016a29f… lmata 107
016a29f… lmata 108 Example:
016a29f… lmata 109
016a29f… lmata 110 ```bash
016a29f… lmata 111 export SCUTTLEBOT_URL=http://localhost:8080
016a29f… lmata 112 export SCUTTLEBOT_TOKEN=$(./run.sh token)
016a29f… lmata 113 export SCUTTLEBOT_CHANNEL=general
1d3caa2… lmata 114 export SCUTTLEBOT_CHANNELS=general,task-42
016a29f… lmata 115 ```
016a29f… lmata 116
016a29f… lmata 117 The hooks also auto-load a shared relay env file if it exists:
016a29f… lmata 118
016a29f… lmata 119 ```bash
016a29f… lmata 120 cat > ~/.config/scuttlebot-relay.env <<'EOF2'
016a29f… lmata 121 SCUTTLEBOT_URL=http://localhost:8080
016a29f… lmata 122 SCUTTLEBOT_TOKEN=...
016a29f… lmata 123 SCUTTLEBOT_CHANNEL=general
1d3caa2… lmata 124 SCUTTLEBOT_CHANNELS=general
016a29f… lmata 125 SCUTTLEBOT_TRANSPORT=http
016a29f… lmata 126 SCUTTLEBOT_IRC_ADDR=127.0.0.1:6667
016a29f… lmata 127 SCUTTLEBOT_HOOKS_ENABLED=1
016a29f… lmata 128 SCUTTLEBOT_INTERRUPT_ON_MESSAGE=1
016a29f… lmata 129 SCUTTLEBOT_POLL_INTERVAL=2s
016a29f… lmata 130 SCUTTLEBOT_PRESENCE_HEARTBEAT=60s
016a29f… lmata 131 EOF2
016a29f… lmata 132 ```
b8ce843… lmata 133
b8ce843… lmata 134 Leave `SCUTTLEBOT_IRC_PASS` unset for the default broker convention so IRC mode
b8ce843… lmata 135 auto-registers ephemeral session nicks. Use `--irc-pass <passphrase>` only when
b8ce843… lmata 136 you intentionally want a fixed identity.
016a29f… lmata 137
016a29f… lmata 138 Disable the hooks entirely:
016a29f… lmata 139
016a29f… lmata 140 ```bash
016a29f… lmata 141 export SCUTTLEBOT_HOOKS_ENABLED=0
016a29f… lmata 142 ```
016a29f… lmata 143
016a29f… lmata 144 ## Hook config
016a29f… lmata 145
016a29f… lmata 146 Preferred path: run the tracked installer and let it wire the files up for you.
016a29f… lmata 147
016a29f… lmata 148 ```bash
016a29f… lmata 149 bash skills/gemini-relay/scripts/install-gemini-relay.sh \
016a29f… lmata 150 --url http://localhost:8080 \
016a29f… lmata 151 --token "$(./run.sh token)" \
1d3caa2… lmata 152 --channel general \
1d3caa2… lmata 153 --channels general,task-42
016a29f… lmata 154 ```
016a29f… lmata 155
016a29f… lmata 156 Manual path:
016a29f… lmata 157
016a29f… lmata 158 Install the scripts:
016a29f… lmata 159
016a29f… lmata 160 ```bash
016a29f… lmata 161 mkdir -p ~/.gemini/hooks
016a29f… lmata 162 cp skills/gemini-relay/hooks/scuttlebot-post.sh ~/.gemini/hooks/
016a29f… lmata 163 cp skills/gemini-relay/hooks/scuttlebot-check.sh ~/.gemini/hooks/
016a29f… lmata 164 cp skills/gemini-relay/hooks/scuttlebot-after-agent.sh ~/.gemini/hooks/
016a29f… lmata 165 chmod +x ~/.gemini/hooks/scuttlebot-post.sh ~/.gemini/hooks/scuttlebot-check.sh ~/.gemini/hooks/scuttlebot-after-agent.sh
016a29f… lmata 166 ```
016a29f… lmata 167
016a29f… lmata 168 Configure Gemini hooks in `~/.gemini/settings.json`:
016a29f… lmata 169
016a29f… lmata 170 ```json
016a29f… lmata 171 {
016a29f… lmata 172 "hooks": {
016a29f… lmata 173 "BeforeTool": [
016a29f… lmata 174 {
016a29f… lmata 175 "matcher": ".*",
016a29f… lmata 176 "hooks": [
016a29f… lmata 177 { "type": "command", "command": "$HOME/.gemini/hooks/scuttlebot-check.sh" }
016a29f… lmata 178 ]
016a29f… lmata 179 }
016a29f… lmata 180 ],
016a29f… lmata 181 "AfterTool": [
016a29f… lmata 182 {
016a29f… lmata 183 "matcher": ".*",
016a29f… lmata 184 "hooks": [
016a29f… lmata 185 { "type": "command", "command": "$HOME/.gemini/hooks/scuttlebot-post.sh" }
016a29f… lmata 186 ]
016a29f… lmata 187 }
016a29f… lmata 188 ],
016a29f… lmata 189 "AfterAgent": [
016a29f… lmata 190 {
016a29f… lmata 191 "matcher": "*",
016a29f… lmata 192 "hooks": [
016a29f… lmata 193 { "type": "command", "command": "$HOME/.gemini/hooks/scuttlebot-after-agent.sh" }
016a29f… lmata 194 ]
016a29f… lmata 195 }
016a29f… lmata 196 ]
016a29f… lmata 197 }
016a29f… lmata 198 }
016a29f… lmata 199 ```
016a29f… lmata 200
016a29f… lmata 201 Install the compiled broker if you want startup/offline presence plus continuous
016a29f… lmata 202 IRC input injection:
016a29f… lmata 203
016a29f… lmata 204 ```bash
016a29f… lmata 205 mkdir -p ~/.local/bin
016a29f… lmata 206 go build -o ~/.local/bin/gemini-relay ./cmd/gemini-relay
016a29f… lmata 207 chmod +x ~/.local/bin/gemini-relay
016a29f… lmata 208 ```
016a29f… lmata 209
016a29f… lmata 210 Launch with:
016a29f… lmata 211
016a29f… lmata 212 ```bash
016a29f… lmata 213 ~/.local/bin/gemini-relay
016a29f… lmata 214 ```
016a29f… lmata 215
016a29f… lmata 216 ## Message filtering semantics
016a29f… lmata 217
016a29f… lmata 218 The check hook only surfaces messages that satisfy all of the following:
016a29f… lmata 219 - newer than the last check for this session
016a29f… lmata 220 - not posted by this session nick
016a29f… lmata 221 - not posted by known service bots
016a29f… lmata 222 - not posted by `claude-*`, `codex-*`, or `gemini-*` status nicks
016a29f… lmata 223 - explicitly mention this session nick
016a29f… lmata 224
016a29f… lmata 225 Ambient channel chat must not halt a live tool loop.
016a29f… lmata 226
016a29f… lmata 227 ## Operational notes
016a29f… lmata 228
016a29f… lmata 229 - `cmd/gemini-relay` can use either the HTTP bridge API or a real IRC socket.
1d3caa2… lmata 230 - `SCUTTLEBOT_CHANNEL` is the primary control channel; `SCUTTLEBOT_CHANNELS` is the startup channel set.
1d3caa2… lmata 231 - `/channels`, `/join #channel`, and `/part #channel` change the live session channel set without rewriting the shared env file.
016a29f… lmata 232 - `SCUTTLEBOT_TRANSPORT=irc` gives the live session a true IRC presence; `SCUTTLEBOT_IRC_PASS` skips auto-registration if you already manage the NickServ account yourself.
016a29f… lmata 233 - `SCUTTLEBOT_PRESENCE_HEARTBEAT=60s` keeps quiet HTTP-mode sessions in the active user list without visible chatter.
1d3caa2… lmata 234 - `SCUTTLEBOT_CHANNEL_STATE_FILE` is the broker-written override file that keeps hooks aligned with live channel joins and parts.
016a29f… lmata 235 - Gemini CLI expects hook success responses on `stdout` to be valid JSON; these relay hooks emit `{}` on success and structured deny JSON on blocks.
016a29f… lmata 236 - Gemini CLI built-in tool names are things like `run_shell_command`, `read_file`, and `write_file`; the activity hook summarizes those native names.
016a29f… lmata 237 - Gemini outbound mirroring is still hook-owned today: `AfterTool` covers tool activity and `AfterAgent` covers final assistant replies. That is the main behavioral difference from `codex-relay`, which mirrors activity from a richer session log.
016a29f… lmata 238 - `scuttlebot-after-agent.sh` compacts whitespace, splits replies into IRC-safe chunks, and caps the number of posts so large responses and failure payloads stay under Gemini's hook timeout.
016a29f… lmata 239 - `SCUTTLEBOT_AFTER_AGENT_MAX_POSTS` defaults to `6`; `SCUTTLEBOT_AFTER_AGENT_CHUNK_WIDTH` defaults to `360`.
016a29f… lmata 240 - If scuttlebot is down or unreachable, the hooks soft-fail and return quickly.
016a29f… lmata 241 - `SCUTTLEBOT_HOOKS_ENABLED=0` disables all Gemini relay hooks explicitly.
016a29f… lmata 242 - They should remain in the repo as installable reference files.
016a29f… lmata 243 - Do not bake tokens into the scripts. Use environment variables.

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button