@globant/coda-windows-x64 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/assets/agents/coda-help.md +166 -0
  2. package/assets/agents/create-workflow.md +264 -0
  3. package/assets/agents/explore.md +26 -0
  4. package/assets/docs/agents.md +162 -0
  5. package/assets/docs/cli-reference.md +131 -0
  6. package/assets/docs/cli-vs-batch.md +58 -0
  7. package/assets/docs/config-json.md +314 -0
  8. package/assets/docs/config-reference.md +329 -0
  9. package/assets/docs/configuration.md +105 -0
  10. package/assets/docs/connect-provider.md +77 -0
  11. package/assets/docs/extensions.md +260 -0
  12. package/assets/docs/faq.md +152 -0
  13. package/assets/docs/glossary.md +41 -0
  14. package/assets/docs/guide-automate.md +135 -0
  15. package/assets/docs/guide-changes.md +101 -0
  16. package/assets/docs/guide-collaborate.md +119 -0
  17. package/assets/docs/guide-extend.md +120 -0
  18. package/assets/docs/guide-understand.md +95 -0
  19. package/assets/docs/hooks.md +704 -0
  20. package/assets/docs/how-it-works.md +73 -0
  21. package/assets/docs/index.md +62 -0
  22. package/assets/docs/installation.md +71 -0
  23. package/assets/docs/logging.md +123 -0
  24. package/assets/docs/overview.md +91 -0
  25. package/assets/docs/permissions.md +93 -0
  26. package/assets/docs/quickstart.md +104 -0
  27. package/assets/docs/sessions.md +139 -0
  28. package/assets/docs/shortcuts.md +61 -0
  29. package/assets/docs/tools-reference.md +81 -0
  30. package/assets/docs/workflows.md +146 -0
  31. package/assets/skills/create-extension/SKILL.md +293 -0
  32. package/assets/skills/create-hook/SKILL.md +442 -0
  33. package/assets/skills/create-skill/SKILL.md +180 -0
  34. package/assets/skills/plan/SKILL.md +25 -0
  35. package/coda.exe +0 -0
  36. package/lib/keytar/build/Release/keytar.node +0 -0
  37. package/lib/keytar/lib/keytar.js +43 -0
  38. package/lib/opentui/assets/javascript/highlights.scm +205 -0
  39. package/lib/opentui/assets/javascript/tree-sitter-javascript.wasm +0 -0
  40. package/lib/opentui/assets/markdown/highlights.scm +150 -0
  41. package/lib/opentui/assets/markdown/injections.scm +27 -0
  42. package/lib/opentui/assets/markdown/tree-sitter-markdown.wasm +0 -0
  43. package/lib/opentui/assets/markdown_inline/highlights.scm +115 -0
  44. package/lib/opentui/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  45. package/lib/opentui/assets/typescript/highlights.scm +604 -0
  46. package/lib/opentui/assets/typescript/tree-sitter-typescript.wasm +0 -0
  47. package/lib/opentui/assets/zig/highlights.scm +284 -0
  48. package/lib/opentui/assets/zig/tree-sitter-zig.wasm +0 -0
  49. package/lib/opentui/parser.worker.js +4244 -0
  50. package/lib/opentui/tree-sitter-3jzf13jk.wasm +0 -0
  51. package/lib/ripgrep/COPYING +3 -0
  52. package/lib/ripgrep/LICENSE-MIT +21 -0
  53. package/lib/ripgrep/UNLICENSE +24 -0
  54. package/lib/ripgrep/rg.exe +0 -0
  55. package/package.json +20 -0
@@ -0,0 +1,704 @@
1
+ # Lifecycle hooks
2
+
3
+ **Hooks** are user-defined shell commands or HTTP endpoints that run automatically at specific points in a Coda session. They let you enforce policies, inject context, gate dangerous operations, run linters, log activity, and integrate with external systems — without modifying Coda's source code.
4
+
5
+ The hook system is **compatible with Claude Code hooks**: the same JSON input/output contract, the same config format, and the same `hooks/hooks.json` plugin convention all work in Coda.
6
+
7
+ ---
8
+
9
+ ## Quick start
10
+
11
+ Add a `hooks` key to `~/.coda/config.json` (global) or `.coda/config.json` (project):
12
+
13
+ ```jsonc
14
+ {
15
+ "hooks": {
16
+ "PreToolUse": [
17
+ {
18
+ "matcher": "Bash",
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "echo 'About to run a Bash command' >> ~/.coda/hooks.log"
23
+ }
24
+ ]
25
+ }
26
+ ]
27
+ }
28
+ }
29
+ ```
30
+
31
+ Restart Coda (or run `/reload-hooks`) for the change to take effect.
32
+
33
+ ---
34
+
35
+ ## Where hooks are configured
36
+
37
+ | Source | Location | Priority |
38
+ | --- | --- | --- |
39
+ | **Project hooks** | `.coda/config.json` → `hooks` key | Highest — run first |
40
+ | **Global hooks** | `~/.coda/config.json` → `hooks` key | Second |
41
+ | **Plugin hooks** | `hooks/hooks.json` inside an installed plugin | Lowest — run last |
42
+
43
+ When the same hook command appears in more than one source for the same event and matcher, only the highest-priority copy runs (deduplication by `event + matcher + type + command/url`).
44
+
45
+ ---
46
+
47
+ ## Hook events
48
+
49
+ Each event fires at a specific point in the session lifecycle.
50
+
51
+ ### Tool events
52
+
53
+ | Event | When it fires | Can block? |
54
+ | --- | --- | --- |
55
+ | `PreToolUse` | Before a tool executes | **Yes** — return `decision: "block"` or exit code 2 |
56
+ | `PostToolUse` | After a tool succeeds | No — can inject `additionalContext` |
57
+ | `PostToolUseFailure` | After a tool throws an error | No — informational |
58
+
59
+ The **matcher** on tool events is tested against the **tool name**. g-coda's runtime tool names are lower-case — `bash`, `write`, `edit`, `read`, `glob`, `grep` — but exact / pipe-separated matchers are **case-insensitive**, so Claude Code's capitalized names (`"Bash"`) also match. (Regex matchers, e.g. `"^mcp__.*"`, are matched case-sensitively.)
60
+
61
+ ### Session events
62
+
63
+ | Event | When it fires | Can block? |
64
+ | --- | --- | --- |
65
+ | `SessionStart` | When a session is created | No |
66
+ | `SessionEnd` | When a session ends | No |
67
+ | `Stop` | When the agent loop finishes a turn | No |
68
+ | `PreCompact` | Before context compaction runs | **Yes** — return `decision: "block"` to cancel |
69
+ | `PostCompact` | After context compaction completes | No |
70
+
71
+ ### Prompt events
72
+
73
+ | Event | When it fires | Can block? |
74
+ | --- | --- | --- |
75
+ | `UserPromptSubmit` | When the user submits a message | **Yes** — can block or rewrite the prompt |
76
+
77
+ ---
78
+
79
+ ## Configuration schema
80
+
81
+ ### Full example
82
+
83
+ ```jsonc
84
+ {
85
+ "hooks": {
86
+ "PreToolUse": [
87
+ {
88
+ "matcher": "Bash",
89
+ "hooks": [
90
+ {
91
+ "type": "command",
92
+ "command": ".coda/hooks/validate-bash.sh",
93
+ "timeout": 10
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "matcher": "write|edit",
99
+ "hooks": [
100
+ {
101
+ "type": "command",
102
+ "command": ".coda/hooks/check-file-policy.sh"
103
+ }
104
+ ]
105
+ }
106
+ ],
107
+ "PostToolUse": [
108
+ {
109
+ "matcher": "write|edit",
110
+ "hooks": [
111
+ {
112
+ "type": "command",
113
+ "command": "npx prettier --write \"$(jq -r .tool_input.file_path)\""
114
+ }
115
+ ]
116
+ }
117
+ ],
118
+ "UserPromptSubmit": [
119
+ {
120
+ "hooks": [
121
+ {
122
+ "type": "http",
123
+ "url": "http://localhost:8080/hooks/prompt",
124
+ "timeout": 5
125
+ }
126
+ ]
127
+ }
128
+ ],
129
+ "SessionStart": [
130
+ {
131
+ "hooks": [
132
+ {
133
+ "type": "command",
134
+ "command": "echo \"Session started at $(date)\" >> ~/.coda/sessions.log"
135
+ }
136
+ ]
137
+ }
138
+ ]
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Matcher patterns
144
+
145
+ The `matcher` field filters which tool names (or session sources) trigger the hook. It is optional — omit it or use `"*"` to match everything.
146
+
147
+ | Pattern | Example | Matches |
148
+ | --- | --- | --- |
149
+ | Omitted or `"*"` | — | All values |
150
+ | Exact string (case-insensitive) | `"bash"` / `"Bash"` | The `bash` tool |
151
+ | Pipe-separated list (case-insensitive) | `"write\|edit"` | `write` or `edit` |
152
+ | JavaScript regex (case-sensitive) | `"^mcp__.*"` | Any MCP tool |
153
+
154
+ ### Hook handler fields
155
+
156
+ #### Command hook (`type: "command"`)
157
+
158
+ | Field | Required | Description |
159
+ | --- | --- | --- |
160
+ | `type` | ✅ | `"command"` |
161
+ | `command` | ✅ | The command to run. In **shell form** (no `args`) it is the command string passed to a shell. In **exec form** (`args` present) it is the executable to spawn directly. Receives JSON on stdin. |
162
+ | `args` | — | Argument vector. When present (even `[]`), the hook runs in **exec form**: `command` is spawned directly with `args` as its arguments and **no shell** — each element is passed verbatim (no quoting/globbing). `shell` is ignored. Path placeholders are substituted in each element. |
163
+ | `shell` | — | Shell for **shell form** only (ignored when `args` is set): `"bash"` (→ `bash -c`) or `"powershell"` (→ `powershell -NoProfile -Command`). Defaults to `sh -c`. |
164
+ | `timeout` | — | Seconds before the hook is killed (default: 600). |
165
+ | `if` | — | Permission rule — conditionally skip the hook (see [Conditional execution](#conditional-execution-if-field)). |
166
+ | `statusMessage` | — | Custom spinner label shown while the hook runs. |
167
+ | `async` | — | `true` → run in background; results polled at turn boundaries. |
168
+ | `asyncRewake` | — | `true` → run detached; on exit code 2 the model is re-engaged with the hook's output. |
169
+ | `once` | — | `true` → run once per session then remove (schema accepted; not yet enforced). |
170
+
171
+ #### HTTP hook (`type: "http"`)
172
+
173
+ | Field | Required | Description |
174
+ | --- | --- | --- |
175
+ | `type` | ✅ | `"http"` |
176
+ | `url` | ✅ | URL to POST JSON to. |
177
+ | `headers` | — | Extra request headers. `$VAR` and `${VAR}` references anywhere in a value (e.g. `"Bearer $MY_TOKEN"`) are interpolated from env, but only for names listed in `allowedEnvVars`. References to unlisted variables are replaced with an empty string. |
178
+ | `allowedEnvVars` | — | Env var names whose values may be interpolated into `headers`. |
179
+ | `timeout` | — | Seconds before the request is aborted (default: 600). |
180
+ | `async` | — | `true` → fire-and-forget: the POST is dispatched in the background and does not block the lifecycle event. Async HTTP hooks cannot block or return a decision; the response is logged but not applied. |
181
+ | `if` | — | Permission rule (see [Conditional execution](#conditional-execution-if-field)). |
182
+
183
+ ---
184
+
185
+ ## JSON input (what your hook receives)
186
+
187
+ Coda sends a JSON object on **stdin** (command hooks) or as the **POST body** (HTTP hooks). All events include these base fields:
188
+
189
+ ```jsonc
190
+ {
191
+ "session_id": "abc123",
192
+ "transcript_path": "",
193
+ "cwd": "/home/user/my-project",
194
+ "hook_event_name": "PreToolUse"
195
+ }
196
+ ```
197
+
198
+ ### Tool events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`)
199
+
200
+ ```jsonc
201
+ {
202
+ "session_id": "abc123",
203
+ "transcript_path": "",
204
+ "cwd": "/home/user/my-project",
205
+ "hook_event_name": "PreToolUse",
206
+ "tool_name": "Bash",
207
+ "tool_input": { "command": "git status", "description": "Check git status" },
208
+ "tool_use_id": "toolu_01XYZ",
209
+ // PostToolUse only:
210
+ "tool_response": "On branch main\n...",
211
+ // PostToolUseFailure only:
212
+ "error": "Command not found: gti"
213
+ }
214
+ ```
215
+
216
+ ### `UserPromptSubmit`
217
+
218
+ ```jsonc
219
+ {
220
+ "session_id": "abc123",
221
+ "transcript_path": "",
222
+ "cwd": "/home/user/my-project",
223
+ "hook_event_name": "UserPromptSubmit",
224
+ "prompt": "Refactor the auth module"
225
+ }
226
+ ```
227
+
228
+ ### `SessionStart`
229
+
230
+ ```jsonc
231
+ {
232
+ "session_id": "abc123",
233
+ "transcript_path": "",
234
+ "cwd": "/home/user/my-project",
235
+ "hook_event_name": "SessionStart",
236
+ "source": "startup"
237
+ }
238
+ ```
239
+
240
+ ### `Stop`
241
+
242
+ ```jsonc
243
+ {
244
+ "session_id": "abc123",
245
+ "transcript_path": "",
246
+ "cwd": "/home/user/my-project",
247
+ "hook_event_name": "Stop",
248
+ "stop_reason": "turn_complete"
249
+ }
250
+ ```
251
+
252
+ ---
253
+
254
+ ## JSON output (what your hook can return)
255
+
256
+ Your hook can print a JSON object to **stdout** (command hooks) or return it as the **response body** (HTTP hooks). All fields are optional.
257
+
258
+ ```jsonc
259
+ {
260
+ // Stop the agent entirely (any event)
261
+ "continue": false,
262
+ "stopReason": "Policy violation detected",
263
+
264
+ // Show a warning to the user without stopping
265
+ "systemMessage": "Warning: this file is read-only",
266
+
267
+ // Block the current operation (PreToolUse, UserPromptSubmit, PreCompact)
268
+ "decision": "block",
269
+ "reason": "Bash commands are not allowed in this project",
270
+
271
+ // Event-specific output
272
+ "hookSpecificOutput": {
273
+ "hookEventName": "PreToolUse",
274
+
275
+ // PreToolUse: override the permission decision
276
+ "permissionDecision": "allow", // or "deny" or "ask"
277
+ "permissionDecisionReason": "Approved by policy engine",
278
+
279
+ // PreToolUse: rewrite the tool's input before it runs
280
+ "updatedInput": { "command": "git status --short" },
281
+
282
+ // PostToolUse: append context to the tool result (shown to the model)
283
+ "additionalContext": "Lint passed. No issues found.",
284
+
285
+ // UserPromptSubmit: rewrite the user's prompt
286
+ "updatedPrompt": "Refactor the auth module. Follow the style guide in STYLE.md."
287
+ }
288
+ }
289
+ ```
290
+
291
+ ### Exit codes (command hooks only)
292
+
293
+ | Exit code | Meaning |
294
+ | --- | --- |
295
+ | `0` | Success — stdout is parsed for JSON output |
296
+ | `2` | **Blocking error** — stderr (or stdout) is shown to the user/model; the operation is blocked |
297
+ | Any other | Non-blocking error — execution continues; stderr is logged |
298
+
299
+ ---
300
+
301
+ ## Blocking operations
302
+
303
+ To **block** a tool call or prompt, use either:
304
+
305
+ - **Exit code 2** — the simplest approach; stderr becomes the error message
306
+ - **JSON output** with `"decision": "block"` and an optional `"reason"`
307
+
308
+ ```bash
309
+ #!/bin/bash
310
+ # .coda/hooks/no-rm.sh
311
+ INPUT=$(cat)
312
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
313
+
314
+ if echo "$COMMAND" | grep -qE '\brm\b.*-rf'; then
315
+ echo "Blocked: rm -rf is not allowed" >&2
316
+ exit 2
317
+ fi
318
+ ```
319
+
320
+ ```jsonc
321
+ // config.json
322
+ {
323
+ "hooks": {
324
+ "PreToolUse": [
325
+ {
326
+ "matcher": "Bash",
327
+ "hooks": [{ "type": "command", "command": ".coda/hooks/no-rm.sh" }]
328
+ }
329
+ ]
330
+ }
331
+ }
332
+ ```
333
+
334
+ ---
335
+
336
+ ## Injecting context into tool results
337
+
338
+ A `PostToolUse` hook can append information to the tool result that the model sees as part of the response. Use `hookSpecificOutput.additionalContext`:
339
+
340
+ ```bash
341
+ #!/bin/bash
342
+ # .coda/hooks/lint-after-write.sh
343
+ INPUT=$(cat)
344
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
345
+
346
+ if [ -n "$FILE" ]; then
347
+ RESULT=$(npx eslint "$FILE" --format compact 2>&1)
348
+ if [ -n "$RESULT" ]; then
349
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":\"ESLint: $RESULT\"}}"
350
+ fi
351
+ fi
352
+ ```
353
+
354
+ The model sees the tool result followed by `[Hook] ESLint: ...`.
355
+
356
+ ---
357
+
358
+ ## Rewriting prompts
359
+
360
+ A `UserPromptSubmit` hook can modify the user's message before the model sees it:
361
+
362
+ ```bash
363
+ #!/bin/bash
364
+ INPUT=$(cat)
365
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt')
366
+ ENHANCED="$PROMPT\n\nContext: $(cat STYLE.md 2>/dev/null | head -20)"
367
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"updatedPrompt\":\"$ENHANCED\"}}"
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Conditional execution (`if` field)
373
+
374
+ The `if` field conditionally skips a hook based on the tool name and its input. It uses the same permission rule syntax as Claude Code:
375
+
376
+ ```
377
+ rule ::= toolName | toolName(content)
378
+ ```
379
+
380
+ | Pattern | Example | Matches |
381
+ | --- | --- | --- |
382
+ | Tool name only | `"Bash"` | Any Bash call |
383
+ | Exact content | `"Bash(git add)"` | Only `git add` |
384
+ | Wildcard | `"Bash(git *)"` | Any git subcommand |
385
+ | File glob | `"Edit(src/*.ts)"` | TypeScript files under `src/` |
386
+ | Prefix (legacy) | `"Bash(npm:*)"` | `npm`, `npm install`, `npm run …` |
387
+
388
+ ```jsonc
389
+ {
390
+ "hooks": {
391
+ "PreToolUse": [
392
+ {
393
+ "matcher": "Bash",
394
+ "hooks": [
395
+ {
396
+ "type": "command",
397
+ "command": ".coda/hooks/git-policy.sh",
398
+ "if": "Bash(git push*)"
399
+ }
400
+ ]
401
+ }
402
+ ]
403
+ }
404
+ }
405
+ ```
406
+
407
+ For non-tool events, hooks with an `if` field are silently skipped.
408
+
409
+ ---
410
+
411
+ ## Plugin hooks
412
+
413
+ Plugins can ship a `hooks/hooks.json` file at the plugin root. The format is identical to the `hooks` key in `config.json`, wrapped in a top-level object:
414
+
415
+ ```jsonc
416
+ // hooks/hooks.json
417
+ {
418
+ "description": "Runs lint after every file write",
419
+ "hooks": {
420
+ "PostToolUse": [
421
+ {
422
+ "matcher": "write|edit",
423
+ "hooks": [
424
+ {
425
+ "type": "command",
426
+ "command": "${CODA_PLUGIN_ROOT}/scripts/lint.sh",
427
+ "timeout": 30
428
+ }
429
+ ]
430
+ }
431
+ ]
432
+ }
433
+ }
434
+ ```
435
+
436
+ ### Path placeholders in plugin hooks
437
+
438
+ | Placeholder | Resolves to |
439
+ | --- | --- |
440
+ | `${CODA_PLUGIN_ROOT}` | The plugin's installation directory |
441
+ | `${CODA_PROJECT_DIR}` | The current project directory (cwd) |
442
+
443
+ ---
444
+
445
+ ## Session environment files (`CODA_ENV_FILE`)
446
+
447
+ Hooks can export environment variables that persist across all subsequent Bash tool commands in the session.
448
+
449
+ ### Mode 1 — External env file (process-level)
450
+
451
+ Set `CODA_ENV_FILE` in your shell before launching Coda. Its contents are prepended to every Bash command:
452
+
453
+ ```bash
454
+ export CODA_ENV_FILE=~/.coda/my-env.sh
455
+ coda
456
+ ```
457
+
458
+ ### Mode 2 — Hook-writable env file (hook-level)
459
+
460
+ For `SessionStart` hooks (bash/wsl only), Coda sets `CODA_ENV_FILE` to a writable path before spawning your hook. Write `export` statements to that file; they are sourced before every subsequent Bash command in the session:
461
+
462
+ ```bash
463
+ #!/bin/bash
464
+ # SessionStart hook — activate a Python venv for the whole session
465
+ source .venv/bin/activate
466
+ echo "export VIRTUAL_ENV=$VIRTUAL_ENV" >> "$CODA_ENV_FILE"
467
+ echo "export PATH=$PATH" >> "$CODA_ENV_FILE"
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Async hooks
473
+
474
+ By default hooks run **synchronously** — the lifecycle event waits for every matched hook to complete. Two opt-in async modes are available:
475
+
476
+ ### `async: true` — tracked background
477
+
478
+ The hook runs in the background. Results are polled at the start of each turn and applied (e.g. `additionalContext` is injected into the next tool result).
479
+
480
+ ```jsonc
481
+ {
482
+ "type": "command",
483
+ "command": ".coda/hooks/slow-analysis.sh",
484
+ "async": true,
485
+ "timeout": 30
486
+ }
487
+ ```
488
+
489
+ ### `asyncRewake: true` — detached watcher
490
+
491
+ The hook runs fully detached. If it exits with code 2, its stderr/stdout is injected as a system message that re-engages the model. Useful for background file watchers or integrity checkers.
492
+
493
+ ```jsonc
494
+ {
495
+ "type": "command",
496
+ "command": ".coda/hooks/file-integrity-watcher.sh",
497
+ "asyncRewake": true
498
+ }
499
+ ```
500
+
501
+ ### Runtime async (hook-initiated)
502
+
503
+ A hook can decide at runtime to go async by printing `{"async": true}` as its first stdout line. Coda backgrounds the process immediately and continues.
504
+
505
+ ---
506
+
507
+ ## Disabling all hooks
508
+
509
+ Set `disableAllHooks: true` in `config.json` to disable all hooks globally. Useful in CI environments or when debugging unexpected behavior:
510
+
511
+ ```jsonc
512
+ {
513
+ "disableAllHooks": true
514
+ }
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Slash commands
520
+
521
+ | Command | What it does |
522
+ | --- | --- |
523
+ | `/hooks` | Browse all configured hooks (event → matchers → handlers) |
524
+ | `/hooks list [event]` | Filter the list by event name |
525
+ | `/reload-hooks` | Reload hooks config from disk without restarting the session |
526
+
527
+ ---
528
+
529
+ ## Environment variables available to hooks
530
+
531
+ | Variable | Value |
532
+ | --- | --- |
533
+ | `CODA_PROJECT_DIR` | Current project directory |
534
+ | `CODA_PLUGIN_ROOT` | Plugin installation directory (plugin hooks only) |
535
+ | `CODA_ENV_FILE` | Writable env file path (SessionStart hooks only, bash/wsl) |
536
+
537
+ All other environment variables from the Coda process are also inherited.
538
+
539
+ ---
540
+
541
+ ## Execution model
542
+
543
+ - **All matching hooks run in parallel** (`Promise.all`). There is no guaranteed order between hooks that match the same event.
544
+ - **Mutation conflicts** (`updatedPrompt`, `updatedInput`, `additionalContext`) are resolved by **last-write-wins** across parallel results.
545
+ - **Any `blocked: true` result blocks the event** regardless of what other hooks returned.
546
+ - **Output is capped at 10 000 characters** per hook (stdout and stderr separately).
547
+ - **Default timeout is 600 seconds** per hook. Set `timeout` to a lower value for interactive hooks.
548
+
549
+ ---
550
+
551
+ ## Security considerations
552
+
553
+ - Hooks only run from config files you control (`~/.coda/config.json`, `.coda/config.json`) or plugins you have explicitly installed. They are not sandboxed.
554
+ - HTTP hook headers only interpolate env vars listed in `allowedEnvVars`.
555
+ - Review hook scripts before enabling them, especially from plugins or shared configs.
556
+ - Use `disableAllHooks: true` in environments where hooks should not run.
557
+
558
+ ---
559
+
560
+ ## Common recipes
561
+
562
+ ### Block `git push` in a project
563
+
564
+ ```jsonc
565
+ {
566
+ "hooks": {
567
+ "PreToolUse": [
568
+ {
569
+ "matcher": "Bash",
570
+ "hooks": [
571
+ {
572
+ "type": "command",
573
+ "command": "jq -e '.tool_input.command | test(\"git push\")' > /dev/null && echo 'git push is blocked in this project' >&2 && exit 2 || exit 0",
574
+ "if": "Bash(git push*)"
575
+ }
576
+ ]
577
+ }
578
+ ]
579
+ }
580
+ }
581
+ ```
582
+
583
+ ### Run Prettier after every file write
584
+
585
+ ```jsonc
586
+ {
587
+ "hooks": {
588
+ "PostToolUse": [
589
+ {
590
+ "matcher": "write|edit",
591
+ "hooks": [
592
+ {
593
+ "type": "command",
594
+ "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -n \"$FILE\" ] && npx prettier --write \"$FILE\" 2>/dev/null; exit 0"
595
+ }
596
+ ]
597
+ }
598
+ ]
599
+ }
600
+ }
601
+ ```
602
+
603
+ ### Log every session to a file
604
+
605
+ ```jsonc
606
+ {
607
+ "hooks": {
608
+ "SessionStart": [
609
+ {
610
+ "hooks": [
611
+ {
612
+ "type": "command",
613
+ "command": "echo \"$(date -Iseconds) session=$(jq -r .session_id) cwd=$(jq -r .cwd)\" >> ~/.coda/session-history.log"
614
+ }
615
+ ]
616
+ }
617
+ ]
618
+ }
619
+ }
620
+ ```
621
+
622
+ ### Notify a webhook on every prompt
623
+
624
+ ```jsonc
625
+ {
626
+ "hooks": {
627
+ "UserPromptSubmit": [
628
+ {
629
+ "hooks": [
630
+ {
631
+ "type": "http",
632
+ "url": "https://hooks.example.com/coda-prompt",
633
+ "headers": { "Authorization": "$WEBHOOK_TOKEN" },
634
+ "allowedEnvVars": ["WEBHOOK_TOKEN"],
635
+ "timeout": 3,
636
+ "async": true
637
+ }
638
+ ]
639
+ }
640
+ ]
641
+ }
642
+ }
643
+ ```
644
+
645
+ ### Activate a Python venv for the whole session
646
+
647
+ ```bash
648
+ #!/bin/bash
649
+ # .coda/hooks/activate-venv.sh
650
+ # Used as a SessionStart hook
651
+ if [ -f ".venv/bin/activate" ]; then
652
+ source .venv/bin/activate
653
+ echo "export VIRTUAL_ENV=\"$VIRTUAL_ENV\"" >> "$CODA_ENV_FILE"
654
+ echo "export PATH=\"$PATH\"" >> "$CODA_ENV_FILE"
655
+ fi
656
+ ```
657
+
658
+ ```jsonc
659
+ {
660
+ "hooks": {
661
+ "SessionStart": [
662
+ {
663
+ "hooks": [
664
+ {
665
+ "type": "command",
666
+ "command": ".coda/hooks/activate-venv.sh"
667
+ }
668
+ ]
669
+ }
670
+ ]
671
+ }
672
+ }
673
+ ```
674
+
675
+ ---
676
+
677
+ ## Troubleshooting
678
+
679
+ **Hooks are not running**
680
+ - Check that `disableAllHooks` is not `true` in any config layer.
681
+ - Run `/hooks` to confirm the hook appears in the snapshot.
682
+ - Run `/reload-hooks` after editing config files.
683
+ - Verify the hook script is executable (`chmod +x`).
684
+
685
+ **Hook is blocking unexpectedly**
686
+ - A non-zero exit code other than 2 is non-blocking. Exit code 2 is the blocking signal.
687
+ - Check that your script does not accidentally exit with code 2 on success paths.
688
+
689
+ **Hook output is not reaching the model**
690
+ - `additionalContext` only works on `PostToolUse` hooks. Verify the event name in `hookSpecificOutput.hookEventName`.
691
+ - The hook must exit with code 0 and print valid JSON to stdout.
692
+
693
+ **Async hook results are not appearing**
694
+ - Async hook results are applied at the **start of the next turn**, not immediately.
695
+ - Check that `timeout` is long enough for the hook to complete.
696
+
697
+ ---
698
+
699
+ ## See also
700
+
701
+ - [Configuration](configuration.md) — where `config.json` files live and how they merge
702
+ - [Features](features.md) — plugins, extensions, agents, skills
703
+ - [How to create a Coda extension](create-extension.md) — programmatic hooks via the extension API
704
+ - [Coda tools overview](tools.md) — tool names to use in `matcher` patterns