swarm_cli 2.0.1 → 2.0.2
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5f6285a136ccf9bda4e010daf5062f38d2dc1fbe27ef0747dd7e08a0f794bfc
|
4
|
+
data.tar.gz: 54400709e283dce60b2422392a45f3ccc56b0876b41ccd919a17b12efbd19413
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afbaa1d360f97e20167256ebfa6c6c68d377bac849a295197641443c7a59f306a132a1084a5bf89d8490b9d79dce67f4bb0a1b480642ce68dedeb758af0ef67e
|
7
|
+
data.tar.gz: b37a53f4855db437e74d2fd6a60245650a41496c603f5529371f5471a4e7d7889bd34f11c5b3764ddc0ae3f97a20777dc9a249da4659c11155a7e5ab7d7f29f8
|
@@ -14,7 +14,9 @@ module SwarmCLI
|
|
14
14
|
|
15
15
|
def initialize(options)
|
16
16
|
@options = options
|
17
|
-
|
17
|
+
# Create scratchpad with persistence for MCP server
|
18
|
+
scratchpad_path = File.join(Dir.pwd, ".swarm", "scratchpad.json")
|
19
|
+
@scratchpad = SwarmSDK::Scratchpad.new(persist_to: scratchpad_path)
|
18
20
|
end
|
19
21
|
|
20
22
|
def execute
|
@@ -10,6 +10,8 @@ module SwarmCLI
|
|
10
10
|
# - :non_interactive - Full headers, task prompt, complete summary (for single execution)
|
11
11
|
# - :interactive - Minimal output for REPL (headers shown in welcome screen)
|
12
12
|
class HumanFormatter
|
13
|
+
attr_reader :spinner_manager
|
14
|
+
|
13
15
|
def initialize(output: $stdout, quiet: false, truncate: false, verbose: false, mode: :non_interactive)
|
14
16
|
@output = output
|
15
17
|
@quiet = quiet
|
@@ -5,6 +5,8 @@ require "tty-spinner"
|
|
5
5
|
require "tty-markdown"
|
6
6
|
require "tty-box"
|
7
7
|
require "pastel"
|
8
|
+
require "async"
|
9
|
+
require "async/condition"
|
8
10
|
|
9
11
|
module SwarmCLI
|
10
12
|
# InteractiveREPL provides a professional, interactive terminal interface
|
@@ -67,6 +69,59 @@ module SwarmCLI
|
|
67
69
|
exit(130)
|
68
70
|
end
|
69
71
|
|
72
|
+
# Execute a message with Ctrl+C cancellation support
|
73
|
+
# Public for testing
|
74
|
+
#
|
75
|
+
# @param input [String] User input to execute
|
76
|
+
# @return [SwarmSDK::Result, nil] Result or nil if cancelled
|
77
|
+
def execute_with_cancellation(input, &log_callback)
|
78
|
+
cancelled = false
|
79
|
+
result = nil
|
80
|
+
|
81
|
+
# Execute in Async block to enable Ctrl+C cancellation
|
82
|
+
Async do |task|
|
83
|
+
# Use Async::Condition for trap-safe cancellation
|
84
|
+
# (Condition#signal uses Thread::Queue which is safe from trap context)
|
85
|
+
cancel_condition = Async::Condition.new
|
86
|
+
|
87
|
+
# Install trap ONLY during execution
|
88
|
+
# When Ctrl+C is pressed, signal the condition instead of calling task.stop
|
89
|
+
old_trap = trap("INT") do
|
90
|
+
cancel_condition.signal(:cancel)
|
91
|
+
end
|
92
|
+
|
93
|
+
begin
|
94
|
+
# Execute swarm in async task
|
95
|
+
llm_task = task.async do
|
96
|
+
@swarm.execute(input, &log_callback)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Monitor task - watches for cancellation signal
|
100
|
+
# Must be created AFTER llm_task so it can reference it
|
101
|
+
monitor_task = task.async do
|
102
|
+
if cancel_condition.wait == :cancel
|
103
|
+
cancelled = true
|
104
|
+
llm_task.stop
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
result = llm_task.wait
|
109
|
+
rescue Async::Stop
|
110
|
+
# Task was stopped by Ctrl+C
|
111
|
+
cancelled = true
|
112
|
+
ensure
|
113
|
+
# Clean up monitor task
|
114
|
+
monitor_task&.stop if monitor_task&.alive?
|
115
|
+
|
116
|
+
# CRITICAL: Restore old trap when done
|
117
|
+
# This ensures Ctrl+C at the prompt still exits the REPL
|
118
|
+
trap("INT", old_trap)
|
119
|
+
end
|
120
|
+
end.wait
|
121
|
+
|
122
|
+
cancelled ? nil : result
|
123
|
+
end
|
124
|
+
|
70
125
|
private
|
71
126
|
|
72
127
|
def setup_ui_components
|
@@ -127,6 +182,7 @@ module SwarmCLI
|
|
127
182
|
puts @colors[:system].call("Lead Agent: #{@swarm.lead_agent}")
|
128
183
|
puts ""
|
129
184
|
puts @colors[:system].call("Type your message and press Enter to submit")
|
185
|
+
puts @colors[:system].call("Press Option+Enter (or ESC then Enter) for multi-line input")
|
130
186
|
puts @colors[:system].call("Type #{@colors[:code].call("/help")} for commands or #{@colors[:code].call("/exit")} to quit")
|
131
187
|
puts ""
|
132
188
|
puts divider
|
@@ -159,14 +215,17 @@ module SwarmCLI
|
|
159
215
|
# Build the prompt indicator with colors
|
160
216
|
prompt_indicator = build_prompt_indicator
|
161
217
|
|
162
|
-
# Use Reline for
|
218
|
+
# Use Reline.readmultiline for multi-line input support
|
219
|
+
# - Option+ENTER (or ESC+ENTER): Adds a newline, continues editing
|
220
|
+
# - Regular ENTER: Always submits immediately
|
163
221
|
# Second parameter true = add to history for arrow up/down
|
164
|
-
|
222
|
+
# Block always returns true = ENTER always submits
|
223
|
+
input = Reline.readmultiline(prompt_indicator, true) { |_lines| true }
|
165
224
|
|
166
|
-
return if
|
225
|
+
return if input.nil? # Ctrl+D returns nil
|
167
226
|
|
168
|
-
#
|
169
|
-
|
227
|
+
# Strip whitespace from the complete input
|
228
|
+
input.strip
|
170
229
|
end
|
171
230
|
|
172
231
|
def display_prompt_stats
|
@@ -279,8 +338,8 @@ module SwarmCLI
|
|
279
338
|
|
280
339
|
puts ""
|
281
340
|
|
282
|
-
# Execute
|
283
|
-
result =
|
341
|
+
# Execute with cancellation support
|
342
|
+
result = execute_with_cancellation(input) do |log_entry|
|
284
343
|
# Skip model warnings - already emitted before first prompt
|
285
344
|
next if log_entry[:type] == "model_lookup_warning"
|
286
345
|
|
@@ -292,6 +351,17 @@ module SwarmCLI
|
|
292
351
|
end
|
293
352
|
end
|
294
353
|
|
354
|
+
# Handle cancellation (result is nil when cancelled)
|
355
|
+
if result.nil?
|
356
|
+
# Stop all active spinners
|
357
|
+
@formatter.spinner_manager.stop_all
|
358
|
+
|
359
|
+
puts ""
|
360
|
+
puts @colors[:warning].call("✗ Request cancelled by user")
|
361
|
+
puts ""
|
362
|
+
return
|
363
|
+
end
|
364
|
+
|
295
365
|
# Check for errors
|
296
366
|
if result.failure?
|
297
367
|
@formatter.on_error(error: result.error, duration: result.duration)
|
@@ -353,7 +423,9 @@ module SwarmCLI
|
|
353
423
|
end,
|
354
424
|
"",
|
355
425
|
@colors[:system].call("Input Tips:"),
|
356
|
-
@colors[:system].call(" •
|
426
|
+
@colors[:system].call(" • Press Enter to submit your message"),
|
427
|
+
@colors[:system].call(" • Press Option+Enter (or ESC then Enter) for multi-line input"),
|
428
|
+
@colors[:system].call(" • Press Ctrl+C to cancel an ongoing request"),
|
357
429
|
@colors[:system].call(" • Press Ctrl+D to exit"),
|
358
430
|
@colors[:system].call(" • Use arrow keys for history and editing"),
|
359
431
|
@colors[:system].call(" • Type / for commands or @ for file paths"),
|
data/lib/swarm_cli/version.rb
CHANGED