claude_agent 0.7.8 → 0.7.10
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 +73 -0
- data/README.md +603 -47
- data/SPEC.md +92 -28
- data/lib/claude_agent/client.rb +181 -7
- data/lib/claude_agent/content_blocks.rb +193 -5
- data/lib/claude_agent/control_protocol.rb +97 -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/get_session_messages.rb +236 -0
- data/lib/claude_agent/hooks.rb +104 -253
- data/lib/claude_agent/list_sessions.rb +398 -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/session.rb +71 -3
- data/lib/claude_agent/session_message_relation.rb +59 -0
- data/lib/claude_agent/session_paths.rb +120 -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 +45 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +58 -2
- data/sig/claude_agent.rbs +336 -7
- metadata +12 -1
data/lib/claude_agent/hooks.rb
CHANGED
|
@@ -66,6 +66,10 @@ module ClaudeAgent
|
|
|
66
66
|
|
|
67
67
|
# Base class for hook input types (TypeScript SDK parity)
|
|
68
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
|
+
#
|
|
69
73
|
class BaseHookInput
|
|
70
74
|
attr_reader :hook_event_name, :session_id, :transcript_path, :cwd, :permission_mode
|
|
71
75
|
|
|
@@ -76,283 +80,130 @@ module ClaudeAgent
|
|
|
76
80
|
@cwd = cwd
|
|
77
81
|
@permission_mode = permission_mode
|
|
78
82
|
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Input for PreToolUse hook
|
|
82
|
-
#
|
|
83
|
-
class PreToolUseInput < BaseHookInput
|
|
84
|
-
attr_reader :tool_name, :tool_input, :tool_use_id
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
107
128
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class PostToolUseFailureInput < BaseHookInput
|
|
111
|
-
attr_reader :tool_name, :tool_input, :tool_use_id, :error, :is_interrupt
|
|
129
|
+
constants.each { |name, value| klass.const_set(name, value) }
|
|
130
|
+
klass.class_eval(&block) if block
|
|
112
131
|
|
|
113
|
-
|
|
114
|
-
super(hook_event_name: "PostToolUseFailure", **kwargs)
|
|
115
|
-
@tool_name = tool_name
|
|
116
|
-
@tool_input = tool_input
|
|
117
|
-
@tool_use_id = tool_use_id
|
|
118
|
-
@error = error
|
|
119
|
-
@is_interrupt = is_interrupt
|
|
132
|
+
ClaudeAgent.const_set("#{event_name}Input", klass)
|
|
120
133
|
end
|
|
121
134
|
end
|
|
122
135
|
|
|
123
|
-
#
|
|
136
|
+
# --- Hook Input Declarations ---
|
|
124
137
|
#
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
127
142
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
@title = title
|
|
132
|
-
@notification_type = notification_type
|
|
133
|
-
end
|
|
134
|
-
end
|
|
143
|
+
BaseHookInput.define_input "PreToolUse",
|
|
144
|
+
required: [ :tool_name, :tool_input ],
|
|
145
|
+
optional: { tool_use_id: nil }
|
|
135
146
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
attr_reader :prompt
|
|
147
|
+
BaseHookInput.define_input "PostToolUse",
|
|
148
|
+
required: [ :tool_name, :tool_input, :tool_response ],
|
|
149
|
+
optional: { tool_use_id: nil }
|
|
140
150
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
end
|
|
145
|
-
end
|
|
151
|
+
BaseHookInput.define_input "PostToolUseFailure",
|
|
152
|
+
required: [ :tool_name, :tool_input, :error ],
|
|
153
|
+
optional: { tool_use_id: nil, is_interrupt: nil }
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
attr_reader :source, :agent_type, :model
|
|
155
|
+
BaseHookInput.define_input "Notification",
|
|
156
|
+
required: [ :message ],
|
|
157
|
+
optional: { title: nil, notification_type: nil }
|
|
151
158
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# @param model [String, nil] Model being used for this session
|
|
155
|
-
def initialize(source:, agent_type: nil, model: nil, **kwargs)
|
|
156
|
-
super(hook_event_name: "SessionStart", **kwargs)
|
|
157
|
-
@source = source
|
|
158
|
-
@agent_type = agent_type
|
|
159
|
-
@model = model
|
|
160
|
-
end
|
|
161
|
-
end
|
|
159
|
+
BaseHookInput.define_input "UserPromptSubmit",
|
|
160
|
+
required: [ :prompt ]
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
attr_reader :reason
|
|
162
|
+
BaseHookInput.define_input "SessionStart",
|
|
163
|
+
required: [ :source ],
|
|
164
|
+
optional: { agent_type: nil, model: nil }
|
|
167
165
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
@reason = reason
|
|
171
|
-
end
|
|
172
|
-
end
|
|
166
|
+
BaseHookInput.define_input "SessionEnd",
|
|
167
|
+
required: [ :reason ]
|
|
173
168
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
class StopInput < BaseHookInput
|
|
177
|
-
attr_reader :stop_hook_active
|
|
169
|
+
BaseHookInput.define_input "Stop",
|
|
170
|
+
optional: { stop_hook_active: false }
|
|
178
171
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@stop_hook_active = stop_hook_active
|
|
182
|
-
end
|
|
183
|
-
end
|
|
172
|
+
BaseHookInput.define_input "SubagentStart",
|
|
173
|
+
required: [ :agent_id, :agent_type ]
|
|
184
174
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class SubagentStartInput < BaseHookInput
|
|
188
|
-
attr_reader :agent_id, :agent_type
|
|
175
|
+
BaseHookInput.define_input "SubagentStop",
|
|
176
|
+
optional: { stop_hook_active: false, agent_id: nil, agent_transcript_path: nil }
|
|
189
177
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
@agent_type = agent_type
|
|
194
|
-
end
|
|
195
|
-
end
|
|
178
|
+
BaseHookInput.define_input "PreCompact",
|
|
179
|
+
required: [ :trigger ],
|
|
180
|
+
optional: { custom_instructions: nil }
|
|
196
181
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
attr_reader :stop_hook_active, :agent_id, :agent_transcript_path
|
|
182
|
+
BaseHookInput.define_input "PermissionRequest",
|
|
183
|
+
required: [ :tool_name, :tool_input ],
|
|
184
|
+
optional: { permission_suggestions: nil }
|
|
201
185
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
@agent_transcript_path = agent_transcript_path
|
|
186
|
+
BaseHookInput.define_input "Setup",
|
|
187
|
+
required: [ :trigger ] do
|
|
188
|
+
def init? = trigger == "init"
|
|
189
|
+
def maintenance? = trigger == "maintenance"
|
|
207
190
|
end
|
|
208
|
-
end
|
|
209
191
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class PreCompactInput < BaseHookInput
|
|
213
|
-
attr_reader :trigger, :custom_instructions
|
|
192
|
+
BaseHookInput.define_input "TeammateIdle",
|
|
193
|
+
required: [ :teammate_name, :team_name ]
|
|
214
194
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
super(hook_event_name: "PreCompact", **kwargs)
|
|
219
|
-
@trigger = trigger
|
|
220
|
-
@custom_instructions = custom_instructions
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# Input for PermissionRequest hook (TypeScript SDK parity)
|
|
225
|
-
#
|
|
226
|
-
class PermissionRequestInput < BaseHookInput
|
|
227
|
-
attr_reader :tool_name, :tool_input, :permission_suggestions
|
|
195
|
+
BaseHookInput.define_input "TaskCompleted",
|
|
196
|
+
required: [ :task_id, :task_subject ],
|
|
197
|
+
optional: { task_description: nil, teammate_name: nil, team_name: nil }
|
|
228
198
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
@permission_suggestions = permission_suggestions
|
|
234
|
-
end
|
|
235
|
-
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 }
|
|
236
203
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
# Triggered during initial setup or maintenance operations.
|
|
240
|
-
#
|
|
241
|
-
# @example
|
|
242
|
-
# input = SetupInput.new(trigger: "init", session_id: "abc-123")
|
|
243
|
-
# input.trigger # => "init"
|
|
244
|
-
# input.init? # => true
|
|
245
|
-
#
|
|
246
|
-
class SetupInput < BaseHookInput
|
|
247
|
-
attr_reader :trigger
|
|
204
|
+
BaseHookInput.define_input "WorktreeCreate",
|
|
205
|
+
required: [ :name ]
|
|
248
206
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
super(hook_event_name: "Setup", **kwargs)
|
|
252
|
-
@trigger = trigger
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
# Check if this is an init trigger
|
|
256
|
-
# @return [Boolean]
|
|
257
|
-
def init?
|
|
258
|
-
trigger == "init"
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# Check if this is a maintenance trigger
|
|
262
|
-
# @return [Boolean]
|
|
263
|
-
def maintenance?
|
|
264
|
-
trigger == "maintenance"
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
# Input for TeammateIdle hook (TypeScript SDK v0.2.33 parity)
|
|
269
|
-
#
|
|
270
|
-
# Fired when a teammate becomes idle.
|
|
271
|
-
#
|
|
272
|
-
class TeammateIdleInput < BaseHookInput
|
|
273
|
-
attr_reader :teammate_name, :team_name
|
|
274
|
-
|
|
275
|
-
# @param teammate_name [String] Name of the idle teammate
|
|
276
|
-
# @param team_name [String] Name of the team
|
|
277
|
-
def initialize(teammate_name:, team_name:, **kwargs)
|
|
278
|
-
super(hook_event_name: "TeammateIdle", **kwargs)
|
|
279
|
-
@teammate_name = teammate_name
|
|
280
|
-
@team_name = team_name
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
# Input for ConfigChange hook (TypeScript SDK v0.2.49 parity)
|
|
285
|
-
#
|
|
286
|
-
# Fired when a configuration file changes.
|
|
287
|
-
#
|
|
288
|
-
# @example
|
|
289
|
-
# input = ConfigChangeInput.new(
|
|
290
|
-
# source: "user_settings",
|
|
291
|
-
# file_path: "~/.claude/settings.json",
|
|
292
|
-
# session_id: "sess-123"
|
|
293
|
-
# )
|
|
294
|
-
#
|
|
295
|
-
class ConfigChangeInput < BaseHookInput
|
|
296
|
-
attr_reader :source, :file_path
|
|
297
|
-
|
|
298
|
-
SOURCES = %w[user_settings project_settings local_settings policy_settings skills].freeze
|
|
299
|
-
|
|
300
|
-
# @param source [String] One of SOURCES
|
|
301
|
-
# @param file_path [String, nil] Path to the changed file
|
|
302
|
-
def initialize(source:, file_path: nil, **kwargs)
|
|
303
|
-
super(hook_event_name: "ConfigChange", **kwargs)
|
|
304
|
-
@source = source
|
|
305
|
-
@file_path = file_path
|
|
306
|
-
end
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Input for WorktreeCreate hook (TypeScript SDK v0.2.50 parity)
|
|
310
|
-
#
|
|
311
|
-
# Fired when a worktree is created.
|
|
312
|
-
#
|
|
313
|
-
class WorktreeCreateInput < BaseHookInput
|
|
314
|
-
attr_reader :name
|
|
315
|
-
|
|
316
|
-
# @param name [String] Worktree name
|
|
317
|
-
def initialize(name:, **kwargs)
|
|
318
|
-
super(hook_event_name: "WorktreeCreate", **kwargs)
|
|
319
|
-
@name = name
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Input for WorktreeRemove hook (TypeScript SDK v0.2.50 parity)
|
|
324
|
-
#
|
|
325
|
-
# Fired when a worktree is removed.
|
|
326
|
-
#
|
|
327
|
-
class WorktreeRemoveInput < BaseHookInput
|
|
328
|
-
attr_reader :worktree_path
|
|
329
|
-
|
|
330
|
-
# @param worktree_path [String] Path to the worktree
|
|
331
|
-
def initialize(worktree_path:, **kwargs)
|
|
332
|
-
super(hook_event_name: "WorktreeRemove", **kwargs)
|
|
333
|
-
@worktree_path = worktree_path
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
# Input for TaskCompleted hook (TypeScript SDK v0.2.33 parity)
|
|
338
|
-
#
|
|
339
|
-
# Fired when a task completes.
|
|
340
|
-
#
|
|
341
|
-
class TaskCompletedInput < BaseHookInput
|
|
342
|
-
attr_reader :task_id, :task_subject, :task_description, :teammate_name, :team_name
|
|
343
|
-
|
|
344
|
-
# @param task_id [String] ID of the completed task
|
|
345
|
-
# @param task_subject [String] Subject of the completed task
|
|
346
|
-
# @param task_description [String, nil] Description of the completed task
|
|
347
|
-
# @param teammate_name [String, nil] Name of the teammate that completed the task
|
|
348
|
-
# @param team_name [String, nil] Name of the team
|
|
349
|
-
def initialize(task_id:, task_subject:, task_description: nil, teammate_name: nil, team_name: nil, **kwargs)
|
|
350
|
-
super(hook_event_name: "TaskCompleted", **kwargs)
|
|
351
|
-
@task_id = task_id
|
|
352
|
-
@task_subject = task_subject
|
|
353
|
-
@task_description = task_description
|
|
354
|
-
@teammate_name = teammate_name
|
|
355
|
-
@team_name = team_name
|
|
356
|
-
end
|
|
357
|
-
end
|
|
207
|
+
BaseHookInput.define_input "WorktreeRemove",
|
|
208
|
+
required: [ :worktree_path ]
|
|
358
209
|
end
|