claude_hooks 1.2.1 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.md +21 -15
- data/docs/API/COMMON.md +4 -3
- data/docs/API/DIRECTORY_ADDED.md +34 -0
- data/docs/API/NOTIFICATION.md +5 -4
- data/docs/API/PERMISSION_DENIED.md +4 -0
- data/docs/API/PERMISSION_REQUEST.md +3 -3
- data/docs/API/POST_MODEL_SWITCH.md +53 -0
- data/docs/API/POST_TOOL_USE.md +7 -0
- data/docs/API/POST_TOOL_USE_FAILURE.md +1 -0
- data/docs/API/PRE_MODEL_SWITCH.md +67 -0
- data/docs/API/PRE_TOOL_USE.md +1 -0
- data/docs/API/SESSION_START.md +7 -1
- data/docs/API/SETUP.md +3 -3
- data/docs/API/STOP.md +6 -0
- data/docs/API/SUBAGENT_STOP.md +10 -0
- data/docs/API/TASK_CREATED.md +9 -7
- data/docs/API/USER_PROMPT_SUBMIT.md +14 -0
- data/docs/API/WORKTREE_REMOVE.md +9 -4
- data/docs/external/claude-hooks-reference.md +1188 -345
- data/docs/mitts/prd.md +8 -0
- data/docs/mitts/setup.md +5 -3
- data/lib/claude_hooks/base.rb +12 -0
- data/lib/claude_hooks/directory_added.rb +24 -0
- data/lib/claude_hooks/output/base.rb +6 -0
- data/lib/claude_hooks/output/directory_added.rb +21 -0
- data/lib/claude_hooks/output/notification.rb +10 -1
- data/lib/claude_hooks/output/post_model_switch.rb +50 -0
- data/lib/claude_hooks/output/post_tool_batch.rb +10 -0
- data/lib/claude_hooks/output/post_tool_use.rb +23 -5
- data/lib/claude_hooks/output/pre_model_switch.rb +121 -0
- data/lib/claude_hooks/output/pre_tool_use.rb +8 -0
- data/lib/claude_hooks/output/task_created.rb +34 -1
- data/lib/claude_hooks/output/user_prompt_expansion.rb +10 -0
- data/lib/claude_hooks/output/user_prompt_submit.rb +19 -6
- data/lib/claude_hooks/output/worktree_remove.rb +4 -2
- data/lib/claude_hooks/permission_denied.rb +7 -0
- data/lib/claude_hooks/permission_request.rb +10 -1
- data/lib/claude_hooks/post_model_switch.rb +70 -0
- data/lib/claude_hooks/post_tool_use.rb +14 -0
- data/lib/claude_hooks/post_tool_use_failure.rb +7 -0
- data/lib/claude_hooks/pre_model_switch.rb +90 -0
- data/lib/claude_hooks/pre_tool_use.rb +7 -0
- data/lib/claude_hooks/session_start.rb +18 -0
- data/lib/claude_hooks/setup.rb +3 -0
- data/lib/claude_hooks/task_created.rb +17 -0
- data/lib/claude_hooks/user_prompt_submit.rb +26 -5
- data/lib/claude_hooks/version.rb +1 -1
- data/lib/claude_hooks.rb +6 -0
- metadata +11 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cca02ef252dc6564f109b8f4a09e7ef7c107695dbfb76bafbf44313215f1713e
|
|
4
|
+
data.tar.gz: f8d90408c943f7b0a8b91d9d067557bafb015dfa33943a369839badb189c5374
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dba4a8356c7a11faedeb51d5ff8cf03bdf70e365ee0b922f9b11e779d24d13037469d2f3bbf0facedf555bb031a35ddffea06e9043f6375276a4faf9bc394161
|
|
7
|
+
data.tar.gz: b1a5334e3a2001a567063b8b896b4fac0debea9e344f0491569902c96eb31f3a8b793b50b67d1bbb1aa0ba0619d9fb0f5183b70c0146107685aa16f54b3708c6
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,53 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2026-10-14
|
|
9
|
+
|
|
10
|
+
Spec-parity update tracking the Claude Code hooks documentation changes analyzed in issues #61, #62, #63, #65, #69, #70, and #71. (Issue #64 required no changes; #70 is docs-only.)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`PreModelSwitch` hook event** (`ClaudeHooks::PreModelSwitch` / `ClaudeHooks::Output::PreModelSwitch`): runs before a requested model switch (Claude Code v2.1.251+). Decision-control hook modeled on `PreToolUse` — builders `approve!`/`allow!`, `deny!`/`block_switch!`, `ask!` (via `permissionDecision`), plus a top-level `block!(reason)` (`decision: "block"`). Input readers: `from_model`, `to_model`, `requested_model`, `source`, `context_tokens`, `prompt_cache_warm`, `cache_ttl`, `estimated_cache_write_usd`, `pricing`. Output accessors `allowed?`/`denied?`/`blocked?`/`should_ask?`, `permission_decision`, `permission_reason`, `decision`, `reason`. Always exits 0 (JSON API); merge precedence `deny > ask > allow`, a top-level block makes the merged decision `deny`, and the merge only emits a `permissionDecision` when some hook set one.
|
|
15
|
+
- **`PostModelSwitch` hook event** (`ClaudeHooks::PostModelSwitch` / `ClaudeHooks::Output::PostModelSwitch`): runs after the session's model changes (Claude Code v2.1.251+). Context-only (can't block); builder `add_additional_context!` / `add_context!`, output accessor `additional_context`. Same input readers as `PreModelSwitch`, with extra `source` values `"auto"`/`"resume"`.
|
|
16
|
+
- **`PostToolUse#classifier_context!(note)`** and **`Output::PostToolUse#classifier_context`**: set/read `hookSpecificOutput.classifierContext`, a note for the auto-mode classifier (Claude Code v2.1.236+). Carried through multi-handler merge.
|
|
17
|
+
- **`TaskCreated#block!(reason)`** / **`allow!`** and **`Output::TaskCreated#decision`/`#reason`/`#blocked?`**: block task creation via a top-level `decision: "block"` (the `reason` is returned to Claude as the task-creation error).
|
|
18
|
+
- **`scratchpad_dir` common input reader** on `ClaudeHooks::Base` (Claude Code v2.1.257+). Not added to `COMMON_INPUT_FIELDS` — it is version-gated and often absent, so it never triggers the missing-field warning.
|
|
19
|
+
- **`mcp_server` input reader** on `PreToolUse`, `PermissionRequest`, `PostToolUse`, `PostToolUseFailure`, and `PermissionDenied` (Claude Code v2.1.274+): for MCP tools, a hash with the server's `name` and `source` (where its definition came from). `nil` for non-MCP tools; not added to `input_fields`.
|
|
20
|
+
- **`UserPromptSubmit` session title and prompt suppression**: `session_title` input reader; `session_title!(title)` and `suppress_original_prompt!` builders (`hookSpecificOutput.sessionTitle` / `suppressOriginalPrompt`); `output.session_title` / `output.suppress_original_prompt?` accessors, both carried through merge (last title wins; suppression wins if any hook asks). `suppress_original_prompt!` keeps a blocked prompt's text out of the block message only; the text can still appear in the session transcript and prompt history.
|
|
21
|
+
- **`SessionStart` resume/fork cost fields**: readers `seconds_since_last_response`, `context_tokens`, `prompt_cache_likely_expired`, `estimated_cache_write_usd` (present when `source` is `"resume"`/`"fork"`).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **`Notification` now always exits 0.** Claude Code ignores a Notification hook's exit code and stderr, so `Output::Notification` no longer exits 2 on `continue: false` — it always exits 0 and writes JSON to stdout, keeping the `terminal_sequence!` (desktop-notification) pattern working.
|
|
26
|
+
- **`WorktreeRemove` exit code now reports failure.** Upstream reversed the contract: a non-zero exit now fails the worktree removal when the directory still exists (previously the exit code was ignored). With a `WorktreeRemove` hook configured, the hook itself does the cleanup (exit 0 means "counts as removed") and its JSON output is discarded. `Output::WorktreeRemove#exit_code` now follows `continue` (0/2) instead of hard-coding 0; use `prevent_continue!(reason)` to block. Note that a `WorktreeRemove` hook that raises now also leaves the worktree on disk, since `CLI.run_hook` exits 1 on errors and Claude Code fails the removal on any non-zero exit.
|
|
27
|
+
- **`UserPromptSubmit#add_additional_context!` / `#empty_additional_context!` no longer wipe other `hookSpecificOutput` fields**, so they compose with the new title/suppression builders in any order. Output for existing single-call usage is unchanged.
|
|
28
|
+
- **`TaskCreated` `continue: false` is ignored** by Claude Code; block via the new `block!` (top-level `decision`) or exit 2 instead.
|
|
29
|
+
- **Docs**: `Stop`/`SubagentStop` input tables now list the inherited `last_assistant_message`, `background_tasks`, and `session_crons` readers; notes added for the stop-hook continuation cap (`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`), SubagentStop firing for internal agents, and expanded pasted content in `UserPromptSubmit#prompt`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **`Output::PostToolUse.merge` no longer drops rewritten tool output.** `updatedToolOutput` / `updatedMCPToolOutput` (from `update_tool_output!` / `update_mcp_tool_output!`) were lost when merged with any other output, so e.g. a redacting hook's rewrite was silently discarded when composed with a context hook. The last non-nil rewrite now wins, as with `PreToolUse`'s `updatedInput`.
|
|
34
|
+
- **`additionalContext` survives merging on `PreToolUse`, `PostToolBatch`, and `UserPromptExpansion`.** These merges dropped it; contexts are now joined with `"\n\n"` like the other context-carrying events.
|
|
35
|
+
- **`PermissionRequest` no longer warns about a missing `tool_use_id`.** Claude Code doesn't send that field to PermissionRequest hooks, so it was removed from `input_fields` (and the README/API docs); every real invocation used to log a spurious missing-field warning.
|
|
36
|
+
|
|
37
|
+
### Deprecated
|
|
38
|
+
|
|
39
|
+
- **`suppressOutput` is a no-op.** `suppress_output!` / `show_output!` / `output.suppress_output?` remain callable for backward compatibility, but Claude Code accepts the field without acting on it.
|
|
40
|
+
- **`Setup` output is discarded.** `Setup#add_additional_context!` remains callable but Claude Code discards all Setup JSON output; use a `SessionStart` hook to inject context instead.
|
|
41
|
+
- **`PermissionRequest#tool_use_id`** always returns `nil` (Claude Code doesn't send the field); kept callable for backward compatibility.
|
|
42
|
+
|
|
43
|
+
### Notes
|
|
44
|
+
|
|
45
|
+
- Total hook event classes: 33 (was 31).
|
|
46
|
+
- No DSL methods removed — deprecated helpers stay callable as no-ops.
|
|
47
|
+
- All test files pass (`ruby test/run_all_tests.rb`). Two existing tests that used `Notification` as an "exits 2 on `continue: false`" example were retargeted to `PreCompact`, and the `WorktreeRemove` exit-code test was rewritten for the new blocking contract.
|
|
48
|
+
|
|
49
|
+
## [1.3.0] - 2026-08-15
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **`DirectoryAdded` hook event** (`ClaudeHooks::DirectoryAdded` / `ClaudeHooks::Output::DirectoryAdded`): non-blocking hook that runs after a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request. Exposes `directory` and `source` readers plus a `system_message!` builder.
|
|
54
|
+
|
|
8
55
|
## [1.2.1] - 2026-07-15
|
|
9
56
|
|
|
10
57
|
### Added
|
data/README.md
CHANGED
|
@@ -289,12 +289,15 @@ The framework supports the following hook types:
|
|
|
289
289
|
| **[PostCompact](docs/API/POST_COMPACT.md)** | `ClaudeHooks::PostCompact` | Runs after transcript compaction completes |
|
|
290
290
|
| **[ConfigChange](docs/API/CONFIG_CHANGE.md)** | `ClaudeHooks::ConfigChange` | Runs when Claude Code configuration changes; can block it |
|
|
291
291
|
| **[CwdChanged](docs/API/CWD_CHANGED.md)** | `ClaudeHooks::CwdChanged` | Runs when the working directory changes |
|
|
292
|
+
| **[DirectoryAdded](docs/API/DIRECTORY_ADDED.md)** | `ClaudeHooks::DirectoryAdded` | Runs when a working directory is added mid-session |
|
|
292
293
|
| **[FileChanged](docs/API/FILE_CHANGED.md)** | `ClaudeHooks::FileChanged` | Runs when a watched file is created, modified, or deleted |
|
|
293
294
|
| **[InstructionsLoaded](docs/API/INSTRUCTIONS_LOADED.md)** | `ClaudeHooks::InstructionsLoaded` | Runs when a CLAUDE.md instructions file is loaded |
|
|
294
295
|
| **[Elicitation](docs/API/ELICITATION.md)** | `ClaudeHooks::Elicitation` | Runs when an MCP server requests user input |
|
|
295
296
|
| **[ElicitationResult](docs/API/ELICITATION_RESULT.md)** | `ClaudeHooks::ElicitationResult` | Runs after an elicitation response is provided |
|
|
296
297
|
| **[WorktreeCreate](docs/API/WORKTREE_CREATE.md)** | `ClaudeHooks::WorktreeCreate` | Runs when Claude Code creates a git worktree |
|
|
297
|
-
| **[WorktreeRemove](docs/API/WORKTREE_REMOVE.md)** | `ClaudeHooks::WorktreeRemove` | Runs when a git worktree is removed |
|
|
298
|
+
| **[WorktreeRemove](docs/API/WORKTREE_REMOVE.md)** | `ClaudeHooks::WorktreeRemove` | Runs when a git worktree is removed; can block the removal |
|
|
299
|
+
| **[PreModelSwitch](docs/API/PRE_MODEL_SWITCH.md)** | `ClaudeHooks::PreModelSwitch` | Runs before a requested model switch; can allow, deny, ask, or block it |
|
|
300
|
+
| **[PostModelSwitch](docs/API/POST_MODEL_SWITCH.md)** | `ClaudeHooks::PostModelSwitch` | Runs after the session's model changes; adds context (can't block) |
|
|
298
301
|
| **[SessionEnd](docs/API/SESSION_END.md)** | `ClaudeHooks::SessionEnd` | Runs when a Claude Code session ends |
|
|
299
302
|
|
|
300
303
|
## 🚀 Claude Hook Flow
|
|
@@ -393,23 +396,23 @@ The framework supports all existing hook types with their respective input field
|
|
|
393
396
|
|
|
394
397
|
| Hook Type | Input Fields |
|
|
395
398
|
|-----------|--------------|
|
|
396
|
-
| **Common (all hooks)** | `session_id`, `transcript_path`, `cwd`, `hook_event_name`, `permission_mode`, `prompt_id`, `agent_id`, `agent_type`, `effort` |
|
|
397
|
-
| **SessionStart** | `source`, `model`, `session_title` |
|
|
399
|
+
| **Common (all hooks)** | `session_id`, `transcript_path`, `cwd`, `hook_event_name`, `permission_mode`, `prompt_id`, `agent_id`, `agent_type`, `effort`, `scratchpad_dir` |
|
|
400
|
+
| **SessionStart** | `source`, `model`, `session_title` + on resume/fork: `seconds_since_last_response`, `context_tokens`, `prompt_cache_likely_expired`, `estimated_cache_write_usd` |
|
|
398
401
|
| **Setup** | `source` |
|
|
399
|
-
| **UserPromptSubmit** | `prompt` |
|
|
402
|
+
| **UserPromptSubmit** | `prompt`, `session_title` |
|
|
400
403
|
| **UserPromptExpansion** | `expansion_type`, `command_name`, `command_args`, `command_source`, `prompt` |
|
|
401
404
|
| **Notification** | `message`, `notification_type` |
|
|
402
405
|
| **MessageDisplay** | `turn_id`, `message_id`, `index`, `final`, `delta` |
|
|
403
|
-
| **PreToolUse** | `tool_name`, `tool_input`, `tool_use_id` |
|
|
404
|
-
| **PermissionRequest** | `tool_name`, `tool_input`, `
|
|
405
|
-
| **PermissionDenied** | `tool_name`, `tool_input`, `tool_use_id`, `reason` |
|
|
406
|
-
| **PostToolUse** | `tool_name`, `tool_input`, `tool_response`, `tool_use_id` |
|
|
406
|
+
| **PreToolUse** | `tool_name`, `tool_input`, `tool_use_id`, `mcp_server` (MCP tools) |
|
|
407
|
+
| **PermissionRequest** | `tool_name`, `tool_input`, `permission_suggestions`, `mcp_server` (MCP tools) |
|
|
408
|
+
| **PermissionDenied** | `tool_name`, `tool_input`, `tool_use_id`, `reason`, `mcp_server` (MCP tools) |
|
|
409
|
+
| **PostToolUse** | `tool_name`, `tool_input`, `tool_response`, `tool_use_id`, `mcp_server` (MCP tools) |
|
|
407
410
|
| **PostToolBatch** | `tool_calls` |
|
|
408
|
-
| **PostToolUseFailure** | `tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`, `duration_ms` |
|
|
411
|
+
| **PostToolUseFailure** | `tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`, `duration_ms`, `mcp_server` (MCP tools) |
|
|
409
412
|
| **Stop** | `stop_hook_active`, `last_assistant_message`, `background_tasks`, `session_crons` |
|
|
410
413
|
| **StopFailure** | `error`, `error_details`, `last_assistant_message` |
|
|
411
414
|
| **SubagentStart** | *(common only: `agent_id`, `agent_type`)* |
|
|
412
|
-
| **SubagentStop** | `stop_hook_active`, `agent_transcript_path` + common `agent_id`/`agent_type` |
|
|
415
|
+
| **SubagentStop** | `stop_hook_active`, `last_assistant_message`, `background_tasks`, `session_crons`, `agent_transcript_path` + common `agent_id`/`agent_type` |
|
|
413
416
|
| **TaskCreated** | `task_id`, `task_subject`, `task_description`, `teammate_name`, `team_name` |
|
|
414
417
|
| **TaskCompleted** | `task_id`, `task_subject`, `task_description`, `teammate_name`, `team_name` |
|
|
415
418
|
| **TeammateIdle** | `teammate_name`, `team_name` |
|
|
@@ -417,12 +420,15 @@ The framework supports all existing hook types with their respective input field
|
|
|
417
420
|
| **PostCompact** | `trigger`, `compact_summary` |
|
|
418
421
|
| **ConfigChange** | `source`, `file_path` |
|
|
419
422
|
| **CwdChanged** | `old_cwd`, `new_cwd` |
|
|
423
|
+
| **DirectoryAdded** | `directory`, `source` |
|
|
420
424
|
| **FileChanged** | `file_path`, `event` |
|
|
421
425
|
| **InstructionsLoaded** | `file_path`, `load_reason` |
|
|
422
426
|
| **Elicitation** | `mcp_server_name`, `message`, `mode`, `url`, `elicitation_id`, `requested_schema` |
|
|
423
427
|
| **ElicitationResult** | `mcp_server_name`, `action`, `mode`, `elicitation_id`, `content` |
|
|
424
428
|
| **WorktreeCreate** | `name` |
|
|
425
429
|
| **WorktreeRemove** | `worktree_path` |
|
|
430
|
+
| **PreModelSwitch** | `from_model`, `to_model`, `requested_model`, `source`, `context_tokens`, `prompt_cache_warm`, `cache_ttl`, `estimated_cache_write_usd`, `pricing` |
|
|
431
|
+
| **PostModelSwitch** | `from_model`, `to_model`, `requested_model`, `source`, `context_tokens`, `prompt_cache_warm`, `cache_ttl`, `estimated_cache_write_usd`, `pricing` |
|
|
426
432
|
| **SessionEnd** | `reason` |
|
|
427
433
|
|
|
428
434
|
### Hooks API
|
|
@@ -646,11 +652,11 @@ Claude Code hooks support multiple exit codes with different behaviors depending
|
|
|
646
652
|
| SessionEnd | Operation continues<br/><br />Logged to debug only (`--debug`) | Non-blocking error<br/><br />Logged to debug only (`--debug`) | N/A<br/><br />Logged to debug only (`--debug`) |
|
|
647
653
|
|
|
648
654
|
> [!NOTE]
|
|
649
|
-
> The
|
|
650
|
-
> - **Blocking via top-level `decision`** (behave like `PreToolUse`/`Stop`): `UserPromptExpansion`, `PostToolBatch`, `ConfigChange
|
|
651
|
-
> - **Blocking via `exit 2` / `continue: false`** (no `decision` field): `
|
|
652
|
-
> - **JSON-API special** (always `exit 0`, decision in `hookSpecificOutput`): `PermissionDenied`, `Elicitation`, `ElicitationResult`, `WorktreeCreate` (bare-path stdout).
|
|
653
|
-
> - **Non-blocking / context-only** (exit code effectively ignored): `Setup`, `SubagentStart`, `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `FileChanged`, `InstructionsLoaded`, `
|
|
655
|
+
> The additional events follow the same families. Their per-event exit-code behavior is documented on each API page under [`docs/API/`](docs/API/):
|
|
656
|
+
> - **Blocking via top-level `decision`** (behave like `PreToolUse`/`Stop`): `UserPromptExpansion`, `PostToolBatch`, `ConfigChange`, `TaskCreated` (also honors `exit 2`).
|
|
657
|
+
> - **Blocking via `exit 2` / `continue: false`** (no `decision` field): `TaskCompleted`, `TeammateIdle`, `WorktreeRemove` (a non-zero exit now fails the removal).
|
|
658
|
+
> - **JSON-API special** (always `exit 0`, decision in `hookSpecificOutput`): `PermissionDenied`, `Elicitation`, `ElicitationResult`, `WorktreeCreate` (bare-path stdout), `PreModelSwitch` (allow/deny/ask, also honors top-level `decision`/`exit 2`).
|
|
659
|
+
> - **Non-blocking / context-only** (exit code effectively ignored): `Setup`, `SubagentStart`, `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `DirectoryAdded`, `FileChanged`, `InstructionsLoaded`, `MessageDisplay`, `Notification` (exit code **and** stderr ignored), `PostModelSwitch` (stdout added as context).
|
|
654
660
|
|
|
655
661
|
|
|
656
662
|
#### Manually outputing and exiting example with success
|
data/docs/API/COMMON.md
CHANGED
|
@@ -17,6 +17,7 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
17
17
|
| `agent_id` | Get the agent ID (present in subagent contexts) |
|
|
18
18
|
| `agent_type` | Get the agent type (present in subagent contexts) |
|
|
19
19
|
| `effort` | Get the effort level string (reads `effort.level`) |
|
|
20
|
+
| `scratchpad_dir` | Path to the session's scratchpad directory. Absent (`nil`) when there is no scratchpad or the temp dir is unavailable (Claude Code v2.1.257+) |
|
|
20
21
|
| `read_transcript` | Read the transcript file |
|
|
21
22
|
| `transcript` | Alias for `read_transcript` |
|
|
22
23
|
|
|
@@ -27,8 +28,8 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
27
28
|
|--------|-------------|
|
|
28
29
|
| `allow_continue!` | Allow Claude to continue (default) |
|
|
29
30
|
| `prevent_continue!(reason)` | Stop Claude with reason |
|
|
30
|
-
| `suppress_output!` |
|
|
31
|
-
| `show_output!` |
|
|
31
|
+
| `suppress_output!` | **Deprecated / no-op.** Claude Code accepts `suppressOutput` but no longer acts on it. Safe to call; has no effect |
|
|
32
|
+
| `show_output!` | **Deprecated / no-op** (see `suppress_output!`) |
|
|
32
33
|
| `clear_specifics!` | Clear hook-specific output |
|
|
33
34
|
| `system_message!(message)` | Set a system message shown to the user (not to Claude) |
|
|
34
35
|
| `clear_system_message!` | Clear the system message |
|
|
@@ -47,7 +48,7 @@ This object provides helpers to access output data, for merging multiple outputs
|
|
|
47
48
|
| `output_data` | RAW output data accessor |
|
|
48
49
|
| `output.continue?` | Check if Claude should continue processing |
|
|
49
50
|
| `output.stop_reason` | Get the stop reason if continue is false |
|
|
50
|
-
| `output.suppress_output?` | Check
|
|
51
|
+
| `output.suppress_output?` | Check the `suppressOutput` flag (deprecated — Claude Code ignores it) |
|
|
51
52
|
| `output.hook_specific_output` | Get the hook-specific output data |
|
|
52
53
|
| `output.system_message` | Get the system message if any |
|
|
53
54
|
| `output.terminal_sequence` | Get the terminal escape sequence (if set) |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# DirectoryAdded API
|
|
2
|
+
|
|
3
|
+
Available when inheriting from `ClaudeHooks::DirectoryAdded`:
|
|
4
|
+
|
|
5
|
+
Runs after a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request. Non-blocking — the directory is already added when the hook runs.
|
|
6
|
+
|
|
7
|
+
## Input Helpers
|
|
8
|
+
|
|
9
|
+
[📚 Shared input helpers](COMMON.md#input-helpers)
|
|
10
|
+
|
|
11
|
+
| Method | Description |
|
|
12
|
+
|--------|-------------|
|
|
13
|
+
| `directory` | Absolute path of the directory that was added |
|
|
14
|
+
| `source` | How the directory was added: `'slash_command'` for `/add-dir` or `'register_repo_root'` for the SDK control request |
|
|
15
|
+
|
|
16
|
+
## Hook State Helpers
|
|
17
|
+
|
|
18
|
+
[📚 Shared hook state methods](COMMON.md#hook-state-methods)
|
|
19
|
+
|
|
20
|
+
| Method | Description |
|
|
21
|
+
|--------|-------------|
|
|
22
|
+
| `system_message!` | Set a message delivered to Claude as context on the next turn (`slash_command`) or written to the debug log (`register_repo_root`) |
|
|
23
|
+
|
|
24
|
+
## Output Helpers
|
|
25
|
+
|
|
26
|
+
[📚 Shared output helpers](COMMON.md#output-helpers)
|
|
27
|
+
|
|
28
|
+
| Method | Description |
|
|
29
|
+
|--------|-------------|
|
|
30
|
+
| `output.system_message` | The configured system message |
|
|
31
|
+
|
|
32
|
+
## Hook Exit Codes
|
|
33
|
+
|
|
34
|
+
Non-blocking. Exit code is ignored; only `systemMessage` is consumed.
|
data/docs/API/NOTIFICATION.md
CHANGED
|
@@ -11,7 +11,7 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
11
11
|
|--------|-------------|
|
|
12
12
|
| `message` | Get the notification message content |
|
|
13
13
|
| `notification_message` | Alias for `message` |
|
|
14
|
-
| `notification_type` | Get the notification type: `'permission_prompt'`, `'idle_prompt'`, `'auth_success'`, `'elicitation_dialog'` |
|
|
14
|
+
| `notification_type` | Get the notification type: `'permission_prompt'`, `'idle_prompt'`, `'auth_success'`, `'elicitation_dialog'`, `'elicitation_url_dialog'`, `'elicitation_complete'`, `'elicitation_response'`, `'agent_needs_input'`, `'agent_completed'`, `'quota_auto_resume_fired'`, `'quota_auto_resume_stale'`, `'quota_auto_resume_disabled'` |
|
|
15
15
|
|
|
16
16
|
## Hook State Helpers
|
|
17
17
|
Notifications are outside facing and do not have any specific state to modify.
|
|
@@ -26,8 +26,9 @@ Notifications don't have any specific hook state and thus doesn't have any speci
|
|
|
26
26
|
|
|
27
27
|
## Hook Exit Codes
|
|
28
28
|
|
|
29
|
+
Claude Code **ignores** a Notification hook's exit code and stderr, and discards `systemMessage`/`continue`, while still emitting `terminalSequence`. `ClaudeHooks::Output::Notification` therefore always exits `0` and writes JSON to stdout — even after `prevent_continue!` — so the `terminal_sequence!` (desktop-notification) pattern keeps working.
|
|
30
|
+
|
|
29
31
|
| Exit Code | Behavior |
|
|
30
32
|
|-----------|----------|
|
|
31
|
-
| `exit 0` |
|
|
32
|
-
|
|
|
33
|
-
| `exit 2` | N/A<br/>Logged to debug only (`--debug`) |
|
|
33
|
+
| `exit 0` | Ignored — always the effective exit code |
|
|
34
|
+
| any other | Ignored (exit code and stderr are discarded) |
|
|
@@ -13,6 +13,7 @@ Runs when a permission request is denied. Can request that Claude Code retries t
|
|
|
13
13
|
| `tool_name` | The name of the tool whose permission was denied |
|
|
14
14
|
| `tool_input` | The input data for the tool |
|
|
15
15
|
| `tool_use_id` | The unique identifier for this tool use |
|
|
16
|
+
| `mcp_server` | For MCP tools, a hash with the server's `name` and `source` (where its definition came from: `plugin`, `sdk`, `user`, `project`, …). Base trust decisions on `source`, not the name. `nil` for non-MCP tools (Claude Code v2.1.274+) |
|
|
16
17
|
| `reason` | The reason the permission was denied |
|
|
17
18
|
|
|
18
19
|
## Hook State Helpers
|
|
@@ -35,3 +36,6 @@ Runs when a permission request is denied. Can request that Claude Code retries t
|
|
|
35
36
|
## Hook Exit Codes
|
|
36
37
|
|
|
37
38
|
Exit code is ignored. Output is via `hookSpecificOutput.retry` (JSON API, exit 0 / stdout).
|
|
39
|
+
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> `retry: true` is ignored for "no-verdict" denials (where Claude Code produced no permission verdict to retry). `retry!` remains correct for the cases it applies to.
|
|
@@ -11,7 +11,8 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
11
11
|
|--------|-------------|
|
|
12
12
|
| `tool_name` | Get the name of the tool requiring permission |
|
|
13
13
|
| `tool_input` | Get the input data for the tool |
|
|
14
|
-
| `tool_use_id` |
|
|
14
|
+
| `tool_use_id` | **Deprecated** — Claude Code doesn't send `tool_use_id` to PermissionRequest hooks, so this is always `nil`. Kept for backward compatibility |
|
|
15
|
+
| `mcp_server` | For MCP tools, a hash with the server's `name` and `source` (where its definition came from: `plugin`, `sdk`, `user`, `project`, …). Base trust decisions on `source`, not the name. `nil` for non-MCP tools (Claude Code v2.1.274+) |
|
|
15
16
|
|
|
16
17
|
## Hook State Helpers
|
|
17
18
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
@@ -49,7 +50,7 @@ PermissionRequest hooks use the JSON API with exit code 0 for all permission dec
|
|
|
49
50
|
|-----------|----------|
|
|
50
51
|
| `exit 0` | Permission decision processed<br/>`STDOUT` contains JSON with decision |
|
|
51
52
|
| `exit 1` | Non-blocking error<br/>`STDERR` shown to user |
|
|
52
|
-
| `exit 2` | **
|
|
53
|
+
| `exit 2` | **No longer honored for PermissionRequest** (Claude Code no longer treats it as a denial)<br/>Use the JSON API with exit 0 and a `decision.behavior` object instead |
|
|
53
54
|
|
|
54
55
|
## Example: Basic Permission Guard
|
|
55
56
|
|
|
@@ -63,7 +64,6 @@ class PermissionGuard < ClaudeHooks::PermissionRequest
|
|
|
63
64
|
|
|
64
65
|
def call
|
|
65
66
|
log "Permission requested for: #{tool_name}"
|
|
66
|
-
log "Tool use ID: #{tool_use_id}"
|
|
67
67
|
log "Permission mode: #{permission_mode}"
|
|
68
68
|
|
|
69
69
|
# Auto-allow safe tools
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# PostModelSwitch API
|
|
2
|
+
|
|
3
|
+
Available when inheriting from `ClaudeHooks::PostModelSwitch`:
|
|
4
|
+
|
|
5
|
+
Runs after the session's model changes (Claude Code v2.1.251+), including changes Claude Code makes on its own (automatic fallback, restoring the model on resume). Use it to give Claude model-specific guidance. It **can't block** — the model has already changed. Its `STDOUT` is added as context for Claude.
|
|
6
|
+
|
|
7
|
+
## Input Helpers
|
|
8
|
+
Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
9
|
+
|
|
10
|
+
[📚 Shared input helpers](COMMON.md#input-helpers)
|
|
11
|
+
|
|
12
|
+
`PostModelSwitch` receives the same fields as [PreModelSwitch](PRE_MODEL_SWITCH.md#input-helpers), with two extra `source` values: `'auto'` (a change Claude Code made on its own) and `'resume'` (the model restored when resuming a session). `requested_model` is `nil` when `source` is `'auto'`.
|
|
13
|
+
|
|
14
|
+
| Method | Description |
|
|
15
|
+
|--------|-------------|
|
|
16
|
+
| `from_model` | Model ID the switch changes from |
|
|
17
|
+
| `to_model` | Model ID the session switched to |
|
|
18
|
+
| `requested_model` | The model the request named (`nil` for `'auto'`; the restored setting for `'resume'`) |
|
|
19
|
+
| `source` | `'command'`, `'picker'`, `'sdk'`, `'auto'`, or `'resume'` |
|
|
20
|
+
| `context_tokens` | Tokens the next request re-sends as its prompt |
|
|
21
|
+
| `prompt_cache_warm` | Whether the prompt cache is likely still warm |
|
|
22
|
+
| `cache_ttl` | Prompt cache lifetime: `'5m'` or `'1h'` |
|
|
23
|
+
| `estimated_cache_write_usd` | Estimated cost (USD) of re-caching `context_tokens` |
|
|
24
|
+
| `pricing` | How `estimated_cache_write_usd` was priced |
|
|
25
|
+
|
|
26
|
+
## Hook State Helpers
|
|
27
|
+
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
28
|
+
|
|
29
|
+
[📚 Shared hook state methods](COMMON.md#hook-state-methods)
|
|
30
|
+
|
|
31
|
+
| Method | Description |
|
|
32
|
+
|--------|-------------|
|
|
33
|
+
| `add_additional_context!(context)` | Add context for Claude after the model changes |
|
|
34
|
+
| `add_context!(context)` | Alias for `add_additional_context!` |
|
|
35
|
+
|
|
36
|
+
## Output Helpers
|
|
37
|
+
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
38
|
+
|
|
39
|
+
[📚 Shared output helpers](COMMON.md#output-helpers)
|
|
40
|
+
|
|
41
|
+
| Method | Description |
|
|
42
|
+
|--------|-------------|
|
|
43
|
+
| `output.additional_context` | Get the additional context that was added |
|
|
44
|
+
|
|
45
|
+
## Hook Exit Codes
|
|
46
|
+
|
|
47
|
+
Context-only — `PostModelSwitch` can't block.
|
|
48
|
+
|
|
49
|
+
| Exit Code | Behavior |
|
|
50
|
+
|-----------|----------|
|
|
51
|
+
| `exit 0` | Operation continues<br/>**`STDOUT` added as context to Claude** |
|
|
52
|
+
| `exit 1` | Non-blocking error<br/>`STDERR` shown to user |
|
|
53
|
+
| `exit 2` | N/A — rendered as a `<hook name> hook error` notice; the session proceeds |
|
data/docs/API/POST_TOOL_USE.md
CHANGED
|
@@ -13,6 +13,7 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
13
13
|
| `tool_input` | Get the input that was passed to the tool |
|
|
14
14
|
| `tool_response` | Get the tool's response/output |
|
|
15
15
|
| `tool_use_id` | Get the unique identifier for this tool use (e.g., `"toolu_01ABC123..."`) |
|
|
16
|
+
| `mcp_server` | For MCP tools, a hash with the server's `name` and `source` (where its definition came from: `plugin`, `sdk`, `user`, `project`, …). Base trust decisions on `source`, not the name. `nil` for non-MCP tools (Claude Code v2.1.274+) |
|
|
16
17
|
|
|
17
18
|
## Hook State Helpers
|
|
18
19
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
@@ -24,6 +25,9 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
24
25
|
| `block_tool!(reason)` | Block the tool result from being used |
|
|
25
26
|
| `approve_tool!(reason)` | Clear any previous block decision (default behavior) |
|
|
26
27
|
| `add_additional_context!(context)` | Add context for Claude to consider after tool use |
|
|
28
|
+
| `update_tool_output!(value)` | Rewrite the tool output shown to Claude |
|
|
29
|
+
| `update_mcp_tool_output!(value)` | Rewrite an MCP tool's output |
|
|
30
|
+
| `classifier_context!(note)` | Add a short note about this call's result for the auto-mode classifier, not for Claude (Claude Code v2.1.236+) |
|
|
27
31
|
|
|
28
32
|
## Output Helpers
|
|
29
33
|
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
@@ -36,6 +40,9 @@ Output helpers provide access to the hook's output data and helper methods for w
|
|
|
36
40
|
| `output.reason` | Get the reason that was set for the decision |
|
|
37
41
|
| `output.blocked?` | Check if the tool result has been blocked |
|
|
38
42
|
| `output.additional_context` | Get the additional context that was added |
|
|
43
|
+
| `output.classifier_context` | Get the note sent to the auto-mode classifier |
|
|
44
|
+
| `output.updated_tool_output` | Get the rewritten tool output (if any) |
|
|
45
|
+
| `output.updated_mcp_tool_output` | Get the rewritten MCP tool output (if any) |
|
|
39
46
|
|
|
40
47
|
## Hook Exit Codes
|
|
41
48
|
|
|
@@ -13,6 +13,7 @@ Runs when a tool call fails. Non-blocking — output and exit code are ignored e
|
|
|
13
13
|
| `tool_name` | The name of the tool that failed |
|
|
14
14
|
| `tool_input` | The input data for the tool |
|
|
15
15
|
| `tool_use_id` | The unique identifier for this tool use |
|
|
16
|
+
| `mcp_server` | For MCP tools, a hash with the server's `name` and `source` (where its definition came from: `plugin`, `sdk`, `user`, `project`, …). Base trust decisions on `source`, not the name. `nil` for non-MCP tools (Claude Code v2.1.274+) |
|
|
16
17
|
| `error` | The error message |
|
|
17
18
|
| `is_interrupt` | Whether the failure was caused by an interrupt (`true`/`false`) |
|
|
18
19
|
| `interrupt?` | Alias for `is_interrupt` |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# PreModelSwitch API
|
|
2
|
+
|
|
3
|
+
Available when inheriting from `ClaudeHooks::PreModelSwitch`:
|
|
4
|
+
|
|
5
|
+
Runs before Claude Code applies a model switch that you or a client requested (Claude Code v2.1.251+). Use it to block a switch, require confirmation, or show what the switch will cost before it happens. Claude Code doesn't run this hook for switches it makes on its own (automatic fallback, resume) — those reach [PostModelSwitch](POST_MODEL_SWITCH.md) only.
|
|
6
|
+
|
|
7
|
+
## Input Helpers
|
|
8
|
+
Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
9
|
+
|
|
10
|
+
[📚 Shared input helpers](COMMON.md#input-helpers)
|
|
11
|
+
|
|
12
|
+
| Method | Description |
|
|
13
|
+
|--------|-------------|
|
|
14
|
+
| `from_model` | Model ID the switch changes from |
|
|
15
|
+
| `to_model` | Model ID the switch changes to (the matcher compares against its canonical name) |
|
|
16
|
+
| `requested_model` | The model the request named (an alias, a full ID, or `nil` for the default model) |
|
|
17
|
+
| `source` | Where the request came from: `'command'`, `'picker'`, or `'sdk'` |
|
|
18
|
+
| `context_tokens` | Tokens the next request re-sends as its prompt (`0` before the first response) |
|
|
19
|
+
| `prompt_cache_warm` | Whether the current model's prompt cache is likely still warm |
|
|
20
|
+
| `cache_ttl` | Prompt cache lifetime requested for this session: `'5m'` or `'1h'` |
|
|
21
|
+
| `estimated_cache_write_usd` | Estimated cost (USD) of re-caching `context_tokens` on `to_model` |
|
|
22
|
+
| `pricing` | How `estimated_cache_write_usd` was priced: `'configured'`, `'catalog'`, or `'default'` |
|
|
23
|
+
|
|
24
|
+
## Hook State Helpers
|
|
25
|
+
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
26
|
+
|
|
27
|
+
[📚 Shared hook state methods](COMMON.md#hook-state-methods)
|
|
28
|
+
|
|
29
|
+
| Method | Description |
|
|
30
|
+
|--------|-------------|
|
|
31
|
+
| `approve!(reason)` / `allow!(reason)` | Let the switch proceed (skips the warm-cache confirmation) |
|
|
32
|
+
| `deny!(reason)` / `block_switch!(reason)` | Cancel the switch (`permissionDecision: 'deny'`) |
|
|
33
|
+
| `ask!(reason)` | Prompt the user to confirm the switch (`permissionDecision: 'ask'`) |
|
|
34
|
+
| `block!(reason)` | Cancel the switch via a top-level `decision: "block"` (equivalent to exit 2) |
|
|
35
|
+
|
|
36
|
+
`PreModelSwitch` does not accept `defer`, `updatedInput`, or `additionalContext`. Only `/model` in an interactive session can show the `'ask'` prompt; elsewhere `'ask'` is treated as a refusal.
|
|
37
|
+
|
|
38
|
+
## Output Helpers
|
|
39
|
+
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
40
|
+
|
|
41
|
+
[📚 Shared output helpers](COMMON.md#output-helpers)
|
|
42
|
+
|
|
43
|
+
| Method | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `output.allowed?` | Whether the switch was allowed (`permission_decision == 'allow'`) |
|
|
46
|
+
| `output.denied?` | Whether the switch was denied (`permission_decision == 'deny'`) |
|
|
47
|
+
| `output.blocked?` | Whether the switch was blocked (via `deny` **or** top-level `decision: "block"`) |
|
|
48
|
+
| `output.should_ask?` | Whether the user is asked to confirm (`permission_decision == 'ask'`) |
|
|
49
|
+
| `output.permission_decision` | The permission decision: `'allow'`, `'deny'`, or `'ask'` |
|
|
50
|
+
| `output.permission_reason` | The reason for the permission decision |
|
|
51
|
+
| `output.decision` | The top-level decision (`'block'` or `nil`) |
|
|
52
|
+
| `output.reason` | The top-level block reason |
|
|
53
|
+
|
|
54
|
+
When multiple hooks return different decisions, precedence is `deny > ask > allow`. `Output::PreModelSwitch.merge` follows the same order. If any hook calls `block!`, the merged `permissionDecision` becomes `deny`. If no hook set a `permissionDecision`, the merge leaves the field out rather than adding an `allow`.
|
|
55
|
+
|
|
56
|
+
## Hook Exit Codes
|
|
57
|
+
|
|
58
|
+
`PreModelSwitch` uses the JSON API with exit code 0; the `permissionDecision` (or top-level `decision`) controls whether the switch proceeds.
|
|
59
|
+
|
|
60
|
+
| Exit Code | Behavior |
|
|
61
|
+
|-----------|----------|
|
|
62
|
+
| `exit 0` | Decision processed<br/>`STDOUT` contains JSON with the decision |
|
|
63
|
+
| `exit 1` | Non-blocking error<br/>`STDERR` shown to user; the switch applies |
|
|
64
|
+
| `exit 2` | **Cancels the switch**<br/>`STDERR` shown to user |
|
|
65
|
+
|
|
66
|
+
> [!NOTE]
|
|
67
|
+
> A `PreModelSwitch` hook that doesn't respond before its timeout **blocks** the switch (unlike `PreToolUse`). The default timeout for this event is 30 seconds.
|
data/docs/API/PRE_TOOL_USE.md
CHANGED
|
@@ -12,6 +12,7 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
12
12
|
| `tool_name` | Get the name of the tool being used |
|
|
13
13
|
| `tool_input` | Get the input data for the tool |
|
|
14
14
|
| `tool_use_id` | Get the unique identifier for this tool use (e.g., `"toolu_01ABC123..."`) |
|
|
15
|
+
| `mcp_server` | For MCP tools, a hash with the server's `name` and `source` (where its definition came from: `plugin`, `sdk`, `user`, `project`, …). Base trust decisions on `source`, not the name. `nil` for non-MCP tools (Claude Code v2.1.274+) |
|
|
15
16
|
|
|
16
17
|
## Hook State Helpers
|
|
17
18
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
data/docs/API/SESSION_START.md
CHANGED
|
@@ -9,7 +9,13 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
9
9
|
|
|
10
10
|
| Method | Description |
|
|
11
11
|
|--------|-------------|
|
|
12
|
-
| `source` | Get the session start source: `'startup'`, `'resume'`, `'clear'`, or `'
|
|
12
|
+
| `source` | Get the session start source: `'startup'`, `'resume'`, `'clear'`, `'compact'`, or `'fork'` |
|
|
13
|
+
| `model` | The session model (Claude Code doesn't always include it) |
|
|
14
|
+
| `session_title` | The session title, when set |
|
|
15
|
+
| `seconds_since_last_response` | Wall-clock seconds since the last response (present on `resume`/`fork`) |
|
|
16
|
+
| `context_tokens` | Tokens the first request re-sends as its prompt (present on `resume`/`fork`) |
|
|
17
|
+
| `prompt_cache_likely_expired` | Whether the prompt cache is likely expired (present on `resume`/`fork`) |
|
|
18
|
+
| `estimated_cache_write_usd` | Estimated cost of re-caching `context_tokens` (present on `resume`/`fork`) |
|
|
13
19
|
|
|
14
20
|
## Hook State Helpers
|
|
15
21
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
data/docs/API/SETUP.md
CHANGED
|
@@ -18,8 +18,8 @@ Runs once during Claude Code startup (before any session begins). Use it to inje
|
|
|
18
18
|
|
|
19
19
|
| Method | Description |
|
|
20
20
|
|--------|-------------|
|
|
21
|
-
| `add_additional_context!(context)` |
|
|
22
|
-
| `add_context!(context)` | Alias for `add_additional_context!` |
|
|
21
|
+
| `add_additional_context!(context)` | **Deprecated / no-op.** Claude Code now discards all `Setup` JSON output, including `additionalContext`. Use a `SessionStart` hook to inject context instead |
|
|
22
|
+
| `add_context!(context)` | Alias for `add_additional_context!` (also a no-op) |
|
|
23
23
|
|
|
24
24
|
## Output Helpers
|
|
25
25
|
|
|
@@ -33,5 +33,5 @@ Runs once during Claude Code startup (before any session begins). Use it to inje
|
|
|
33
33
|
|
|
34
34
|
| Exit Code | Behavior |
|
|
35
35
|
|-----------|----------|
|
|
36
|
-
| `exit 0` | Continues normally
|
|
36
|
+
| `exit 0` | Continues normally. Claude Code discards all `Setup` output (including stdout context) |
|
|
37
37
|
| `exit 2` | N/A — non-blocking event |
|
data/docs/API/STOP.md
CHANGED
|
@@ -10,6 +10,9 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
10
10
|
| Method | Description |
|
|
11
11
|
|--------|-------------|
|
|
12
12
|
| `stop_hook_active` | Check if Claude Code is already continuing as a result of a stop hook |
|
|
13
|
+
| `last_assistant_message` | Text of Claude's final response for this turn |
|
|
14
|
+
| `background_tasks` | Background tasks still running (array) |
|
|
15
|
+
| `session_crons` | Scheduled session crons (array) |
|
|
13
16
|
|
|
14
17
|
## Hook State Helpers
|
|
15
18
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
@@ -26,6 +29,9 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
26
29
|
| `block!(instructions)` | Alias for `continue_with_instructions!` |
|
|
27
30
|
| `ensure_stopping!` | Allow Claude to stop normally (default behavior) |
|
|
28
31
|
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> Claude Code caps consecutive stop-hook continuations: after 8 in a row it lets the turn end despite the next block. The count resets each time Claude calls a tool. Raise the cap with the `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` environment variable. Check `stop_hook_active` to avoid loops.
|
|
34
|
+
|
|
29
35
|
## Output Helpers
|
|
30
36
|
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
31
37
|
|
data/docs/API/SUBAGENT_STOP.md
CHANGED
|
@@ -10,6 +10,13 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
10
10
|
| Method | Description |
|
|
11
11
|
|--------|-------------|
|
|
12
12
|
| `stop_hook_active` | Check if Claude Code is already continuing as a result of a stop hook |
|
|
13
|
+
| `last_assistant_message` | Text of the subagent's final response (with `SubagentHandback`, its closing text — not the delivered report) |
|
|
14
|
+
| `background_tasks` | Background tasks still running (array, scoped to the parent session) |
|
|
15
|
+
| `session_crons` | Scheduled session crons (array, scoped to the parent session) |
|
|
16
|
+
| `agent_transcript_path` | Path to the subagent's own transcript |
|
|
17
|
+
|
|
18
|
+
> [!NOTE]
|
|
19
|
+
> SubagentStop also fires when Claude Code's own internal agents finish (e.g. prompt suggestions, `/btw` side questions). For those, `agent_type` is the agent the session itself runs as, so don't assume every event is a subagent Claude spawned.
|
|
13
20
|
|
|
14
21
|
## Hook State Helpers
|
|
15
22
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
@@ -26,6 +33,9 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
26
33
|
| `block!(instructions)` | Alias for `continue_with_instructions!` |
|
|
27
34
|
| `ensure_stopping!` | Allow Claude to stop normally (default behavior) |
|
|
28
35
|
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> Claude Code caps consecutive stop-hook continuations: after 8 in a row it lets the turn end despite the next block. The count resets each time Claude calls a tool. Raise the cap with the `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` environment variable. Check `stop_hook_active` to avoid loops.
|
|
38
|
+
|
|
29
39
|
## Output Helpers
|
|
30
40
|
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
31
41
|
|
data/docs/API/TASK_CREATED.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Available when inheriting from `ClaudeHooks::TaskCreated`:
|
|
4
4
|
|
|
5
|
-
Runs when a teammate task is created.
|
|
5
|
+
Runs when a teammate task is created. Block task creation via `block!` (top-level `decision: "block"`) or exit 2. Claude Code **ignores** `continue: false` for this event, so `prevent_continue!` alone no longer blocks unless it results in exit 2.
|
|
6
6
|
|
|
7
7
|
## Input Helpers
|
|
8
8
|
|
|
@@ -22,9 +22,10 @@ Runs when a teammate task is created. Can block Claude from continuing via `prev
|
|
|
22
22
|
|
|
23
23
|
| Method | Description |
|
|
24
24
|
|--------|-------------|
|
|
25
|
-
| `
|
|
25
|
+
| `block!(reason)` | Block task creation via top-level `decision: "block"`; `reason` is returned to Claude as the task-creation error |
|
|
26
|
+
| `allow!` / `unblock!` | Clear a previously set block |
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
Block via `block!` (top-level `decision`) or exit 2. `continue: false` is ignored for this event.
|
|
28
29
|
|
|
29
30
|
## Output Helpers
|
|
30
31
|
|
|
@@ -32,12 +33,13 @@ There is no top-level `decision` field for this event — block via `prevent_con
|
|
|
32
33
|
|
|
33
34
|
| Method | Description |
|
|
34
35
|
|--------|-------------|
|
|
35
|
-
| `output.
|
|
36
|
-
| `output.
|
|
36
|
+
| `output.decision` | The decision (`'block'` or `nil`) |
|
|
37
|
+
| `output.reason` | The block reason returned to Claude |
|
|
38
|
+
| `output.blocked?` | Whether task creation was blocked (`decision == 'block'`) |
|
|
37
39
|
|
|
38
40
|
## Hook Exit Codes
|
|
39
41
|
|
|
40
42
|
| Exit Code | Behavior |
|
|
41
43
|
|-----------|----------|
|
|
42
|
-
| `exit 0` |
|
|
43
|
-
| `exit 2` | Blocks
|
|
44
|
+
| `exit 0` | Task creation proceeds (unless `decision: "block"` is set) |
|
|
45
|
+
| `exit 2` | Blocks task creation |
|
|
@@ -12,6 +12,13 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
12
12
|
| `prompt` | Get the user's prompt text |
|
|
13
13
|
| `user_prompt` | Alias for `prompt` |
|
|
14
14
|
| `current_prompt` | Alias for `prompt` |
|
|
15
|
+
| `session_title` | The session's custom title, when one is set (same meaning as SessionStart's `session_title`) |
|
|
16
|
+
|
|
17
|
+
> [!NOTE]
|
|
18
|
+
> `UserPromptSubmit` also fires on turns Claude Code starts on its own — a scheduled task or `/loop` iteration, a background subagent reporting back, or a message another session sends — so `prompt` isn't always text the user typed.
|
|
19
|
+
|
|
20
|
+
> [!NOTE]
|
|
21
|
+
> Pasted content that collapsed to a `[Pasted text #N]` placeholder arrives expanded in `prompt`. When Claude Code marks pasted text for Claude, it sits between `<pasted_content id="…">` and `</pasted_content id="…">` lines — account for them if you parse the prompt.
|
|
15
22
|
|
|
16
23
|
## Hook State Helpers
|
|
17
24
|
Hook state methods are helpers to modify the hook's internal state (`output_data`) before yielding back to Claude Code.
|
|
@@ -25,6 +32,11 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
25
32
|
| `empty_additional_context!` | Remove additional context |
|
|
26
33
|
| `block_prompt!(reason)` | Block the prompt from processing |
|
|
27
34
|
| `unblock_prompt!` | Unblock a previously blocked prompt |
|
|
35
|
+
| `session_title!(title)` | Set the session title (e.g. name sessions from the prompt content) |
|
|
36
|
+
| `suppress_original_prompt!(value = true)` | When the prompt is blocked (`block_prompt!` or exit 2), leave the prompt text out of the block message |
|
|
37
|
+
|
|
38
|
+
> [!WARNING]
|
|
39
|
+
> By default the block message ends with `Original prompt:` and the submitted text, and is written to the session transcript on disk. `suppress_original_prompt!` removes it from that message only — the text can still appear in other local files such as prompt history, so a blocking hook isn't a way to keep a secret off disk.
|
|
28
40
|
|
|
29
41
|
## Output Helpers
|
|
30
42
|
Output helpers provide access to the hook's output data and helper methods for working with the output state.
|
|
@@ -37,6 +49,8 @@ Output helpers provide access to the hook's output data and helper methods for w
|
|
|
37
49
|
| `output.reason` | Get the reason for the decision |
|
|
38
50
|
| `output.blocked?` | Check if the prompt has been blocked (decision == 'block') |
|
|
39
51
|
| `output.additional_context` | Get the additional context that was added |
|
|
52
|
+
| `output.session_title` | Get the session title that was set (if any) |
|
|
53
|
+
| `output.suppress_original_prompt?` | Whether the prompt text is left out of the block message |
|
|
40
54
|
|
|
41
55
|
## Hook Exit Codes
|
|
42
56
|
|