claude_agent 0.7.10 → 0.7.11
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 +25 -0
- data/README.md +72 -29
- data/SPEC.md +79 -12
- data/lib/claude_agent/client.rb +10 -32
- data/lib/claude_agent/control_protocol.rb +13 -3
- data/lib/claude_agent/conversation.rb +38 -20
- data/lib/claude_agent/event_handler.rb +71 -22
- data/lib/claude_agent/hooks.rb +2 -2
- data/lib/claude_agent/live_tool_activity.rb +136 -0
- data/lib/claude_agent/message_parser.rb +16 -3
- data/lib/claude_agent/messages.rb +15 -5
- data/lib/claude_agent/options.rb +3 -23
- data/lib/claude_agent/query.rb +0 -54
- data/lib/claude_agent/tool_activity_tracker.rb +176 -0
- data/lib/claude_agent/types.rb +16 -5
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +2 -0
- data/sig/claude_agent.rbs +137 -12
- metadata +3 -1
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
module ClaudeAgent
|
|
4
4
|
# Dispatches typed events as messages flow through a conversation turn.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
6
|
+
# Three event layers fire for every message:
|
|
7
|
+
#
|
|
8
|
+
# 1. **Catch-all** — +:message+ fires for every message
|
|
9
|
+
# 2. **Type-based** — +message.type+ fires (e.g. +:assistant+, +:stream_event+, +:status+)
|
|
10
|
+
# 3. **Decomposed** — convenience events for rich content types (+:text+, +:thinking+, etc.)
|
|
11
|
+
#
|
|
12
|
+
# Register handlers for specific events instead of writing +case+ statements
|
|
7
13
|
# over raw message types. Use standalone or via {Client#on}.
|
|
8
14
|
#
|
|
9
15
|
# @example Standalone
|
|
@@ -14,6 +20,11 @@ module ClaudeAgent
|
|
|
14
20
|
#
|
|
15
21
|
# client.receive_response.each { |msg| handler.handle(msg) }
|
|
16
22
|
#
|
|
23
|
+
# @example Type-based events
|
|
24
|
+
# handler.on_stream_event { |evt| handle_stream(evt) }
|
|
25
|
+
# handler.on_tool_progress { |prog| update_spinner(prog) }
|
|
26
|
+
# handler.on_status { |status| show_status(status) }
|
|
27
|
+
#
|
|
17
28
|
# @example Via Client
|
|
18
29
|
# client.on_text { |text| print text }
|
|
19
30
|
# client.on_tool_use { |tool| puts tool.display_label }
|
|
@@ -25,13 +36,22 @@ module ClaudeAgent
|
|
|
25
36
|
# .on_result { |r| puts "\nDone!" }
|
|
26
37
|
#
|
|
27
38
|
class EventHandler
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
# Type-based events — one per message type, auto-dispatched from message.type
|
|
40
|
+
TYPE_EVENTS = %i[
|
|
41
|
+
user assistant system result stream_event compact_boundary
|
|
42
|
+
status tool_progress hook_response auth_status task_notification
|
|
43
|
+
hook_started hook_progress tool_use_summary task_started
|
|
44
|
+
task_progress rate_limit_event prompt_suggestion files_persisted
|
|
45
|
+
].freeze
|
|
46
|
+
|
|
47
|
+
# Decomposed events — extracted content from rich message types
|
|
48
|
+
DECOMPOSED_EVENTS = %i[text thinking tool_use tool_result].freeze
|
|
49
|
+
|
|
50
|
+
# Meta events — catch-all
|
|
51
|
+
META_EVENTS = %i[message].freeze
|
|
52
|
+
|
|
53
|
+
# All known events
|
|
54
|
+
EVENTS = (META_EVENTS + TYPE_EVENTS + DECOMPOSED_EVENTS).freeze
|
|
35
55
|
|
|
36
56
|
def initialize
|
|
37
57
|
@handlers = Hash.new { |h, k| h[k] = [] }
|
|
@@ -40,7 +60,7 @@ module ClaudeAgent
|
|
|
40
60
|
|
|
41
61
|
# Register a handler for an event
|
|
42
62
|
#
|
|
43
|
-
# @param event [Symbol] Event name
|
|
63
|
+
# @param event [Symbol] Event name (any symbol, including future/unknown types)
|
|
44
64
|
# @yield Event-specific arguments
|
|
45
65
|
# @return [self]
|
|
46
66
|
def on(event, &block)
|
|
@@ -49,53 +69,82 @@ module ClaudeAgent
|
|
|
49
69
|
end
|
|
50
70
|
|
|
51
71
|
# @!method on_message(&block)
|
|
52
|
-
# Register a handler for every message
|
|
72
|
+
# Register a handler for every message (catch-all)
|
|
53
73
|
# @yield [message] Any message object
|
|
54
74
|
# @return [self]
|
|
55
75
|
|
|
76
|
+
# @!method on_assistant(&block)
|
|
77
|
+
# Register a handler for AssistantMessage
|
|
78
|
+
# @yield [AssistantMessage] The assistant message
|
|
79
|
+
# @return [self]
|
|
80
|
+
|
|
81
|
+
# @!method on_user(&block)
|
|
82
|
+
# Register a handler for UserMessage
|
|
83
|
+
# @yield [UserMessage] The user message
|
|
84
|
+
# @return [self]
|
|
85
|
+
|
|
86
|
+
# @!method on_result(&block)
|
|
87
|
+
# Register a handler for ResultMessage (end of turn)
|
|
88
|
+
# @yield [ResultMessage] The result
|
|
89
|
+
# @return [self]
|
|
90
|
+
|
|
91
|
+
# @!method on_stream_event(&block)
|
|
92
|
+
# Register a handler for StreamEvent
|
|
93
|
+
# @yield [StreamEvent] The stream event
|
|
94
|
+
# @return [self]
|
|
95
|
+
|
|
96
|
+
# @!method on_status(&block)
|
|
97
|
+
# Register a handler for StatusMessage
|
|
98
|
+
# @yield [StatusMessage] The status message
|
|
99
|
+
# @return [self]
|
|
100
|
+
|
|
101
|
+
# @!method on_tool_progress(&block)
|
|
102
|
+
# Register a handler for ToolProgressMessage
|
|
103
|
+
# @yield [ToolProgressMessage] The tool progress message
|
|
104
|
+
# @return [self]
|
|
105
|
+
|
|
56
106
|
# @!method on_text(&block)
|
|
57
|
-
# Register a handler for assistant text content
|
|
107
|
+
# Register a handler for assistant text content (decomposed)
|
|
58
108
|
# @yield [String] Text from the AssistantMessage
|
|
59
109
|
# @return [self]
|
|
60
110
|
|
|
61
111
|
# @!method on_thinking(&block)
|
|
62
|
-
# Register a handler for assistant thinking content
|
|
112
|
+
# Register a handler for assistant thinking content (decomposed)
|
|
63
113
|
# @yield [String] Thinking from the AssistantMessage
|
|
64
114
|
# @return [self]
|
|
65
115
|
|
|
66
116
|
# @!method on_tool_use(&block)
|
|
67
|
-
# Register a handler for tool use requests
|
|
117
|
+
# Register a handler for tool use requests (decomposed)
|
|
68
118
|
# @yield [ToolUseBlock, ServerToolUseBlock] The tool use block
|
|
69
119
|
# @return [self]
|
|
70
120
|
|
|
71
121
|
# @!method on_tool_result(&block)
|
|
72
|
-
# Register a handler for tool results, paired with the original request
|
|
122
|
+
# Register a handler for tool results, paired with the original request (decomposed)
|
|
73
123
|
# @yield [ToolResultBlock, ToolUseBlock|nil] Result block and matched tool use
|
|
74
124
|
# @return [self]
|
|
75
125
|
|
|
76
|
-
|
|
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|
|
|
126
|
+
EVENTS.each do |event|
|
|
82
127
|
define_method(:"on_#{event}") { |&block| on(event, &block) }
|
|
83
128
|
end
|
|
84
129
|
|
|
85
130
|
# Dispatch a message to registered handlers
|
|
86
131
|
#
|
|
132
|
+
# Fires events in order:
|
|
133
|
+
# 1. +:message+ (catch-all)
|
|
134
|
+
# 2. +message.type+ (type-based, e.g. +:assistant+, +:stream_event+)
|
|
135
|
+
# 3. Decomposed events (+:text+, +:thinking+, +:tool_use+, +:tool_result+)
|
|
136
|
+
#
|
|
87
137
|
# @param message [Message] Any SDK message
|
|
88
138
|
# @return [void]
|
|
89
139
|
def handle(message)
|
|
90
140
|
emit(:message, message)
|
|
141
|
+
emit(message.type, message)
|
|
91
142
|
|
|
92
143
|
case message
|
|
93
144
|
when AssistantMessage
|
|
94
145
|
handle_assistant(message)
|
|
95
146
|
when UserMessage, UserMessageReplay
|
|
96
147
|
handle_user(message)
|
|
97
|
-
when ResultMessage
|
|
98
|
-
emit(:result, message)
|
|
99
148
|
end
|
|
100
149
|
end
|
|
101
150
|
|
data/lib/claude_agent/hooks.rb
CHANGED
|
@@ -167,13 +167,13 @@ module ClaudeAgent
|
|
|
167
167
|
required: [ :reason ]
|
|
168
168
|
|
|
169
169
|
BaseHookInput.define_input "Stop",
|
|
170
|
-
optional: { stop_hook_active: false }
|
|
170
|
+
optional: { stop_hook_active: false, last_assistant_message: nil }
|
|
171
171
|
|
|
172
172
|
BaseHookInput.define_input "SubagentStart",
|
|
173
173
|
required: [ :agent_id, :agent_type ]
|
|
174
174
|
|
|
175
175
|
BaseHookInput.define_input "SubagentStop",
|
|
176
|
-
optional: { stop_hook_active: false, agent_id: nil, agent_transcript_path: nil }
|
|
176
|
+
optional: { stop_hook_active: false, agent_id: nil, agent_transcript_path: nil, agent_type: nil, last_assistant_message: nil }
|
|
177
177
|
|
|
178
178
|
BaseHookInput.define_input "PreCompact",
|
|
179
179
|
required: [ :trigger ],
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeAgent
|
|
4
|
+
# Mutable wrapper around a ToolUseBlock for real-time status tracking.
|
|
5
|
+
#
|
|
6
|
+
# Unlike {ToolActivity} (immutable, built after a turn completes),
|
|
7
|
+
# LiveToolActivity tracks status changes as they happen — running,
|
|
8
|
+
# done, or error — making it suitable for live UIs.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# activity = LiveToolActivity.new(tool_use)
|
|
12
|
+
# activity.running? # => true
|
|
13
|
+
# activity.elapsed # => nil
|
|
14
|
+
#
|
|
15
|
+
# activity.update_elapsed(2.5)
|
|
16
|
+
# activity.elapsed # => 2.5
|
|
17
|
+
#
|
|
18
|
+
# activity.complete!(tool_result)
|
|
19
|
+
# activity.done? # => true
|
|
20
|
+
# activity.elapsed # => 2.5 (preserved from progress)
|
|
21
|
+
#
|
|
22
|
+
class LiveToolActivity
|
|
23
|
+
attr_reader :tool_use, :tool_result, :status, :started_at, :elapsed
|
|
24
|
+
|
|
25
|
+
# @param tool_use [ToolUseBlock, ServerToolUseBlock] The tool use block
|
|
26
|
+
def initialize(tool_use)
|
|
27
|
+
@tool_use = tool_use
|
|
28
|
+
@tool_result = nil
|
|
29
|
+
@status = :running
|
|
30
|
+
@started_at = Time.now
|
|
31
|
+
@elapsed = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Tool use ID
|
|
35
|
+
# @return [String]
|
|
36
|
+
def id
|
|
37
|
+
tool_use.id
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Tool name
|
|
41
|
+
# @return [String]
|
|
42
|
+
def name
|
|
43
|
+
tool_use.name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Tool input
|
|
47
|
+
# @return [Hash]
|
|
48
|
+
def input
|
|
49
|
+
tool_use.input
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Human-readable label (delegates to ToolUseBlock#display_label)
|
|
53
|
+
# @return [String]
|
|
54
|
+
def display_label
|
|
55
|
+
tool_use.display_label
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Detailed summary (delegates to ToolUseBlock#summary)
|
|
59
|
+
# @param max [Integer] Maximum length
|
|
60
|
+
# @return [String]
|
|
61
|
+
def summary(max: 60)
|
|
62
|
+
tool_use.summary(max: max)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# File path if this is a file-based tool
|
|
66
|
+
# @return [String, nil]
|
|
67
|
+
def file_path
|
|
68
|
+
tool_use.file_path
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Mark the tool as complete with its result.
|
|
72
|
+
#
|
|
73
|
+
# Transitions status to :done or :error based on the result.
|
|
74
|
+
# Calculates elapsed time from started_at if not already set by progress updates.
|
|
75
|
+
#
|
|
76
|
+
# @param result [ToolResultBlock, ServerToolResultBlock] The tool result
|
|
77
|
+
# @return [void]
|
|
78
|
+
def complete!(result)
|
|
79
|
+
@tool_result = result
|
|
80
|
+
@status = result.is_error ? :error : :done
|
|
81
|
+
@elapsed ||= Time.now - @started_at
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Update elapsed time from a ToolProgressMessage.
|
|
85
|
+
#
|
|
86
|
+
# @param seconds [Float] Elapsed time in seconds
|
|
87
|
+
# @return [void]
|
|
88
|
+
def update_elapsed(seconds)
|
|
89
|
+
@elapsed = seconds
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Whether the tool is currently running.
|
|
93
|
+
# @return [Boolean]
|
|
94
|
+
def running?
|
|
95
|
+
@status == :running
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Whether the tool completed successfully.
|
|
99
|
+
# @return [Boolean]
|
|
100
|
+
def done?
|
|
101
|
+
@status == :done
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Whether the tool completed with an error.
|
|
105
|
+
# @return [Boolean]
|
|
106
|
+
def error?
|
|
107
|
+
@status == :error
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Whether the tool execution is complete (done or error).
|
|
111
|
+
# @return [Boolean]
|
|
112
|
+
def complete?
|
|
113
|
+
@status == :done || @status == :error
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @return [Hash]
|
|
117
|
+
def to_h
|
|
118
|
+
{
|
|
119
|
+
id: id,
|
|
120
|
+
name: name,
|
|
121
|
+
status: @status,
|
|
122
|
+
elapsed: @elapsed,
|
|
123
|
+
started_at: @started_at
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def inspect
|
|
128
|
+
parts = [ "#<#{self.class}" ]
|
|
129
|
+
parts << "id=#{id}"
|
|
130
|
+
parts << "name=#{name}"
|
|
131
|
+
parts << "status=#{@status}"
|
|
132
|
+
parts << "elapsed=#{@elapsed}s" if @elapsed
|
|
133
|
+
"#{parts.join(" ")}>"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -254,7 +254,8 @@ module ClaudeAgent
|
|
|
254
254
|
StatusMessage.new(
|
|
255
255
|
uuid: raw[:uuid] || "",
|
|
256
256
|
session_id: raw[:session_id] || "",
|
|
257
|
-
status: raw[:status]
|
|
257
|
+
status: raw[:status],
|
|
258
|
+
permission_mode: raw[:permission_mode]
|
|
258
259
|
)
|
|
259
260
|
end
|
|
260
261
|
|
|
@@ -265,7 +266,8 @@ module ClaudeAgent
|
|
|
265
266
|
tool_use_id: raw[:tool_use_id] || "",
|
|
266
267
|
tool_name: raw[:tool_name] || "",
|
|
267
268
|
parent_tool_use_id: raw[:parent_tool_use_id],
|
|
268
|
-
elapsed_time_seconds: raw[:elapsed_time_seconds] || 0
|
|
269
|
+
elapsed_time_seconds: raw[:elapsed_time_seconds] || 0,
|
|
270
|
+
task_id: raw[:task_id]
|
|
269
271
|
)
|
|
270
272
|
end
|
|
271
273
|
|
|
@@ -295,13 +297,24 @@ module ClaudeAgent
|
|
|
295
297
|
end
|
|
296
298
|
|
|
297
299
|
def parse_task_notification_message(raw)
|
|
300
|
+
usage = raw[:usage]
|
|
301
|
+
parsed_usage = if usage
|
|
302
|
+
TaskUsage.new(
|
|
303
|
+
total_tokens: usage[:total_tokens] || 0,
|
|
304
|
+
tool_uses: usage[:tool_uses] || 0,
|
|
305
|
+
duration_ms: usage[:duration_ms] || 0
|
|
306
|
+
)
|
|
307
|
+
end
|
|
308
|
+
|
|
298
309
|
TaskNotificationMessage.new(
|
|
299
310
|
uuid: raw[:uuid] || "",
|
|
300
311
|
session_id: raw[:session_id] || "",
|
|
301
312
|
task_id: raw[:task_id] || "",
|
|
302
313
|
status: raw[:status] || "unknown",
|
|
303
314
|
output_file: raw[:output_file] || "",
|
|
304
|
-
summary: raw[:summary] || ""
|
|
315
|
+
summary: raw[:summary] || "",
|
|
316
|
+
tool_use_id: raw[:tool_use_id],
|
|
317
|
+
usage: parsed_usage
|
|
305
318
|
)
|
|
306
319
|
end
|
|
307
320
|
|
|
@@ -288,7 +288,11 @@ module ClaudeAgent
|
|
|
288
288
|
# status: "compacting"
|
|
289
289
|
# )
|
|
290
290
|
#
|
|
291
|
-
StatusMessage = Data.define(:uuid, :session_id, :status) do
|
|
291
|
+
StatusMessage = Data.define(:uuid, :session_id, :status, :permission_mode) do
|
|
292
|
+
def initialize(uuid:, session_id:, status:, permission_mode: nil)
|
|
293
|
+
super
|
|
294
|
+
end
|
|
295
|
+
|
|
292
296
|
def type
|
|
293
297
|
:status
|
|
294
298
|
end
|
|
@@ -313,7 +317,8 @@ module ClaudeAgent
|
|
|
313
317
|
:tool_use_id,
|
|
314
318
|
:tool_name,
|
|
315
319
|
:parent_tool_use_id,
|
|
316
|
-
:elapsed_time_seconds
|
|
320
|
+
:elapsed_time_seconds,
|
|
321
|
+
:task_id
|
|
317
322
|
) do
|
|
318
323
|
def initialize(
|
|
319
324
|
uuid:,
|
|
@@ -321,7 +326,8 @@ module ClaudeAgent
|
|
|
321
326
|
tool_use_id:,
|
|
322
327
|
tool_name:,
|
|
323
328
|
elapsed_time_seconds:,
|
|
324
|
-
parent_tool_use_id: nil
|
|
329
|
+
parent_tool_use_id: nil,
|
|
330
|
+
task_id: nil
|
|
325
331
|
)
|
|
326
332
|
super
|
|
327
333
|
end
|
|
@@ -469,7 +475,9 @@ module ClaudeAgent
|
|
|
469
475
|
:task_id,
|
|
470
476
|
:status,
|
|
471
477
|
:output_file,
|
|
472
|
-
:summary
|
|
478
|
+
:summary,
|
|
479
|
+
:tool_use_id,
|
|
480
|
+
:usage
|
|
473
481
|
) do
|
|
474
482
|
def initialize(
|
|
475
483
|
uuid:,
|
|
@@ -477,7 +485,9 @@ module ClaudeAgent
|
|
|
477
485
|
task_id:,
|
|
478
486
|
status:,
|
|
479
487
|
output_file:,
|
|
480
|
-
summary
|
|
488
|
+
summary:,
|
|
489
|
+
tool_use_id: nil,
|
|
490
|
+
usage: nil
|
|
481
491
|
)
|
|
482
492
|
super
|
|
483
493
|
end
|
data/lib/claude_agent/options.rb
CHANGED
|
@@ -39,9 +39,6 @@ module ClaudeAgent
|
|
|
39
39
|
enable_file_checkpointing: false,
|
|
40
40
|
persist_session: true,
|
|
41
41
|
betas: [],
|
|
42
|
-
init: false,
|
|
43
|
-
init_only: false,
|
|
44
|
-
maintenance: false,
|
|
45
42
|
prompt_suggestions: false,
|
|
46
43
|
debug: false,
|
|
47
44
|
debug_file: nil
|
|
@@ -60,12 +57,11 @@ module ClaudeAgent
|
|
|
60
57
|
continue_conversation resume fork_session resume_session_at session_id
|
|
61
58
|
max_turns max_budget_usd thinking effort max_thinking_tokens
|
|
62
59
|
strict_mcp_config mcp_servers hooks
|
|
63
|
-
|
|
60
|
+
sandbox cwd add_dirs env agent
|
|
64
61
|
cli_path extra_args agents setting_sources plugins
|
|
65
62
|
include_partial_messages output_format enable_file_checkpointing
|
|
66
63
|
persist_session prompt_suggestions betas max_buffer_size stderr_callback
|
|
67
64
|
abort_controller spawn_claude_code_process
|
|
68
|
-
init init_only maintenance
|
|
69
65
|
debug debug_file
|
|
70
66
|
logger
|
|
71
67
|
].freeze
|
|
@@ -93,10 +89,9 @@ module ClaudeAgent
|
|
|
93
89
|
args.concat(conversation_args)
|
|
94
90
|
args.concat(limits_args)
|
|
95
91
|
args.concat(mcp_args)
|
|
96
|
-
args.concat(
|
|
92
|
+
args.concat(sandbox_args)
|
|
97
93
|
args.concat(environment_args)
|
|
98
94
|
args.concat(output_args)
|
|
99
|
-
args.concat(setup_hook_args)
|
|
100
95
|
args.concat(debug_args)
|
|
101
96
|
args.concat(extra_cli_args)
|
|
102
97
|
end
|
|
@@ -233,9 +228,8 @@ module ClaudeAgent
|
|
|
233
228
|
end
|
|
234
229
|
end
|
|
235
230
|
|
|
236
|
-
def
|
|
231
|
+
def sandbox_args
|
|
237
232
|
[].tap do |args|
|
|
238
|
-
args.push("--settings", settings) if settings
|
|
239
233
|
if sandbox
|
|
240
234
|
args.push("--sandbox", JSON.generate(sandbox.to_h))
|
|
241
235
|
end
|
|
@@ -244,7 +238,6 @@ module ClaudeAgent
|
|
|
244
238
|
|
|
245
239
|
def environment_args
|
|
246
240
|
[].tap do |args|
|
|
247
|
-
args.push("--user", user) if user
|
|
248
241
|
args.push("--agent", agent) if agent
|
|
249
242
|
add_dirs.each { |dir| args.push("--add-dir", dir.to_s) }
|
|
250
243
|
args.push("--setting-sources", setting_sources.join(",")) if setting_sources&.any?
|
|
@@ -270,14 +263,6 @@ module ClaudeAgent
|
|
|
270
263
|
end
|
|
271
264
|
end
|
|
272
265
|
|
|
273
|
-
def setup_hook_args
|
|
274
|
-
[].tap do |args|
|
|
275
|
-
args.push("--init") if init
|
|
276
|
-
args.push("--init-only") if init_only
|
|
277
|
-
args.push("--maintenance") if maintenance
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
266
|
def debug_args
|
|
282
267
|
[].tap do |args|
|
|
283
268
|
args.push("--debug") if debug
|
|
@@ -342,11 +327,6 @@ module ClaudeAgent
|
|
|
342
327
|
if session_id && (continue_conversation || resume) && !fork_session
|
|
343
328
|
raise ConfigurationError, "session_id cannot be used with continue or resume unless fork_session is also set"
|
|
344
329
|
end
|
|
345
|
-
|
|
346
|
-
setup_options = [ init, init_only, maintenance ].count { |opt| opt }
|
|
347
|
-
if setup_options > 1
|
|
348
|
-
raise ConfigurationError, "Only one of init, init_only, or maintenance can be set at a time"
|
|
349
|
-
end
|
|
350
330
|
end
|
|
351
331
|
end
|
|
352
332
|
end
|
data/lib/claude_agent/query.rb
CHANGED
|
@@ -2,48 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module ClaudeAgent
|
|
4
4
|
class << self
|
|
5
|
-
# Run Setup hooks and exit
|
|
6
|
-
#
|
|
7
|
-
# This is a convenience method for running Setup hooks without starting
|
|
8
|
-
# a conversation. Useful for CI/CD pipelines or scripts that need to
|
|
9
|
-
# ensure setup is complete before proceeding.
|
|
10
|
-
#
|
|
11
|
-
# @param trigger [Symbol] The setup trigger (:init or :maintenance)
|
|
12
|
-
# @param options [Options, nil] Additional configuration options
|
|
13
|
-
# @return [Array<Message>] All messages received during setup
|
|
14
|
-
#
|
|
15
|
-
# @example Run init setup
|
|
16
|
-
# messages = ClaudeAgent.run_setup
|
|
17
|
-
# result = messages.last
|
|
18
|
-
# puts "Setup completed" if result.success?
|
|
19
|
-
#
|
|
20
|
-
# @example Run init setup with custom options
|
|
21
|
-
# options = ClaudeAgent::Options.new(cwd: "/my/project")
|
|
22
|
-
# ClaudeAgent.run_setup(trigger: :init, options: options)
|
|
23
|
-
#
|
|
24
|
-
# @note The :maintenance trigger requires --maintenance flag which
|
|
25
|
-
# continues into a conversation. For maintenance-only behavior,
|
|
26
|
-
# use options with maintenance: true and handle accordingly.
|
|
27
|
-
#
|
|
28
|
-
def run_setup(trigger: :init, options: nil)
|
|
29
|
-
options ||= Options.new
|
|
30
|
-
|
|
31
|
-
case trigger
|
|
32
|
-
when :init
|
|
33
|
-
# Create new options with init_only set
|
|
34
|
-
setup_options = Options.new(**options_to_hash(options).merge(init_only: true))
|
|
35
|
-
when :maintenance
|
|
36
|
-
# Note: There's no --maintenance-only flag, so we use --maintenance
|
|
37
|
-
# which will continue into a conversation. The caller should handle this.
|
|
38
|
-
setup_options = Options.new(**options_to_hash(options).merge(maintenance: true))
|
|
39
|
-
else
|
|
40
|
-
raise ArgumentError, "Invalid trigger: #{trigger}. Must be :init or :maintenance"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Run with an empty prompt - setup hooks run before the prompt is processed
|
|
44
|
-
query(prompt: "", options: setup_options).to_a
|
|
45
|
-
end
|
|
46
|
-
|
|
47
5
|
# One-shot query to Claude Code CLI
|
|
48
6
|
#
|
|
49
7
|
# This is a simple, stateless interface for sending a single prompt
|
|
@@ -151,17 +109,5 @@ module ClaudeAgent
|
|
|
151
109
|
events&.reset!
|
|
152
110
|
turn
|
|
153
111
|
end
|
|
154
|
-
|
|
155
|
-
private
|
|
156
|
-
|
|
157
|
-
# Convert an Options object to a hash for merging
|
|
158
|
-
# @param options [Options] The options object
|
|
159
|
-
# @return [Hash] Hash of option values
|
|
160
|
-
def options_to_hash(options)
|
|
161
|
-
Options::ATTRIBUTES.each_with_object({}) do |attr, hash|
|
|
162
|
-
value = options.send(attr)
|
|
163
|
-
hash[attr] = value unless value.nil?
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
112
|
end
|
|
167
113
|
end
|