claude_hooks 1.3.0 → 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 +41 -0
- data/README.md +19 -15
- data/docs/API/COMMON.md +4 -3
- 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 +973 -294
- 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/output/base.rb +4 -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 +4 -0
- metadata +8 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module ClaudeHooks
|
|
6
|
+
class PreModelSwitch < Base
|
|
7
|
+
def self.hook_type
|
|
8
|
+
'PreModelSwitch'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.input_fields
|
|
12
|
+
%w[from_model to_model requested_model source]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# === INPUT DATA ACCESS ===
|
|
16
|
+
|
|
17
|
+
def from_model
|
|
18
|
+
@input_data['from_model'] || @input_data['fromModel']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_model
|
|
22
|
+
@input_data['to_model'] || @input_data['toModel']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def requested_model
|
|
26
|
+
@input_data['requested_model'] || @input_data['requestedModel']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def source
|
|
30
|
+
@input_data['source']
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def context_tokens
|
|
34
|
+
@input_data['context_tokens'] || @input_data['contextTokens']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def prompt_cache_warm
|
|
38
|
+
@input_data.key?('prompt_cache_warm') ? @input_data['prompt_cache_warm'] : @input_data['promptCacheWarm']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cache_ttl
|
|
42
|
+
@input_data['cache_ttl'] || @input_data['cacheTtl']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def estimated_cache_write_usd
|
|
46
|
+
@input_data['estimated_cache_write_usd'] || @input_data['estimatedCacheWriteUsd']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def pricing
|
|
50
|
+
@input_data['pricing']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# === OUTPUT DATA HELPERS ===
|
|
54
|
+
#
|
|
55
|
+
# PreModelSwitch mirrors PreToolUse's permissionDecision API (allow/deny/ask).
|
|
56
|
+
# It does not accept 'defer', 'updatedInput', or 'additionalContext'.
|
|
57
|
+
|
|
58
|
+
def approve!(reason = '')
|
|
59
|
+
@output_data['hookSpecificOutput'] = {
|
|
60
|
+
'hookEventName' => hook_event_name,
|
|
61
|
+
'permissionDecision' => 'allow',
|
|
62
|
+
'permissionDecisionReason' => reason
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
alias_method :allow!, :approve!
|
|
66
|
+
|
|
67
|
+
def deny!(reason = '')
|
|
68
|
+
@output_data['hookSpecificOutput'] = {
|
|
69
|
+
'hookEventName' => hook_event_name,
|
|
70
|
+
'permissionDecision' => 'deny',
|
|
71
|
+
'permissionDecisionReason' => reason
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
alias_method :block_switch!, :deny!
|
|
75
|
+
|
|
76
|
+
def ask!(reason = '')
|
|
77
|
+
@output_data['hookSpecificOutput'] = {
|
|
78
|
+
'hookEventName' => hook_event_name,
|
|
79
|
+
'permissionDecision' => 'ask',
|
|
80
|
+
'permissionDecisionReason' => reason
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Top-level decision:block also cancels the switch (equivalent to exit 2).
|
|
85
|
+
def block!(reason = '')
|
|
86
|
+
@output_data['decision'] = 'block'
|
|
87
|
+
@output_data['reason'] = reason
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -26,6 +26,13 @@ module ClaudeHooks
|
|
|
26
26
|
@input_data['tool_use_id'] || @input_data['toolUseId']
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# For MCP tools: { 'name' => ..., 'source' => ... } describing where the server's
|
|
30
|
+
# definition came from (e.g. 'plugin', 'sdk', 'user', 'project'). Base trust
|
|
31
|
+
# decisions on 'source', not on the name. nil for non-MCP tools. Requires Claude Code v2.1.274+.
|
|
32
|
+
def mcp_server
|
|
33
|
+
@input_data['mcp_server'] || @input_data['mcpServer']
|
|
34
|
+
end
|
|
35
|
+
|
|
29
36
|
# === OUTPUT DATA HELPERS ===
|
|
30
37
|
|
|
31
38
|
def approve_tool!(reason = '')
|
|
@@ -26,6 +26,24 @@ module ClaudeHooks
|
|
|
26
26
|
@input_data['session_title'] || @input_data['sessionTitle']
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# Optional cost/context fields present when source is "resume" or "fork".
|
|
30
|
+
|
|
31
|
+
def seconds_since_last_response
|
|
32
|
+
@input_data['seconds_since_last_response'] || @input_data['secondsSinceLastResponse']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def context_tokens
|
|
36
|
+
@input_data['context_tokens'] || @input_data['contextTokens']
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def prompt_cache_likely_expired
|
|
40
|
+
@input_data.key?('prompt_cache_likely_expired') ? @input_data['prompt_cache_likely_expired'] : @input_data['promptCacheLikelyExpired']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def estimated_cache_write_usd
|
|
44
|
+
@input_data['estimated_cache_write_usd'] || @input_data['estimatedCacheWriteUsd']
|
|
45
|
+
end
|
|
46
|
+
|
|
29
47
|
# === OUTPUT DATA HELPERS ===
|
|
30
48
|
|
|
31
49
|
def add_additional_context!(context)
|
data/lib/claude_hooks/setup.rb
CHANGED
|
@@ -16,6 +16,9 @@ module ClaudeHooks
|
|
|
16
16
|
@input_data['source']
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# DEPRECATED: Claude Code now discards all Setup JSON output, including
|
|
20
|
+
# hookSpecificOutput.additionalContext. Kept for backward compatibility, but
|
|
21
|
+
# this no longer adds context — use a SessionStart hook to inject context instead.
|
|
19
22
|
def add_additional_context!(context)
|
|
20
23
|
@output_data['hookSpecificOutput'] ||= { 'hookEventName' => hook_event_name }
|
|
21
24
|
@output_data['hookSpecificOutput']['additionalContext'] = context
|
|
@@ -31,5 +31,22 @@ module ClaudeHooks
|
|
|
31
31
|
def team_name
|
|
32
32
|
@input_data['team_name'] || @input_data['teamName']
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
# === OUTPUT DATA HELPERS ===
|
|
36
|
+
#
|
|
37
|
+
# Claude Code ignores `continue: false` for TaskCreated. Block task creation
|
|
38
|
+
# either by exiting 2 or with a top-level decision:block; `reason` is returned
|
|
39
|
+
# to Claude as the task-creation error.
|
|
40
|
+
|
|
41
|
+
def block!(reason = '')
|
|
42
|
+
@output_data['decision'] = 'block'
|
|
43
|
+
@output_data['reason'] = reason
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def allow!
|
|
47
|
+
@output_data['decision'] = nil
|
|
48
|
+
@output_data['reason'] = nil
|
|
49
|
+
end
|
|
50
|
+
alias_method :unblock!, :allow!
|
|
34
51
|
end
|
|
35
52
|
end
|
|
@@ -20,18 +20,39 @@ module ClaudeHooks
|
|
|
20
20
|
alias_method :user_prompt, :prompt
|
|
21
21
|
alias_method :current_prompt, :prompt
|
|
22
22
|
|
|
23
|
+
# The session's custom title, when one is set. Requires Claude Code to send it
|
|
24
|
+
# (same meaning as SessionStart's session_title).
|
|
25
|
+
def session_title
|
|
26
|
+
@input_data['session_title'] || @input_data['sessionTitle']
|
|
27
|
+
end
|
|
28
|
+
|
|
23
29
|
# === OUTPUT DATA HELPERS ===
|
|
24
30
|
|
|
25
31
|
def add_additional_context!(context)
|
|
26
|
-
@output_data['hookSpecificOutput']
|
|
27
|
-
|
|
28
|
-
'additionalContext' => context
|
|
29
|
-
}
|
|
32
|
+
@output_data['hookSpecificOutput'] ||= { 'hookEventName' => hook_event_name }
|
|
33
|
+
@output_data['hookSpecificOutput']['additionalContext'] = context
|
|
30
34
|
end
|
|
31
35
|
alias_method :add_context!, :add_additional_context!
|
|
32
36
|
|
|
33
37
|
def empty_additional_context!
|
|
34
|
-
@output_data['hookSpecificOutput']
|
|
38
|
+
hso = @output_data['hookSpecificOutput']
|
|
39
|
+
return unless hso
|
|
40
|
+
|
|
41
|
+
hso.delete('additionalContext')
|
|
42
|
+
@output_data['hookSpecificOutput'] = nil if hso.keys == ['hookEventName']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def session_title!(title)
|
|
46
|
+
@output_data['hookSpecificOutput'] ||= { 'hookEventName' => hook_event_name }
|
|
47
|
+
@output_data['hookSpecificOutput']['sessionTitle'] = title
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# When the prompt is blocked (block_prompt! or exit 2), leave the prompt text out
|
|
51
|
+
# of the block message shown to the user and written to the transcript. Doesn't
|
|
52
|
+
# remove it from other local files such as prompt history.
|
|
53
|
+
def suppress_original_prompt!(value = true)
|
|
54
|
+
@output_data['hookSpecificOutput'] ||= { 'hookEventName' => hook_event_name }
|
|
55
|
+
@output_data['hookSpecificOutput']['suppressOriginalPrompt'] = value
|
|
35
56
|
end
|
|
36
57
|
|
|
37
58
|
def block_prompt!(reason = '')
|
data/lib/claude_hooks/version.rb
CHANGED
data/lib/claude_hooks.rb
CHANGED
|
@@ -38,6 +38,8 @@ require_relative "claude_hooks/elicitation"
|
|
|
38
38
|
require_relative "claude_hooks/elicitation_result"
|
|
39
39
|
require_relative "claude_hooks/worktree_create"
|
|
40
40
|
require_relative "claude_hooks/directory_added"
|
|
41
|
+
require_relative "claude_hooks/pre_model_switch"
|
|
42
|
+
require_relative "claude_hooks/post_model_switch"
|
|
41
43
|
|
|
42
44
|
# Output classes
|
|
43
45
|
require_relative "claude_hooks/output/base"
|
|
@@ -72,6 +74,8 @@ require_relative "claude_hooks/output/elicitation"
|
|
|
72
74
|
require_relative "claude_hooks/output/elicitation_result"
|
|
73
75
|
require_relative "claude_hooks/output/worktree_create"
|
|
74
76
|
require_relative "claude_hooks/output/directory_added"
|
|
77
|
+
require_relative "claude_hooks/output/pre_model_switch"
|
|
78
|
+
require_relative "claude_hooks/output/post_model_switch"
|
|
75
79
|
|
|
76
80
|
module ClaudeHooks
|
|
77
81
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude_hooks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gabriel Dehan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -83,10 +83,12 @@ files:
|
|
|
83
83
|
- docs/API/PERMISSION_DENIED.md
|
|
84
84
|
- docs/API/PERMISSION_REQUEST.md
|
|
85
85
|
- docs/API/POST_COMPACT.md
|
|
86
|
+
- docs/API/POST_MODEL_SWITCH.md
|
|
86
87
|
- docs/API/POST_TOOL_BATCH.md
|
|
87
88
|
- docs/API/POST_TOOL_USE.md
|
|
88
89
|
- docs/API/POST_TOOL_USE_FAILURE.md
|
|
89
90
|
- docs/API/PRE_COMPACT.md
|
|
91
|
+
- docs/API/PRE_MODEL_SWITCH.md
|
|
90
92
|
- docs/API/PRE_TOOL_USE.md
|
|
91
93
|
- docs/API/SESSION_END.md
|
|
92
94
|
- docs/API/SESSION_START.md
|
|
@@ -146,10 +148,12 @@ files:
|
|
|
146
148
|
- lib/claude_hooks/output/permission_denied.rb
|
|
147
149
|
- lib/claude_hooks/output/permission_request.rb
|
|
148
150
|
- lib/claude_hooks/output/post_compact.rb
|
|
151
|
+
- lib/claude_hooks/output/post_model_switch.rb
|
|
149
152
|
- lib/claude_hooks/output/post_tool_batch.rb
|
|
150
153
|
- lib/claude_hooks/output/post_tool_use.rb
|
|
151
154
|
- lib/claude_hooks/output/post_tool_use_failure.rb
|
|
152
155
|
- lib/claude_hooks/output/pre_compact.rb
|
|
156
|
+
- lib/claude_hooks/output/pre_model_switch.rb
|
|
153
157
|
- lib/claude_hooks/output/pre_tool_use.rb
|
|
154
158
|
- lib/claude_hooks/output/session_end.rb
|
|
155
159
|
- lib/claude_hooks/output/session_start.rb
|
|
@@ -168,10 +172,12 @@ files:
|
|
|
168
172
|
- lib/claude_hooks/permission_denied.rb
|
|
169
173
|
- lib/claude_hooks/permission_request.rb
|
|
170
174
|
- lib/claude_hooks/post_compact.rb
|
|
175
|
+
- lib/claude_hooks/post_model_switch.rb
|
|
171
176
|
- lib/claude_hooks/post_tool_batch.rb
|
|
172
177
|
- lib/claude_hooks/post_tool_use.rb
|
|
173
178
|
- lib/claude_hooks/post_tool_use_failure.rb
|
|
174
179
|
- lib/claude_hooks/pre_compact.rb
|
|
180
|
+
- lib/claude_hooks/pre_model_switch.rb
|
|
175
181
|
- lib/claude_hooks/pre_tool_use.rb
|
|
176
182
|
- lib/claude_hooks/session_end.rb
|
|
177
183
|
- lib/claude_hooks/session_start.rb
|