swarm_cli 2.0.2 → 2.0.3
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/lib/swarm_cli/cli.rb +36 -4
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +22 -0
- data/lib/swarm_cli/interactive_repl.rb +227 -21
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_cli.rb +7 -0
- metadata +17 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f2300451b3b37327ea450e9eebc57db327859aed5b87f7b84a8dc07b7cb1ec2
|
|
4
|
+
data.tar.gz: d6742156018f591a09f396b0e08f63295b6978bfa3a07a3ef76702c791820baa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e0a82b80722dab137f89a8249201216c3c112d1d235377e0cb17cc452526dd0c140a020f21b023f20c63ce7e1a3b3c1ae5375d88a8ef6defe0a98a5b6a0e569d
|
|
7
|
+
data.tar.gz: b824bf81f759bf1fd05378cbf6df259298ce6e4118150fc6598b2f3e19f5f17e10dea584a4b91e3348dd176af8423df93e07ab17049b5183747a65f614f4ef97
|
data/lib/swarm_cli/cli.rb
CHANGED
|
@@ -36,10 +36,15 @@ module SwarmCLI
|
|
|
36
36
|
when "migrate"
|
|
37
37
|
migrate_command(@args[1..])
|
|
38
38
|
else
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
# Check if it's an extension command
|
|
40
|
+
if CommandRegistry.registered?(command)
|
|
41
|
+
extension_command(command, @args[1..])
|
|
42
|
+
else
|
|
43
|
+
$stderr.puts "Unknown command: #{command}"
|
|
44
|
+
$stderr.puts
|
|
45
|
+
print_help
|
|
46
|
+
exit(1)
|
|
47
|
+
end
|
|
43
48
|
end
|
|
44
49
|
rescue StandardError => e
|
|
45
50
|
$stderr.puts "Fatal error: #{e.message}"
|
|
@@ -123,6 +128,14 @@ module SwarmCLI
|
|
|
123
128
|
exit(1)
|
|
124
129
|
end
|
|
125
130
|
|
|
131
|
+
def extension_command(command_name, args)
|
|
132
|
+
# Get extension command class from registry
|
|
133
|
+
command_class = CommandRegistry.get(command_name)
|
|
134
|
+
|
|
135
|
+
# Execute extension command
|
|
136
|
+
command_class.execute(args)
|
|
137
|
+
end
|
|
138
|
+
|
|
126
139
|
def print_help
|
|
127
140
|
puts
|
|
128
141
|
puts "SwarmCLI v#{VERSION} - AI Agent Orchestration"
|
|
@@ -132,12 +145,24 @@ module SwarmCLI
|
|
|
132
145
|
puts " swarm migrate INPUT_FILE [--output OUTPUT_FILE]"
|
|
133
146
|
puts " swarm mcp serve CONFIG_FILE"
|
|
134
147
|
puts " swarm mcp tools [TOOL_NAMES...]"
|
|
148
|
+
|
|
149
|
+
# Show extension commands dynamically
|
|
150
|
+
CommandRegistry.commands.each do |cmd|
|
|
151
|
+
puts " swarm #{cmd} ..."
|
|
152
|
+
end
|
|
153
|
+
|
|
135
154
|
puts
|
|
136
155
|
puts "Commands:"
|
|
137
156
|
puts " run Execute a swarm with AI agents"
|
|
138
157
|
puts " migrate Migrate Claude Swarm v1 config to SwarmSDK v2 format"
|
|
139
158
|
puts " mcp serve Start an MCP server exposing swarm lead agent"
|
|
140
159
|
puts " mcp tools Start an MCP server exposing SwarmSDK tools"
|
|
160
|
+
|
|
161
|
+
# Show extension command descriptions (if registered)
|
|
162
|
+
if CommandRegistry.registered?("memory")
|
|
163
|
+
puts " memory Manage SwarmMemory embeddings"
|
|
164
|
+
end
|
|
165
|
+
|
|
141
166
|
puts
|
|
142
167
|
puts "Options:"
|
|
143
168
|
puts " -p, --prompt PROMPT Task prompt for the swarm"
|
|
@@ -159,6 +184,13 @@ module SwarmCLI
|
|
|
159
184
|
puts " swarm mcp tools # Expose all SwarmSDK tools"
|
|
160
185
|
puts " swarm mcp tools Bash Grep Read # Space-separated tools"
|
|
161
186
|
puts " swarm mcp tools ScratchpadWrite,ScratchpadRead # Comma-separated tools"
|
|
187
|
+
|
|
188
|
+
# Show extension command examples dynamically
|
|
189
|
+
if CommandRegistry.registered?("memory")
|
|
190
|
+
puts " swarm memory setup # Setup embeddings (download model)"
|
|
191
|
+
puts " swarm memory status # Check embedding status"
|
|
192
|
+
end
|
|
193
|
+
|
|
162
194
|
puts
|
|
163
195
|
end
|
|
164
196
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmCLI
|
|
4
|
+
# Registry for CLI command extensions
|
|
5
|
+
#
|
|
6
|
+
# Allows gems (like swarm_memory) to register additional CLI commands
|
|
7
|
+
# that integrate seamlessly with the main swarm CLI.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # In swarm_memory gem
|
|
11
|
+
# SwarmCLI::CommandRegistry.register(:memory, MyMemoryCommand)
|
|
12
|
+
#
|
|
13
|
+
# # User runs:
|
|
14
|
+
# swarm memory status
|
|
15
|
+
#
|
|
16
|
+
# # SwarmCLI routes to MyMemoryCommand.execute(["status"])
|
|
17
|
+
class CommandRegistry
|
|
18
|
+
@extensions = {}
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
# Register a command extension
|
|
22
|
+
#
|
|
23
|
+
# @param command_name [Symbol, String] Command name (e.g., :memory)
|
|
24
|
+
# @param command_class [Class] Command class with execute(args) method
|
|
25
|
+
# @return [void]
|
|
26
|
+
#
|
|
27
|
+
# @example
|
|
28
|
+
# CommandRegistry.register(:memory, SwarmMemory::CLI::Commands)
|
|
29
|
+
def register(command_name, command_class)
|
|
30
|
+
@extensions ||= {}
|
|
31
|
+
@extensions[command_name.to_s] = command_class
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get command class by name
|
|
35
|
+
#
|
|
36
|
+
# @param command_name [String] Command name
|
|
37
|
+
# @return [Class, nil] Command class or nil if not found
|
|
38
|
+
def get(command_name)
|
|
39
|
+
@extensions ||= {}
|
|
40
|
+
@extensions[command_name.to_s]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if a command is registered
|
|
44
|
+
#
|
|
45
|
+
# @param command_name [String] Command name
|
|
46
|
+
# @return [Boolean] True if command exists
|
|
47
|
+
def registered?(command_name)
|
|
48
|
+
@extensions ||= {}
|
|
49
|
+
@extensions.key?(command_name.to_s)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get all registered command names
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<String>] Command names
|
|
55
|
+
def commands
|
|
56
|
+
@extensions ||= {}
|
|
57
|
+
@extensions.keys
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -264,6 +264,16 @@ module SwarmCLI
|
|
|
264
264
|
agent = entry[:agent]
|
|
265
265
|
@usage_tracker.track_tool_call(tool_call_id: entry[:tool_call_id], tool_name: entry[:tool])
|
|
266
266
|
|
|
267
|
+
# Special handling for Think tool - show as thoughts, not as a tool call
|
|
268
|
+
if entry[:tool] == "Think" && entry[:arguments] && entry[:arguments]["thoughts"]
|
|
269
|
+
thoughts = entry[:arguments]["thoughts"]
|
|
270
|
+
thinking = @event_renderer.thinking_text(thoughts, indent: @depth_tracker.get(agent))
|
|
271
|
+
@output.puts thinking unless thinking.empty?
|
|
272
|
+
@output.puts
|
|
273
|
+
# Don't show spinner for Think tool
|
|
274
|
+
return
|
|
275
|
+
end
|
|
276
|
+
|
|
267
277
|
# Render tool call event
|
|
268
278
|
@output.puts @event_renderer.tool_call(
|
|
269
279
|
agent: agent,
|
|
@@ -297,6 +307,18 @@ module SwarmCLI
|
|
|
297
307
|
agent = entry[:agent]
|
|
298
308
|
tool_name = entry[:tool] || @usage_tracker.tool_name_for(entry[:tool_call_id])
|
|
299
309
|
|
|
310
|
+
# Special handling for Think tool - skip showing result (already shown as thoughts)
|
|
311
|
+
if tool_name == "Think"
|
|
312
|
+
# Don't show anything - thoughts were already displayed in handle_tool_call
|
|
313
|
+
# Start spinner for agent processing
|
|
314
|
+
unless @quiet
|
|
315
|
+
spinner_key = "agent_#{agent}".to_sym
|
|
316
|
+
indent = @depth_tracker.indent(agent)
|
|
317
|
+
@spinner_manager.start(spinner_key, "#{indent}#{agent} is processing...")
|
|
318
|
+
end
|
|
319
|
+
return
|
|
320
|
+
end
|
|
321
|
+
|
|
300
322
|
# Stop tool spinner with success
|
|
301
323
|
unless @quiet || tool_name == "TodoWrite"
|
|
302
324
|
spinner_key = "tool_#{entry[:tool_call_id]}".to_sym
|
|
@@ -23,11 +23,23 @@ module SwarmCLI
|
|
|
23
23
|
class InteractiveREPL
|
|
24
24
|
COMMANDS = {
|
|
25
25
|
"/help" => "Show available commands",
|
|
26
|
-
"/clear" => "Clear the
|
|
26
|
+
"/clear" => "Clear the lead agent's conversation context",
|
|
27
|
+
"/tools" => "List the lead agent's available tools",
|
|
27
28
|
"/history" => "Show conversation history",
|
|
29
|
+
"/defrag" => "Run memory defragmentation workflow (find and link related entries)",
|
|
28
30
|
"/exit" => "Exit the REPL (or press Ctrl+D)",
|
|
29
31
|
}.freeze
|
|
30
32
|
|
|
33
|
+
# History configuration
|
|
34
|
+
HISTORY_SIZE = 1000
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
# Get history file path (can be overridden with SWARM_HISTORY env var)
|
|
38
|
+
def history_file
|
|
39
|
+
ENV["SWARM_HISTORY"] || File.expand_path("~/.swarm/history")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
31
43
|
def initialize(swarm:, options:, initial_message: nil)
|
|
32
44
|
@swarm = swarm
|
|
33
45
|
@options = options
|
|
@@ -37,6 +49,7 @@ module SwarmCLI
|
|
|
37
49
|
@validation_warnings_shown = false
|
|
38
50
|
|
|
39
51
|
setup_ui_components
|
|
52
|
+
setup_persistent_history
|
|
40
53
|
|
|
41
54
|
# Create formatter for swarm execution output (interactive mode)
|
|
42
55
|
@formatter = Formatters::HumanFormatter.new(
|
|
@@ -67,6 +80,9 @@ module SwarmCLI
|
|
|
67
80
|
display_goodbye
|
|
68
81
|
display_session_summary
|
|
69
82
|
exit(130)
|
|
83
|
+
ensure
|
|
84
|
+
# Save history on exit
|
|
85
|
+
save_persistent_history
|
|
70
86
|
end
|
|
71
87
|
|
|
72
88
|
# Execute a message with Ctrl+C cancellation support
|
|
@@ -122,6 +138,61 @@ module SwarmCLI
|
|
|
122
138
|
cancelled ? nil : result
|
|
123
139
|
end
|
|
124
140
|
|
|
141
|
+
# Handle slash commands
|
|
142
|
+
# Public for testing
|
|
143
|
+
#
|
|
144
|
+
# @param input [String] Command input (e.g., "/help", "/clear")
|
|
145
|
+
def handle_command(input)
|
|
146
|
+
command = input.split.first.downcase
|
|
147
|
+
|
|
148
|
+
case command
|
|
149
|
+
when "/help"
|
|
150
|
+
display_help
|
|
151
|
+
when "/clear"
|
|
152
|
+
clear_context
|
|
153
|
+
when "/tools"
|
|
154
|
+
list_tools
|
|
155
|
+
when "/history"
|
|
156
|
+
display_history
|
|
157
|
+
when "/defrag"
|
|
158
|
+
defrag_memory
|
|
159
|
+
when "/exit"
|
|
160
|
+
# Break from main loop to trigger session summary
|
|
161
|
+
throw(:exit_repl)
|
|
162
|
+
else
|
|
163
|
+
puts render_error("Unknown command: #{command}")
|
|
164
|
+
puts @colors[:system].call("Type /help for available commands")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Save persistent history to file
|
|
169
|
+
# Public for testing
|
|
170
|
+
#
|
|
171
|
+
# @return [void]
|
|
172
|
+
def save_persistent_history
|
|
173
|
+
history_file = self.class.history_file
|
|
174
|
+
return unless history_file
|
|
175
|
+
|
|
176
|
+
history = Reline::HISTORY.to_a
|
|
177
|
+
|
|
178
|
+
# Limit to configured size
|
|
179
|
+
if HISTORY_SIZE.positive? && history.size > HISTORY_SIZE
|
|
180
|
+
history = history.last(HISTORY_SIZE)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Write with secure permissions (owner read/write only)
|
|
184
|
+
File.open(history_file, "w", 0o600, encoding: Encoding::UTF_8) do |f|
|
|
185
|
+
# Handle multi-line entries by escaping newlines with backslash
|
|
186
|
+
history.each do |entry|
|
|
187
|
+
escaped = entry.scrub.split("\n").join("\\\n")
|
|
188
|
+
f.puts(escaped)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
rescue Errno::EACCES, Errno::ENOENT
|
|
192
|
+
# Can't write history - continue anyway
|
|
193
|
+
nil
|
|
194
|
+
end
|
|
195
|
+
|
|
125
196
|
private
|
|
126
197
|
|
|
127
198
|
def setup_ui_components
|
|
@@ -151,6 +222,9 @@ module SwarmCLI
|
|
|
151
222
|
config.add_default_key_binding_by_keymap(:emacs, [9], :fuzzy_complete)
|
|
152
223
|
config.add_default_key_binding_by_keymap(:vi_insert, [9], :fuzzy_complete)
|
|
153
224
|
|
|
225
|
+
# Configure history size
|
|
226
|
+
Reline.core.config.history_size = HISTORY_SIZE
|
|
227
|
+
|
|
154
228
|
# Setup colors using detached styles for performance
|
|
155
229
|
@colors = {
|
|
156
230
|
prompt: @pastel.bright_cyan.bold.detach,
|
|
@@ -170,6 +244,33 @@ module SwarmCLI
|
|
|
170
244
|
}
|
|
171
245
|
end
|
|
172
246
|
|
|
247
|
+
def setup_persistent_history
|
|
248
|
+
history_file = self.class.history_file
|
|
249
|
+
|
|
250
|
+
# Ensure history directory exists
|
|
251
|
+
FileUtils.mkdir_p(File.dirname(history_file))
|
|
252
|
+
|
|
253
|
+
# Load history from file
|
|
254
|
+
return unless File.exist?(history_file)
|
|
255
|
+
|
|
256
|
+
File.open(history_file, "r:UTF-8") do |f|
|
|
257
|
+
f.each_line do |line|
|
|
258
|
+
line = line.chomp
|
|
259
|
+
|
|
260
|
+
# Handle multi-line entries (backslash continuation)
|
|
261
|
+
if Reline::HISTORY.last&.end_with?("\\")
|
|
262
|
+
Reline::HISTORY.last.delete_suffix!("\\")
|
|
263
|
+
Reline::HISTORY.last << "\n" << line
|
|
264
|
+
else
|
|
265
|
+
Reline::HISTORY << line unless line.empty?
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
270
|
+
# History file doesn't exist or can't be read - that's OK
|
|
271
|
+
nil
|
|
272
|
+
end
|
|
273
|
+
|
|
173
274
|
def display_welcome
|
|
174
275
|
divider = @colors[:divider].call("─" * 60)
|
|
175
276
|
|
|
@@ -312,26 +413,6 @@ module SwarmCLI
|
|
|
312
413
|
end
|
|
313
414
|
end
|
|
314
415
|
|
|
315
|
-
def handle_command(input)
|
|
316
|
-
command = input.split.first.downcase
|
|
317
|
-
|
|
318
|
-
case command
|
|
319
|
-
when "/help"
|
|
320
|
-
display_help
|
|
321
|
-
when "/clear"
|
|
322
|
-
system("clear") || system("cls")
|
|
323
|
-
display_welcome
|
|
324
|
-
when "/history"
|
|
325
|
-
display_history
|
|
326
|
-
when "/exit"
|
|
327
|
-
# Break from main loop to trigger session summary
|
|
328
|
-
throw(:exit_repl)
|
|
329
|
-
else
|
|
330
|
-
puts render_error("Unknown command: #{command}")
|
|
331
|
-
puts @colors[:system].call("Type /help for available commands")
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
416
|
def handle_message(input)
|
|
336
417
|
# Add to history
|
|
337
418
|
@conversation_history << { role: "user", content: input }
|
|
@@ -442,6 +523,111 @@ module SwarmCLI
|
|
|
442
523
|
puts help_box
|
|
443
524
|
end
|
|
444
525
|
|
|
526
|
+
def clear_context
|
|
527
|
+
# Get the lead agent
|
|
528
|
+
lead = @swarm.agent(@swarm.lead_agent)
|
|
529
|
+
|
|
530
|
+
# Clear the agent's conversation history
|
|
531
|
+
lead.reset_messages!
|
|
532
|
+
|
|
533
|
+
# Clear REPL conversation history
|
|
534
|
+
@conversation_history.clear
|
|
535
|
+
|
|
536
|
+
# Display confirmation
|
|
537
|
+
puts ""
|
|
538
|
+
puts @colors[:success].call("✓ Conversation context cleared for #{@swarm.lead_agent}")
|
|
539
|
+
puts @colors[:system].call(" Starting fresh - previous messages removed from context")
|
|
540
|
+
puts ""
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def list_tools
|
|
544
|
+
# Get the lead agent
|
|
545
|
+
lead = @swarm.agent(@swarm.lead_agent)
|
|
546
|
+
|
|
547
|
+
# Get tools hash (tool_name => tool_instance)
|
|
548
|
+
tools_hash = lead.tools
|
|
549
|
+
|
|
550
|
+
puts ""
|
|
551
|
+
puts @colors[:header].call("Available Tools for #{@swarm.lead_agent}:")
|
|
552
|
+
puts @colors[:divider].call("─" * 60)
|
|
553
|
+
puts ""
|
|
554
|
+
|
|
555
|
+
if tools_hash.empty?
|
|
556
|
+
puts @colors[:system].call("No tools available")
|
|
557
|
+
return
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# Group tools by category
|
|
561
|
+
memory_tools = []
|
|
562
|
+
standard_tools = []
|
|
563
|
+
delegation_tools = []
|
|
564
|
+
mcp_tools = []
|
|
565
|
+
other_tools = []
|
|
566
|
+
|
|
567
|
+
tools_hash.each_value do |tool|
|
|
568
|
+
tool_name = tool.name
|
|
569
|
+
case tool_name
|
|
570
|
+
when /^Memory/, "LoadSkill"
|
|
571
|
+
memory_tools << tool_name
|
|
572
|
+
when /^DelegateTaskTo/
|
|
573
|
+
delegation_tools << tool_name
|
|
574
|
+
when /^mcp__/
|
|
575
|
+
mcp_tools << tool_name
|
|
576
|
+
when "Read", "Write", "Edit", "MultiEdit", "Bash", "Grep", "Glob",
|
|
577
|
+
"TodoWrite", "Think", "Clock", "WebFetch",
|
|
578
|
+
"ScratchpadWrite", "ScratchpadRead", "ScratchpadList"
|
|
579
|
+
standard_tools << tool_name
|
|
580
|
+
else
|
|
581
|
+
other_tools << tool_name
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# Display tools by category
|
|
586
|
+
if standard_tools.any?
|
|
587
|
+
puts @colors[:agent_label].call("Standard Tools:")
|
|
588
|
+
standard_tools.sort.each do |name|
|
|
589
|
+
puts @colors[:system].call(" • #{name}")
|
|
590
|
+
end
|
|
591
|
+
puts ""
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
if memory_tools.any?
|
|
595
|
+
puts @colors[:agent_label].call("Memory Tools:")
|
|
596
|
+
memory_tools.sort.each do |name|
|
|
597
|
+
puts @colors[:system].call(" • #{name}")
|
|
598
|
+
end
|
|
599
|
+
puts ""
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
if delegation_tools.any?
|
|
603
|
+
puts @colors[:agent_label].call("Delegation Tools:")
|
|
604
|
+
delegation_tools.sort.each do |name|
|
|
605
|
+
puts @colors[:system].call(" • #{name}")
|
|
606
|
+
end
|
|
607
|
+
puts ""
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
if mcp_tools.any?
|
|
611
|
+
puts @colors[:agent_label].call("MCP Tools:")
|
|
612
|
+
mcp_tools.sort.each do |name|
|
|
613
|
+
puts @colors[:system].call(" • #{name}")
|
|
614
|
+
end
|
|
615
|
+
puts ""
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
if other_tools.any?
|
|
619
|
+
puts @colors[:agent_label].call("Other Tools:")
|
|
620
|
+
other_tools.sort.each do |name|
|
|
621
|
+
puts @colors[:system].call(" • #{name}")
|
|
622
|
+
end
|
|
623
|
+
puts ""
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
puts @colors[:divider].call("─" * 60)
|
|
627
|
+
puts @colors[:system].call("Total: #{tools_hash.size} tools")
|
|
628
|
+
puts ""
|
|
629
|
+
end
|
|
630
|
+
|
|
445
631
|
def display_history
|
|
446
632
|
if @conversation_history.empty?
|
|
447
633
|
puts @colors[:system].call("No conversation history yet")
|
|
@@ -474,6 +660,26 @@ module SwarmCLI
|
|
|
474
660
|
puts @colors[:divider].call("─" * 60)
|
|
475
661
|
end
|
|
476
662
|
|
|
663
|
+
def defrag_memory
|
|
664
|
+
puts ""
|
|
665
|
+
puts @colors[:header].call("🔧 Memory Defragmentation Workflow")
|
|
666
|
+
puts @colors[:divider].call("─" * 60)
|
|
667
|
+
puts ""
|
|
668
|
+
|
|
669
|
+
# Inject prompt to run find_related then link_related
|
|
670
|
+
prompt = <<~PROMPT.strip
|
|
671
|
+
Run memory defragmentation workflow:
|
|
672
|
+
|
|
673
|
+
1. First, run MemoryDefrag(action: "find_related") to discover related entries
|
|
674
|
+
2. Review the results carefully
|
|
675
|
+
3. Then run MemoryDefrag(action: "link_related", dry_run: false) to create bidirectional links
|
|
676
|
+
|
|
677
|
+
Report what you found and what links were created.
|
|
678
|
+
PROMPT
|
|
679
|
+
|
|
680
|
+
handle_message(prompt)
|
|
681
|
+
end
|
|
682
|
+
|
|
477
683
|
def display_goodbye
|
|
478
684
|
puts ""
|
|
479
685
|
goodbye_text = @colors[:success].call("👋 Goodbye! Thanks for using Swarm CLI")
|
data/lib/swarm_cli/version.rb
CHANGED
data/lib/swarm_cli.rb
CHANGED
|
@@ -36,3 +36,10 @@ module SwarmCLI
|
|
|
36
36
|
class ConfigurationError < Error; end
|
|
37
37
|
class ExecutionError < Error; end
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
# Try to load swarm_memory gem if available (for CLI command extensions)
|
|
41
|
+
begin
|
|
42
|
+
require "swarm_memory"
|
|
43
|
+
rescue LoadError
|
|
44
|
+
# swarm_memory not installed - that's fine, memory commands won't be available
|
|
45
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: swarm_cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paulo Arruda
|
|
@@ -205,6 +205,20 @@ dependencies:
|
|
|
205
205
|
- - "~>"
|
|
206
206
|
- !ruby/object:Gem::Version
|
|
207
207
|
version: '2.15'
|
|
208
|
+
- !ruby/object:Gem::Dependency
|
|
209
|
+
name: reverse_markdown
|
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
|
211
|
+
requirements:
|
|
212
|
+
- - "~>"
|
|
213
|
+
- !ruby/object:Gem::Version
|
|
214
|
+
version: 3.0.0
|
|
215
|
+
type: :runtime
|
|
216
|
+
prerelease: false
|
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - "~>"
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: 3.0.0
|
|
208
222
|
- !ruby/object:Gem::Dependency
|
|
209
223
|
name: roo
|
|
210
224
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -234,6 +248,7 @@ files:
|
|
|
234
248
|
- exe/swarm
|
|
235
249
|
- lib/swarm_cli.rb
|
|
236
250
|
- lib/swarm_cli/cli.rb
|
|
251
|
+
- lib/swarm_cli/command_registry.rb
|
|
237
252
|
- lib/swarm_cli/commands/mcp_serve.rb
|
|
238
253
|
- lib/swarm_cli/commands/mcp_tools.rb
|
|
239
254
|
- lib/swarm_cli/commands/migrate.rb
|
|
@@ -268,7 +283,7 @@ licenses:
|
|
|
268
283
|
- MIT
|
|
269
284
|
metadata:
|
|
270
285
|
source_code_uri: https://github.com/parruda/claude-swarm
|
|
271
|
-
changelog_uri: https://github.com/parruda/claude-swarm/blob/main/CHANGELOG.md
|
|
286
|
+
changelog_uri: https://github.com/parruda/claude-swarm/blob/main/docs/v2/CHANGELOG.swarm_cli.md
|
|
272
287
|
rdoc_options: []
|
|
273
288
|
require_paths:
|
|
274
289
|
- lib
|