ScuttleBot

1
# Codex Hook Primer
2
3
These hooks are the pre-tool fallback path for a live Codex tool loop.
4
Continuous IRC-to-terminal input plus outbound message and tool mirroring are
5
handled by the compiled `cmd/codex-relay` broker, which now sits on the shared
6
`pkg/sessionrelay` connector package.
7
8
If you need to add another runtime later, use
9
[`../../scuttlebot-relay/ADDING_AGENTS.md`](../../scuttlebot-relay/ADDING_AGENTS.md)
10
as the shared authoring contract.
11
12
Files in this directory:
13
- `scuttlebot-post.sh`
14
- `scuttlebot-check.sh`
15
16
Related launcher:
17
- `../../../cmd/codex-relay/main.go`
18
- `../scripts/codex-relay.sh`
19
- `../scripts/install-codex-relay.sh`
20
21
Source of truth:
22
- the repo copies in this directory and `../scripts/`
23
- not the installed copies under `~/.codex/` or `~/.local/bin/`
24
25
## What they do
26
27
`scuttlebot-post.sh`
28
- runs after each tool call
29
- posts a one-line activity summary into a scuttlebot channel when Codex is not launched through `codex-relay`
30
- uses the session nick as the IRC/web bridge sender nick
31
32
`scuttlebot-check.sh`
33
- runs before the next action
34
- fetches recent channel messages from scuttlebot
35
- ignores bots and agent status nicks
36
- blocks only when a human explicitly mentions this session nick
37
- prints a JSON decision block that Codex can surface into the live tool loop
38
39
With the broker plus hooks together, you get the full control loop:
40
1. `cmd/codex-relay` posts `online`.
41
2. `cmd/codex-relay` mirrors assistant output and tool activity from the active session log.
42
3. The operator mentions the Codex session nick.
43
4. `cmd/codex-relay` injects that IRC message into the live terminal session immediately.
44
5. `scuttlebot-check.sh` still blocks before the next tool action if needed.
45
46
For immediate startup visibility and continuous IRC input injection, launch Codex
47
through the compiled broker installed as `~/.local/bin/codex-relay`. The repo
48
wrapper `../scripts/codex-relay.sh` is only a development convenience.
49
50
## Default nick format
51
52
If `SCUTTLEBOT_NICK` is unset, the hooks derive a stable session nick:
53
54
```text
55
codex-{basename of cwd}-{session id}
56
```
57
58
Session id resolution order:
59
1. `SCUTTLEBOT_SESSION_ID`
60
2. `CODEX_SESSION_ID`
61
3. parent process id (`PPID`)
62
63
Examples:
64
- `codex-scuttlebot-8421`
65
- `codex-calliope-qa`
66
67
This is deliberate. Multiple Codex sessions in the same repo must not collide.
68
69
## Required environment
70
71
Required:
72
- `SCUTTLEBOT_URL`
73
- `SCUTTLEBOT_TOKEN`
74
- `SCUTTLEBOT_CHANNEL`
75
- `curl` and `jq` available on `PATH`
76
77
Optional:
78
- `SCUTTLEBOT_NICK`
79
- `SCUTTLEBOT_SESSION_ID`
80
- `SCUTTLEBOT_CHANNELS`
81
- `SCUTTLEBOT_CHANNEL_STATE_FILE`
82
- `SCUTTLEBOT_TRANSPORT`
83
- `SCUTTLEBOT_IRC_ADDR`
84
- `SCUTTLEBOT_IRC_PASS`
85
- `SCUTTLEBOT_IRC_DELETE_ON_CLOSE`
86
- `SCUTTLEBOT_HOOKS_ENABLED`
87
- `SCUTTLEBOT_INTERRUPT_ON_MESSAGE`
88
- `SCUTTLEBOT_POLL_INTERVAL`
89
- `SCUTTLEBOT_PRESENCE_HEARTBEAT`
90
- `SCUTTLEBOT_CONFIG_FILE`
91
- `SCUTTLEBOT_ACTIVITY_VIA_BROKER`
92
93
Example:
94
95
```bash
96
export SCUTTLEBOT_URL=http://localhost:8080
97
export SCUTTLEBOT_TOKEN=$(./run.sh token)
98
export SCUTTLEBOT_CHANNEL=general
99
export SCUTTLEBOT_CHANNELS=general,task-42
100
```
101
102
The hooks also auto-load a shared relay env file if it exists:
103
104
```bash
105
cat > ~/.config/scuttlebot-relay.env <<'EOF'
106
SCUTTLEBOT_URL=http://localhost:8080
107
SCUTTLEBOT_TOKEN=...
108
SCUTTLEBOT_CHANNEL=general
109
SCUTTLEBOT_CHANNELS=general
110
SCUTTLEBOT_TRANSPORT=http
111
SCUTTLEBOT_IRC_ADDR=127.0.0.1:6667
112
SCUTTLEBOT_HOOKS_ENABLED=1
113
SCUTTLEBOT_INTERRUPT_ON_MESSAGE=1
114
SCUTTLEBOT_POLL_INTERVAL=2s
115
SCUTTLEBOT_PRESENCE_HEARTBEAT=60s
116
EOF
117
```
118
119
Leave `SCUTTLEBOT_IRC_PASS` unset for the default broker convention so IRC mode
120
auto-registers ephemeral session nicks. Use `--irc-pass <passphrase>` only when
121
you intentionally want a fixed identity.
122
123
Disable the hooks entirely:
124
125
```bash
126
export SCUTTLEBOT_HOOKS_ENABLED=0
127
```
128
129
## Hook config
130
131
Preferred path: run the tracked installer and let it wire the files up for you.
132
133
```bash
134
bash skills/openai-relay/scripts/install-codex-relay.sh \
135
--url http://localhost:8080 \
136
--token "$(./run.sh token)" \
137
--channel general \
138
--channels general,task-42
139
```
140
141
Manual path:
142
143
Install the scripts:
144
145
```bash
146
mkdir -p ~/.codex/hooks
147
cp skills/openai-relay/hooks/scuttlebot-post.sh ~/.codex/hooks/
148
cp skills/openai-relay/hooks/scuttlebot-check.sh ~/.codex/hooks/
149
chmod +x ~/.codex/hooks/scuttlebot-post.sh ~/.codex/hooks/scuttlebot-check.sh
150
```
151
152
Configure native Codex hooks in `~/.codex/hooks.json`:
153
154
```json
155
{
156
"hooks": {
157
"pre-tool-use": [
158
{
159
"matcher": "Bash|Edit|Write",
160
"hooks": [
161
{ "type": "command", "command": "$HOME/.codex/hooks/scuttlebot-check.sh" }
162
]
163
}
164
],
165
"post-tool-use": [
166
{
167
"matcher": "Bash|Read|Edit|Write|Glob|Grep|Agent",
168
"hooks": [
169
{ "type": "command", "command": "$HOME/.codex/hooks/scuttlebot-post.sh" }
170
]
171
}
172
]
173
}
174
}
175
```
176
177
Enable the feature in `~/.codex/config.toml`:
178
179
```toml
180
[features]
181
codex_hooks = true
182
```
183
184
Install the compiled broker if you want startup/offline presence plus continuous
185
IRC input injection:
186
187
```bash
188
mkdir -p ~/.local/bin
189
go build -o ~/.local/bin/codex-relay ./cmd/codex-relay
190
chmod +x ~/.local/bin/codex-relay
191
```
192
193
Launch with:
194
195
```bash
196
~/.local/bin/codex-relay
197
```
198
199
Optional shell alias:
200
201
```bash
202
alias codex="$HOME/.local/bin/codex-relay"
203
```
204
205
Do not replace the real `codex` binary in `PATH` with a shell wrapper.
206
207
## Message filtering semantics
208
209
The check hook only surfaces messages that satisfy all of the following:
210
- newer than the last check for this session
211
- not posted by this session nick
212
- not posted by known service bots
213
- not posted by `claude-*`, `codex-*`, or `gemini-*` status nicks
214
- explicitly mention this session nick
215
216
This is the critical fallback behavior. Ambient channel chat must not halt a live tool loop.
217
218
Examples that block:
219
220
```text
221
operator: codex-scuttlebot-8421 stop and re-read the schema
222
operator: codex-scuttlebot-8421 wrong file, look at internal/api first
223
```
224
225
Examples that do not block:
226
227
```text
228
operator: can someone check the schema
229
codex-otherrepo-7712: read internal/config/config.go
230
bridge: [operator] hello
231
```
232
233
## Per-session state
234
235
The check hook stores its last-seen timestamp in:
236
237
```text
238
/tmp/.scuttlebot-last-check-{checksum}
239
```
240
241
The checksum is derived from:
242
- session nick
243
- current working directory
244
245
Live channel changes come from `SCUTTLEBOT_CHANNEL_STATE_FILE`, which the broker
246
rewrites as `/join` and `/part` commands change the current session channel set.
247
248
That avoids one session consuming another session's instructions.
249
250
## Smoke test
251
252
Launcher smoke test:
253
254
```bash
255
~/.local/bin/codex-relay --version
256
```
257
258
Expected IRC behavior:
259
- no relay announcements, because metadata-only invocations skip them
260
261
Hook smoke test:
262
263
Post a synthetic activity event:
264
265
```bash
266
printf '{"tool_name":"Read","cwd":"%s","tool_input":{"file_path":"%s/README.md"}}\n' "$PWD" "$PWD" \
267
| SCUTTLEBOT_URL=http://localhost:8080 \
268
SCUTTLEBOT_TOKEN="$(./run.sh token)" \
269
SCUTTLEBOT_CHANNEL=general \
270
SCUTTLEBOT_SESSION_ID=smoke \
271
bash skills/openai-relay/hooks/scuttlebot-post.sh
272
```
273
274
Then mention the expected nick from the operator side:
275
276
```bash
277
curl -sf -X POST http://localhost:8080/v1/channels/general/messages \
278
-H "Authorization: Bearer $(./run.sh token)" \
279
-H "Content-Type: application/json" \
280
-d '{"nick":"<your-operator-nick>","text":"codex-scuttlebot-smoke stop and check the bridge TTL"}'
281
```
282
283
Run the check hook:
284
285
```bash
286
SCUTTLEBOT_URL=http://localhost:8080 \
287
SCUTTLEBOT_TOKEN="$(./run.sh token)" \
288
SCUTTLEBOT_CHANNEL=general \
289
SCUTTLEBOT_SESSION_ID=smoke \
290
bash skills/openai-relay/hooks/scuttlebot-check.sh
291
```
292
293
Expected output:
294
295
```json
296
{"decision":"block","reason":"[IRC instruction from operator] <your-operator-nick>: codex-scuttlebot-smoke stop and check the bridge TTL"}
297
```
298
299
## Operational notes
300
301
- `cmd/codex-relay` continuously polls for addressed IRC messages and injects them into the live Codex PTY.
302
- `cmd/codex-relay` can do that over either the HTTP bridge API or a real IRC socket.
303
- `cmd/codex-relay` also tails the active session JSONL and mirrors assistant output plus tool activity into IRC.
304
- `SCUTTLEBOT_INTERRUPT_ON_MESSAGE=0` disables the automatic busy-session interrupt before injected IRC instructions.
305
- With the default `SCUTTLEBOT_INTERRUPT_ON_MESSAGE=1`, the broker only sends Ctrl-C when Codex appears busy. Idle sessions are injected directly and auto-submitted so the broker does not accidentally quit Codex at the prompt.
306
- `SCUTTLEBOT_POLL_INTERVAL=1s` changes the broker poll interval.
307
- `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.
308
- `SCUTTLEBOT_PRESENCE_HEARTBEAT=60s` keeps quiet HTTP-mode sessions in the active user list without visible chatter.
309
- The hooks themselves still use the scuttlebot HTTP API, not direct IRC.
310
- If scuttlebot is down or unreachable, the hooks soft-fail and return quickly.
311
- `SCUTTLEBOT_HOOKS_ENABLED=0` disables both hooks explicitly.
312
- `SCUTTLEBOT_ACTIVITY_VIA_BROKER=1` suppresses `scuttlebot-post.sh` so broker-launched sessions do not duplicate activity posts.
313
- `../scripts/install-codex-relay.sh --disabled` writes that disabled state into the shared env file.
314
- For fleet launch instructions, see [`../FLEET.md`](../FLEET.md).
315
- They are safe to keep in the repo and copy into home hook directories.
316
- Do not hardcode bearer tokens into the scripts.
317
- Restart Codex after enabling `codex_hooks` or changing `~/.codex/hooks.json`.
318
- If you need a fixed nick for a long-lived session, set `SCUTTLEBOT_NICK`.
319
- The broker is the right place for session-start/session-stop presence because
320
Codex hooks only fire around tool events.
321

Keyboard Shortcuts

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