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.
@@ -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
- def initialize(tool_name:, tool_input:, tool_use_id: nil, **kwargs)
87
- super(hook_event_name: "PreToolUse", **kwargs)
88
- @tool_name = tool_name
89
- @tool_input = tool_input
90
- @tool_use_id = tool_use_id
91
- end
92
- end
93
-
94
- # Input for PostToolUse hook
95
- #
96
- class PostToolUseInput < BaseHookInput
97
- attr_reader :tool_name, :tool_input, :tool_response, :tool_use_id
98
-
99
- def initialize(tool_name:, tool_input:, tool_response:, tool_use_id: nil, **kwargs)
100
- super(hook_event_name: "PostToolUse", **kwargs)
101
- @tool_name = tool_name
102
- @tool_input = tool_input
103
- @tool_response = tool_response
104
- @tool_use_id = tool_use_id
105
- end
106
- end
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
- # Input for PostToolUseFailure hook (TypeScript SDK parity)
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
- def initialize(tool_name:, tool_input:, error:, tool_use_id: nil, is_interrupt: nil, **kwargs)
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
- # Input for Notification hook (TypeScript SDK parity)
136
+ # --- Hook Input Declarations ---
124
137
  #
125
- class NotificationInput < BaseHookInput
126
- attr_reader :message, :title, :notification_type
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
- def initialize(message:, title: nil, notification_type: nil, **kwargs)
129
- super(hook_event_name: "Notification", **kwargs)
130
- @message = message
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
- # Input for UserPromptSubmit hook
137
- #
138
- class UserPromptSubmitInput < BaseHookInput
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
- def initialize(prompt:, **kwargs)
142
- super(hook_event_name: "UserPromptSubmit", **kwargs)
143
- @prompt = prompt
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
- # Input for SessionStart hook (TypeScript SDK parity)
148
- #
149
- class SessionStartInput < BaseHookInput
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
- # @param source [String] One of: "startup", "resume", "clear", "compact"
153
- # @param agent_type [String, nil] Type of agent if running in subagent context
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
- # Input for SessionEnd hook (TypeScript SDK parity)
164
- #
165
- class SessionEndInput < BaseHookInput
166
- attr_reader :reason
162
+ BaseHookInput.define_input "SessionStart",
163
+ required: [ :source ],
164
+ optional: { agent_type: nil, model: nil }
167
165
 
168
- def initialize(reason:, **kwargs)
169
- super(hook_event_name: "SessionEnd", **kwargs)
170
- @reason = reason
171
- end
172
- end
166
+ BaseHookInput.define_input "SessionEnd",
167
+ required: [ :reason ]
173
168
 
174
- # Input for Stop hook
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
- def initialize(stop_hook_active: false, **kwargs)
180
- super(hook_event_name: "Stop", **kwargs)
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
- # Input for SubagentStart hook (TypeScript SDK parity)
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
- def initialize(agent_id:, agent_type:, **kwargs)
191
- super(hook_event_name: "SubagentStart", **kwargs)
192
- @agent_id = agent_id
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
- # Input for SubagentStop hook
198
- #
199
- class SubagentStopInput < BaseHookInput
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
- def initialize(stop_hook_active: false, agent_id: nil, agent_transcript_path: nil, **kwargs)
203
- super(hook_event_name: "SubagentStop", **kwargs)
204
- @stop_hook_active = stop_hook_active
205
- @agent_id = agent_id
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
- # Input for PreCompact hook
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
- # @param trigger [String] One of: "manual", "auto"
216
- # @param custom_instructions [String, nil] Custom instructions for compaction
217
- def initialize(trigger:, custom_instructions: nil, **kwargs)
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
- def initialize(tool_name:, tool_input:, permission_suggestions: nil, **kwargs)
230
- super(hook_event_name: "PermissionRequest", **kwargs)
231
- @tool_name = tool_name
232
- @tool_input = tool_input
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
- # Input for Setup hook (TypeScript SDK parity)
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
- # @param trigger [String] One of: "init", "maintenance"
250
- def initialize(trigger:, **kwargs)
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