claude_agent 0.7.7 → 0.7.9
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 +65 -0
- data/README.md +551 -37
- data/SPEC.md +70 -30
- data/lib/claude_agent/client.rb +197 -7
- data/lib/claude_agent/content_blocks.rb +193 -5
- data/lib/claude_agent/control_protocol.rb +111 -11
- data/lib/claude_agent/conversation.rb +248 -0
- data/lib/claude_agent/cumulative_usage.rb +106 -0
- data/lib/claude_agent/event_handler.rb +152 -0
- data/lib/claude_agent/hooks.rb +106 -225
- data/lib/claude_agent/list_sessions.rb +508 -0
- data/lib/claude_agent/mcp/server.rb +3 -3
- data/lib/claude_agent/mcp/tool.rb +4 -4
- data/lib/claude_agent/message_parser.rb +201 -185
- data/lib/claude_agent/messages.rb +86 -13
- data/lib/claude_agent/options.rb +5 -4
- data/lib/claude_agent/permission_queue.rb +87 -0
- data/lib/claude_agent/permission_request.rb +151 -0
- data/lib/claude_agent/permissions.rb +4 -2
- data/lib/claude_agent/query.rb +34 -0
- data/lib/claude_agent/tool_activity.rb +78 -0
- data/lib/claude_agent/turn_result.rb +239 -0
- data/lib/claude_agent/types.rb +29 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +39 -1
- data/sig/claude_agent.rbs +285 -4
- metadata +9 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeAgent
|
|
4
|
+
# Tracks cumulative usage statistics across multiple conversation turns
|
|
5
|
+
#
|
|
6
|
+
# Token counts are summed across all turns. Cost and turn count reflect
|
|
7
|
+
# the session-cumulative values from the most recent result (the CLI
|
|
8
|
+
# already accumulates these across the session).
|
|
9
|
+
#
|
|
10
|
+
# @example Via Client
|
|
11
|
+
# ClaudeAgent::Client.open do |client|
|
|
12
|
+
# client.send_message("Hello")
|
|
13
|
+
# client.receive_response.each { |m| }
|
|
14
|
+
#
|
|
15
|
+
# client.send_message("Follow up")
|
|
16
|
+
# client.receive_response.each { |m| }
|
|
17
|
+
#
|
|
18
|
+
# usage = client.cumulative_usage
|
|
19
|
+
# puts "Tokens: #{usage.input_tokens} in / #{usage.output_tokens} out"
|
|
20
|
+
# puts "Cost: $#{usage.total_cost_usd}"
|
|
21
|
+
# puts "Turns: #{usage.num_turns}"
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @example Standalone
|
|
25
|
+
# tracker = ClaudeAgent::CumulativeUsage.new
|
|
26
|
+
# messages.each { |msg| tracker.track(msg) }
|
|
27
|
+
# puts tracker.to_h
|
|
28
|
+
#
|
|
29
|
+
class CumulativeUsage
|
|
30
|
+
attr_reader :input_tokens, :output_tokens,
|
|
31
|
+
:cache_read_input_tokens, :cache_creation_input_tokens,
|
|
32
|
+
:total_cost_usd, :num_turns,
|
|
33
|
+
:duration_ms, :duration_api_ms
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@mutex = Mutex.new
|
|
37
|
+
@input_tokens = 0
|
|
38
|
+
@output_tokens = 0
|
|
39
|
+
@cache_read_input_tokens = 0
|
|
40
|
+
@cache_creation_input_tokens = 0
|
|
41
|
+
@total_cost_usd = 0.0
|
|
42
|
+
@num_turns = 0
|
|
43
|
+
@duration_ms = 0
|
|
44
|
+
@duration_api_ms = 0
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Update cumulative usage from a message
|
|
48
|
+
#
|
|
49
|
+
# Only processes {ResultMessage} instances; other message types are ignored.
|
|
50
|
+
#
|
|
51
|
+
# @param message [Object] Any message object
|
|
52
|
+
# @return [void]
|
|
53
|
+
def track(message)
|
|
54
|
+
return unless message.is_a?(ResultMessage)
|
|
55
|
+
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
if message.usage
|
|
58
|
+
@input_tokens += message.usage[:input_tokens].to_i
|
|
59
|
+
@output_tokens += message.usage[:output_tokens].to_i
|
|
60
|
+
@cache_read_input_tokens += message.usage[:cache_read_input_tokens].to_i
|
|
61
|
+
@cache_creation_input_tokens += message.usage[:cache_creation_input_tokens].to_i
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Cost and turn count are session-cumulative from the CLI
|
|
65
|
+
@total_cost_usd = message.total_cost_usd if message.total_cost_usd
|
|
66
|
+
@num_turns = message.num_turns if message.num_turns
|
|
67
|
+
|
|
68
|
+
# Durations are per-turn, sum them
|
|
69
|
+
@duration_ms += message.duration_ms.to_i
|
|
70
|
+
@duration_api_ms += message.duration_api_ms.to_i
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Reset all counters to zero
|
|
75
|
+
#
|
|
76
|
+
# @return [void]
|
|
77
|
+
def reset!
|
|
78
|
+
@mutex.synchronize do
|
|
79
|
+
@input_tokens = 0
|
|
80
|
+
@output_tokens = 0
|
|
81
|
+
@cache_read_input_tokens = 0
|
|
82
|
+
@cache_creation_input_tokens = 0
|
|
83
|
+
@total_cost_usd = 0.0
|
|
84
|
+
@num_turns = 0
|
|
85
|
+
@duration_ms = 0
|
|
86
|
+
@duration_api_ms = 0
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @return [Hash] All tracked fields as a hash
|
|
91
|
+
def to_h
|
|
92
|
+
@mutex.synchronize do
|
|
93
|
+
{
|
|
94
|
+
input_tokens: @input_tokens,
|
|
95
|
+
output_tokens: @output_tokens,
|
|
96
|
+
cache_read_input_tokens: @cache_read_input_tokens,
|
|
97
|
+
cache_creation_input_tokens: @cache_creation_input_tokens,
|
|
98
|
+
total_cost_usd: @total_cost_usd,
|
|
99
|
+
num_turns: @num_turns,
|
|
100
|
+
duration_ms: @duration_ms,
|
|
101
|
+
duration_api_ms: @duration_api_ms
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeAgent
|
|
4
|
+
# Dispatches typed events as messages flow through a conversation turn.
|
|
5
|
+
#
|
|
6
|
+
# Register handlers for specific events instead of writing `case` statements
|
|
7
|
+
# over raw message types. Use standalone or via {Client#on}.
|
|
8
|
+
#
|
|
9
|
+
# @example Standalone
|
|
10
|
+
# handler = ClaudeAgent::EventHandler.new
|
|
11
|
+
# handler.on_text { |text| print text }
|
|
12
|
+
# handler.on_tool_use { |tool| puts "Using: #{tool.display_label}" }
|
|
13
|
+
# handler.on_result { |result| puts "Cost: $#{result.total_cost_usd}" }
|
|
14
|
+
#
|
|
15
|
+
# client.receive_response.each { |msg| handler.handle(msg) }
|
|
16
|
+
#
|
|
17
|
+
# @example Via Client
|
|
18
|
+
# client.on_text { |text| print text }
|
|
19
|
+
# client.on_tool_use { |tool| puts tool.display_label }
|
|
20
|
+
# turn = client.send_and_receive("Fix the bug")
|
|
21
|
+
#
|
|
22
|
+
# @example Chaining
|
|
23
|
+
# handler = ClaudeAgent::EventHandler.new
|
|
24
|
+
# .on_text { |text| print text }
|
|
25
|
+
# .on_result { |r| puts "\nDone!" }
|
|
26
|
+
#
|
|
27
|
+
class EventHandler
|
|
28
|
+
# Events:
|
|
29
|
+
# :message — every message (catch-all)
|
|
30
|
+
# :text — AssistantMessage text content
|
|
31
|
+
# :thinking — AssistantMessage thinking content
|
|
32
|
+
# :tool_use — ToolUseBlock or ServerToolUseBlock
|
|
33
|
+
# :tool_result — ToolResultBlock or ServerToolResultBlock, paired with original tool_use
|
|
34
|
+
# :result — ResultMessage (end of turn)
|
|
35
|
+
|
|
36
|
+
def initialize
|
|
37
|
+
@handlers = Hash.new { |h, k| h[k] = [] }
|
|
38
|
+
@pending_tool_uses = {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Register a handler for an event
|
|
42
|
+
#
|
|
43
|
+
# @param event [Symbol] Event name
|
|
44
|
+
# @yield Event-specific arguments
|
|
45
|
+
# @return [self]
|
|
46
|
+
def on(event, &block)
|
|
47
|
+
@handlers[event] << block
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @!method on_message(&block)
|
|
52
|
+
# Register a handler for every message
|
|
53
|
+
# @yield [message] Any message object
|
|
54
|
+
# @return [self]
|
|
55
|
+
|
|
56
|
+
# @!method on_text(&block)
|
|
57
|
+
# Register a handler for assistant text content
|
|
58
|
+
# @yield [String] Text from the AssistantMessage
|
|
59
|
+
# @return [self]
|
|
60
|
+
|
|
61
|
+
# @!method on_thinking(&block)
|
|
62
|
+
# Register a handler for assistant thinking content
|
|
63
|
+
# @yield [String] Thinking from the AssistantMessage
|
|
64
|
+
# @return [self]
|
|
65
|
+
|
|
66
|
+
# @!method on_tool_use(&block)
|
|
67
|
+
# Register a handler for tool use requests
|
|
68
|
+
# @yield [ToolUseBlock, ServerToolUseBlock] The tool use block
|
|
69
|
+
# @return [self]
|
|
70
|
+
|
|
71
|
+
# @!method on_tool_result(&block)
|
|
72
|
+
# Register a handler for tool results, paired with the original request
|
|
73
|
+
# @yield [ToolResultBlock, ToolUseBlock|nil] Result block and matched tool use
|
|
74
|
+
# @return [self]
|
|
75
|
+
|
|
76
|
+
# @!method on_result(&block)
|
|
77
|
+
# Register a handler for the final ResultMessage
|
|
78
|
+
# @yield [ResultMessage] The result
|
|
79
|
+
# @return [self]
|
|
80
|
+
|
|
81
|
+
%i[message text thinking tool_use tool_result result].each do |event|
|
|
82
|
+
define_method(:"on_#{event}") { |&block| on(event, &block) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Dispatch a message to registered handlers
|
|
86
|
+
#
|
|
87
|
+
# @param message [Message] Any SDK message
|
|
88
|
+
# @return [void]
|
|
89
|
+
def handle(message)
|
|
90
|
+
emit(:message, message)
|
|
91
|
+
|
|
92
|
+
case message
|
|
93
|
+
when AssistantMessage
|
|
94
|
+
handle_assistant(message)
|
|
95
|
+
when UserMessage, UserMessageReplay
|
|
96
|
+
handle_user(message)
|
|
97
|
+
when ResultMessage
|
|
98
|
+
emit(:result, message)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Clear turn-level tracking state (pending tool uses)
|
|
103
|
+
#
|
|
104
|
+
# Called automatically between turns when used via Client.
|
|
105
|
+
# Call manually when reusing a standalone handler across turns.
|
|
106
|
+
#
|
|
107
|
+
# @return [void]
|
|
108
|
+
def reset!
|
|
109
|
+
@pending_tool_uses.clear
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Whether any handlers have been registered
|
|
113
|
+
# @return [Boolean]
|
|
114
|
+
def has_handlers?
|
|
115
|
+
@handlers.any? { |_, v| v.any? }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def handle_assistant(message)
|
|
121
|
+
text = message.text
|
|
122
|
+
emit(:text, text) unless text.empty?
|
|
123
|
+
|
|
124
|
+
thinking = message.thinking
|
|
125
|
+
emit(:thinking, thinking) unless thinking.empty?
|
|
126
|
+
|
|
127
|
+
message.content.each do |block|
|
|
128
|
+
case block
|
|
129
|
+
when ToolUseBlock, ServerToolUseBlock
|
|
130
|
+
@pending_tool_uses[block.id] = block
|
|
131
|
+
emit(:tool_use, block)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def handle_user(message)
|
|
137
|
+
return unless message.content.is_a?(Array)
|
|
138
|
+
|
|
139
|
+
message.content.each do |block|
|
|
140
|
+
case block
|
|
141
|
+
when ToolResultBlock, ServerToolResultBlock
|
|
142
|
+
tool_use = @pending_tool_uses.delete(block.tool_use_id)
|
|
143
|
+
emit(:tool_result, block, tool_use)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def emit(event, *args)
|
|
149
|
+
@handlers[event].each { |handler| handler.call(*args) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
data/lib/claude_agent/hooks.rb
CHANGED
|
@@ -19,6 +19,8 @@ module ClaudeAgent
|
|
|
19
19
|
TeammateIdle
|
|
20
20
|
TaskCompleted
|
|
21
21
|
ConfigChange
|
|
22
|
+
WorktreeCreate
|
|
23
|
+
WorktreeRemove
|
|
22
24
|
].freeze
|
|
23
25
|
|
|
24
26
|
# Matcher configuration for hooks
|
|
@@ -64,6 +66,10 @@ module ClaudeAgent
|
|
|
64
66
|
|
|
65
67
|
# Base class for hook input types (TypeScript SDK parity)
|
|
66
68
|
#
|
|
69
|
+
# Subclasses are generated declaratively via {.define_input}, which creates
|
|
70
|
+
# a class with attr_readers, a keyword-argument initializer, and automatic
|
|
71
|
+
# hook_event_name/base field inheritance.
|
|
72
|
+
#
|
|
67
73
|
class BaseHookInput
|
|
68
74
|
attr_reader :hook_event_name, :session_id, :transcript_path, :cwd, :permission_mode
|
|
69
75
|
|
|
@@ -74,255 +80,130 @@ module ClaudeAgent
|
|
|
74
80
|
@cwd = cwd
|
|
75
81
|
@permission_mode = permission_mode
|
|
76
82
|
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Input for PreToolUse hook
|
|
80
|
-
#
|
|
81
|
-
class PreToolUseInput < BaseHookInput
|
|
82
|
-
attr_reader :tool_name, :tool_input, :tool_use_id
|
|
83
|
-
|
|
84
|
-
def initialize(tool_name:, tool_input:, tool_use_id: nil, **kwargs)
|
|
85
|
-
super(hook_event_name: "PreToolUse", **kwargs)
|
|
86
|
-
@tool_name = tool_name
|
|
87
|
-
@tool_input = tool_input
|
|
88
|
-
@tool_use_id = tool_use_id
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Input for PostToolUse hook
|
|
93
|
-
#
|
|
94
|
-
class PostToolUseInput < BaseHookInput
|
|
95
|
-
attr_reader :tool_name, :tool_input, :tool_response, :tool_use_id
|
|
96
|
-
|
|
97
|
-
def initialize(tool_name:, tool_input:, tool_response:, tool_use_id: nil, **kwargs)
|
|
98
|
-
super(hook_event_name: "PostToolUse", **kwargs)
|
|
99
|
-
@tool_name = tool_name
|
|
100
|
-
@tool_input = tool_input
|
|
101
|
-
@tool_response = tool_response
|
|
102
|
-
@tool_use_id = tool_use_id
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Input for PostToolUseFailure hook (TypeScript SDK parity)
|
|
107
|
-
#
|
|
108
|
-
class PostToolUseFailureInput < BaseHookInput
|
|
109
|
-
attr_reader :tool_name, :tool_input, :tool_use_id, :error, :is_interrupt
|
|
110
|
-
|
|
111
|
-
def initialize(tool_name:, tool_input:, error:, tool_use_id: nil, is_interrupt: nil, **kwargs)
|
|
112
|
-
super(hook_event_name: "PostToolUseFailure", **kwargs)
|
|
113
|
-
@tool_name = tool_name
|
|
114
|
-
@tool_input = tool_input
|
|
115
|
-
@tool_use_id = tool_use_id
|
|
116
|
-
@error = error
|
|
117
|
-
@is_interrupt = is_interrupt
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Input for Notification hook (TypeScript SDK parity)
|
|
122
|
-
#
|
|
123
|
-
class NotificationInput < BaseHookInput
|
|
124
|
-
attr_reader :message, :title, :notification_type
|
|
125
|
-
|
|
126
|
-
def initialize(message:, title: nil, notification_type: nil, **kwargs)
|
|
127
|
-
super(hook_event_name: "Notification", **kwargs)
|
|
128
|
-
@message = message
|
|
129
|
-
@title = title
|
|
130
|
-
@notification_type = notification_type
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Input for UserPromptSubmit hook
|
|
135
|
-
#
|
|
136
|
-
class UserPromptSubmitInput < BaseHookInput
|
|
137
|
-
attr_reader :prompt
|
|
138
|
-
|
|
139
|
-
def initialize(prompt:, **kwargs)
|
|
140
|
-
super(hook_event_name: "UserPromptSubmit", **kwargs)
|
|
141
|
-
@prompt = prompt
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Input for SessionStart hook (TypeScript SDK parity)
|
|
146
|
-
#
|
|
147
|
-
class SessionStartInput < BaseHookInput
|
|
148
|
-
attr_reader :source, :agent_type, :model
|
|
149
|
-
|
|
150
|
-
# @param source [String] One of: "startup", "resume", "clear", "compact"
|
|
151
|
-
# @param agent_type [String, nil] Type of agent if running in subagent context
|
|
152
|
-
# @param model [String, nil] Model being used for this session
|
|
153
|
-
def initialize(source:, agent_type: nil, model: nil, **kwargs)
|
|
154
|
-
super(hook_event_name: "SessionStart", **kwargs)
|
|
155
|
-
@source = source
|
|
156
|
-
@agent_type = agent_type
|
|
157
|
-
@model = model
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
83
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
84
|
+
# Define a hook input subclass declaratively
|
|
85
|
+
#
|
|
86
|
+
# Generates a complete input class with attr_readers, a keyword-argument
|
|
87
|
+
# initializer, and automatic hook_event_name forwarding. The generated class
|
|
88
|
+
# is registered as ClaudeAgent::{event_name}Input.
|
|
89
|
+
#
|
|
90
|
+
# @param event_name [String] Hook event name (e.g., "PreToolUse")
|
|
91
|
+
# @param required [Array<Symbol>] Required keyword arguments
|
|
92
|
+
# @param optional [Hash<Symbol, Object>] Optional keyword arguments with defaults
|
|
93
|
+
# @param constants [Hash<Symbol, Object>] Constants to define on the class
|
|
94
|
+
# @param block [Proc] Optional block for additional instance methods
|
|
95
|
+
# @return [Class] The generated subclass
|
|
96
|
+
#
|
|
97
|
+
# @example Simple declaration
|
|
98
|
+
# BaseHookInput.define_input "PreToolUse",
|
|
99
|
+
# required: [:tool_name, :tool_input],
|
|
100
|
+
# optional: { tool_use_id: nil }
|
|
101
|
+
#
|
|
102
|
+
# @example With custom behavior
|
|
103
|
+
# BaseHookInput.define_input "Setup",
|
|
104
|
+
# required: [:trigger] do
|
|
105
|
+
# def init? = trigger == "init"
|
|
106
|
+
# def maintenance? = trigger == "maintenance"
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
def self.define_input(event_name, required: [], optional: {}, constants: {}, &block)
|
|
110
|
+
klass = Class.new(self)
|
|
111
|
+
all_fields = required + optional.keys
|
|
112
|
+
klass.attr_reader(*all_fields)
|
|
113
|
+
|
|
114
|
+
# Build keyword argument signature
|
|
115
|
+
params = required.map { |f| "#{f}:" }
|
|
116
|
+
params += optional.map { |f, default| "#{f}: #{default.inspect}" }
|
|
117
|
+
params << "**kwargs"
|
|
118
|
+
|
|
119
|
+
# Build instance variable assignments
|
|
120
|
+
assignments = all_fields.map { |f| "@#{f} = #{f}" }.join("\n ")
|
|
121
|
+
|
|
122
|
+
klass.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
123
|
+
def initialize(#{params.join(", ")})
|
|
124
|
+
super(hook_event_name: "#{event_name}", **kwargs)
|
|
125
|
+
#{assignments}
|
|
126
|
+
end
|
|
127
|
+
RUBY
|
|
171
128
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
class StopInput < BaseHookInput
|
|
175
|
-
attr_reader :stop_hook_active
|
|
129
|
+
constants.each { |name, value| klass.const_set(name, value) }
|
|
130
|
+
klass.class_eval(&block) if block
|
|
176
131
|
|
|
177
|
-
|
|
178
|
-
super(hook_event_name: "Stop", **kwargs)
|
|
179
|
-
@stop_hook_active = stop_hook_active
|
|
132
|
+
ClaudeAgent.const_set("#{event_name}Input", klass)
|
|
180
133
|
end
|
|
181
134
|
end
|
|
182
135
|
|
|
183
|
-
#
|
|
136
|
+
# --- Hook Input Declarations ---
|
|
184
137
|
#
|
|
185
|
-
|
|
186
|
-
|
|
138
|
+
# Each declaration generates a complete input class with:
|
|
139
|
+
# - attr_readers for all fields
|
|
140
|
+
# - initialize with required/optional keyword arguments
|
|
141
|
+
# - Automatic hook_event_name and base field inheritance
|
|
187
142
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
@agent_type = agent_type
|
|
192
|
-
end
|
|
193
|
-
end
|
|
143
|
+
BaseHookInput.define_input "PreToolUse",
|
|
144
|
+
required: [ :tool_name, :tool_input ],
|
|
145
|
+
optional: { tool_use_id: nil }
|
|
194
146
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
attr_reader :stop_hook_active, :agent_id, :agent_transcript_path
|
|
147
|
+
BaseHookInput.define_input "PostToolUse",
|
|
148
|
+
required: [ :tool_name, :tool_input, :tool_response ],
|
|
149
|
+
optional: { tool_use_id: nil }
|
|
199
150
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
@agent_id = agent_id
|
|
204
|
-
@agent_transcript_path = agent_transcript_path
|
|
205
|
-
end
|
|
206
|
-
end
|
|
151
|
+
BaseHookInput.define_input "PostToolUseFailure",
|
|
152
|
+
required: [ :tool_name, :tool_input, :error ],
|
|
153
|
+
optional: { tool_use_id: nil, is_interrupt: nil }
|
|
207
154
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
attr_reader :trigger, :custom_instructions
|
|
155
|
+
BaseHookInput.define_input "Notification",
|
|
156
|
+
required: [ :message ],
|
|
157
|
+
optional: { title: nil, notification_type: nil }
|
|
212
158
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def initialize(trigger:, custom_instructions: nil, **kwargs)
|
|
216
|
-
super(hook_event_name: "PreCompact", **kwargs)
|
|
217
|
-
@trigger = trigger
|
|
218
|
-
@custom_instructions = custom_instructions
|
|
219
|
-
end
|
|
220
|
-
end
|
|
159
|
+
BaseHookInput.define_input "UserPromptSubmit",
|
|
160
|
+
required: [ :prompt ]
|
|
221
161
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
attr_reader :tool_name, :tool_input, :permission_suggestions
|
|
162
|
+
BaseHookInput.define_input "SessionStart",
|
|
163
|
+
required: [ :source ],
|
|
164
|
+
optional: { agent_type: nil, model: nil }
|
|
226
165
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@tool_name = tool_name
|
|
230
|
-
@tool_input = tool_input
|
|
231
|
-
@permission_suggestions = permission_suggestions
|
|
232
|
-
end
|
|
233
|
-
end
|
|
166
|
+
BaseHookInput.define_input "SessionEnd",
|
|
167
|
+
required: [ :reason ]
|
|
234
168
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
# Triggered during initial setup or maintenance operations.
|
|
238
|
-
#
|
|
239
|
-
# @example
|
|
240
|
-
# input = SetupInput.new(trigger: "init", session_id: "abc-123")
|
|
241
|
-
# input.trigger # => "init"
|
|
242
|
-
# input.init? # => true
|
|
243
|
-
#
|
|
244
|
-
class SetupInput < BaseHookInput
|
|
245
|
-
attr_reader :trigger
|
|
169
|
+
BaseHookInput.define_input "Stop",
|
|
170
|
+
optional: { stop_hook_active: false }
|
|
246
171
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
super(hook_event_name: "Setup", **kwargs)
|
|
250
|
-
@trigger = trigger
|
|
251
|
-
end
|
|
172
|
+
BaseHookInput.define_input "SubagentStart",
|
|
173
|
+
required: [ :agent_id, :agent_type ]
|
|
252
174
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def init?
|
|
256
|
-
trigger == "init"
|
|
257
|
-
end
|
|
175
|
+
BaseHookInput.define_input "SubagentStop",
|
|
176
|
+
optional: { stop_hook_active: false, agent_id: nil, agent_transcript_path: nil }
|
|
258
177
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
trigger == "maintenance"
|
|
263
|
-
end
|
|
264
|
-
end
|
|
178
|
+
BaseHookInput.define_input "PreCompact",
|
|
179
|
+
required: [ :trigger ],
|
|
180
|
+
optional: { custom_instructions: nil }
|
|
265
181
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
#
|
|
270
|
-
class TeammateIdleInput < BaseHookInput
|
|
271
|
-
attr_reader :teammate_name, :team_name
|
|
182
|
+
BaseHookInput.define_input "PermissionRequest",
|
|
183
|
+
required: [ :tool_name, :tool_input ],
|
|
184
|
+
optional: { permission_suggestions: nil }
|
|
272
185
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
@teammate_name = teammate_name
|
|
278
|
-
@team_name = team_name
|
|
186
|
+
BaseHookInput.define_input "Setup",
|
|
187
|
+
required: [ :trigger ] do
|
|
188
|
+
def init? = trigger == "init"
|
|
189
|
+
def maintenance? = trigger == "maintenance"
|
|
279
190
|
end
|
|
280
|
-
end
|
|
281
191
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
# Fired when a configuration file changes.
|
|
285
|
-
#
|
|
286
|
-
# @example
|
|
287
|
-
# input = ConfigChangeInput.new(
|
|
288
|
-
# source: "user_settings",
|
|
289
|
-
# file_path: "~/.claude/settings.json",
|
|
290
|
-
# session_id: "sess-123"
|
|
291
|
-
# )
|
|
292
|
-
#
|
|
293
|
-
class ConfigChangeInput < BaseHookInput
|
|
294
|
-
attr_reader :source, :file_path
|
|
192
|
+
BaseHookInput.define_input "TeammateIdle",
|
|
193
|
+
required: [ :teammate_name, :team_name ]
|
|
295
194
|
|
|
296
|
-
|
|
195
|
+
BaseHookInput.define_input "TaskCompleted",
|
|
196
|
+
required: [ :task_id, :task_subject ],
|
|
197
|
+
optional: { task_description: nil, teammate_name: nil, team_name: nil }
|
|
297
198
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
@source = source
|
|
303
|
-
@file_path = file_path
|
|
304
|
-
end
|
|
305
|
-
end
|
|
199
|
+
BaseHookInput.define_input "ConfigChange",
|
|
200
|
+
required: [ :source ],
|
|
201
|
+
optional: { file_path: nil },
|
|
202
|
+
constants: { SOURCES: %w[user_settings project_settings local_settings policy_settings skills].freeze }
|
|
306
203
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
# Fired when a task completes.
|
|
310
|
-
#
|
|
311
|
-
class TaskCompletedInput < BaseHookInput
|
|
312
|
-
attr_reader :task_id, :task_subject, :task_description, :teammate_name, :team_name
|
|
204
|
+
BaseHookInput.define_input "WorktreeCreate",
|
|
205
|
+
required: [ :name ]
|
|
313
206
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
# @param task_description [String, nil] Description of the completed task
|
|
317
|
-
# @param teammate_name [String, nil] Name of the teammate that completed the task
|
|
318
|
-
# @param team_name [String, nil] Name of the team
|
|
319
|
-
def initialize(task_id:, task_subject:, task_description: nil, teammate_name: nil, team_name: nil, **kwargs)
|
|
320
|
-
super(hook_event_name: "TaskCompleted", **kwargs)
|
|
321
|
-
@task_id = task_id
|
|
322
|
-
@task_subject = task_subject
|
|
323
|
-
@task_description = task_description
|
|
324
|
-
@teammate_name = teammate_name
|
|
325
|
-
@team_name = team_name
|
|
326
|
-
end
|
|
327
|
-
end
|
|
207
|
+
BaseHookInput.define_input "WorktreeRemove",
|
|
208
|
+
required: [ :worktree_path ]
|
|
328
209
|
end
|