swarm_cli 2.0.0.pre.1

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.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ module Formatters
5
+ # JsonFormatter streams JSON log entries in real-time for consumption by scripts.
6
+ # Each log entry is output as a single line of JSON (newline-delimited JSON).
7
+ #
8
+ # No colors, no spinners, no animations - just pure structured data.
9
+ class JsonFormatter
10
+ def initialize(output: $stdout)
11
+ @output = output
12
+ end
13
+
14
+ # Called when swarm execution starts
15
+ #
16
+ # Note: SwarmSDK now automatically emits swarm_start as a log event,
17
+ # so we don't emit it here to avoid duplicates. The swarm_start event
18
+ # will flow through on_log() from the SwarmSDK event stream.
19
+ def on_start(config_path:, swarm_name:, lead_agent:, prompt:)
20
+ # SwarmSDK emits swarm_start automatically - no need to emit here
21
+ end
22
+
23
+ # Called for each log entry from SwarmSDK
24
+ def on_log(entry)
25
+ emit(entry)
26
+ end
27
+
28
+ # Called when swarm execution completes successfully
29
+ #
30
+ # No action needed - SwarmSDK's swarm_stop event already contains all necessary
31
+ # information (content, last_agent, duration, metrics, etc.)
32
+ def on_success(result:)
33
+ # SwarmSDK emits swarm_stop automatically with complete information
34
+ end
35
+
36
+ # Called when swarm execution fails
37
+ #
38
+ # No action needed - SwarmSDK's swarm_stop event already contains error information
39
+ def on_error(error:, duration: nil)
40
+ # SwarmSDK emits swarm_stop automatically with error information
41
+ end
42
+
43
+ private
44
+
45
+ def emit(data)
46
+ @output.puts(data.to_json)
47
+ @output.flush
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ # Options for the `swarm mcp serve` command
5
+ class McpServeOptions
6
+ include TTY::Option
7
+
8
+ usage do
9
+ program "swarm"
10
+ commands "mcp", "serve"
11
+ desc "Start an MCP server exposing swarm lead agent as a tool"
12
+ example "swarm mcp serve team.yml"
13
+ end
14
+
15
+ argument :config_file do
16
+ desc "Path to swarm configuration file (YAML)"
17
+ required
18
+ end
19
+
20
+ option :help do
21
+ short "-h"
22
+ long "--help"
23
+ desc "Print usage"
24
+ end
25
+
26
+ def validate!
27
+ errors = []
28
+
29
+ # Config file must exist
30
+ if config_file && !File.exist?(config_file)
31
+ errors << "Configuration file not found: #{config_file}"
32
+ end
33
+
34
+ unless errors.empty?
35
+ raise SwarmCLI::ExecutionError, errors.join("\n")
36
+ end
37
+ end
38
+
39
+ # Convenience accessor
40
+ def config_file
41
+ params[:config_file]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ # Options for the `swarm mcp tools` command
5
+ class McpToolsOptions
6
+ include TTY::Option
7
+
8
+ usage do
9
+ program "swarm"
10
+ commands "mcp", "tools"
11
+ desc "Start an MCP server exposing SwarmSDK tools"
12
+ example "swarm mcp tools"
13
+ example "swarm mcp tools Read Write Bash"
14
+ example "swarm mcp tools ScratchpadWrite,ScratchpadRead"
15
+ end
16
+
17
+ argument :tool_names do
18
+ desc "Optional tool names to expose (defaults to all non-special tools)"
19
+ optional
20
+ arity :any
21
+ end
22
+
23
+ option :help do
24
+ short "-h"
25
+ long "--help"
26
+ desc "Print usage"
27
+ end
28
+
29
+ def validate!
30
+ errors = []
31
+
32
+ # Validate tool names if provided
33
+ if tool_names&.any?
34
+ invalid_tools = SwarmSDK::Tools::Registry.validate(tool_names)
35
+ if invalid_tools.any?
36
+ available = SwarmSDK::Tools::Registry.available_names.join(", ")
37
+ errors << "Invalid tool names: #{invalid_tools.join(", ")}. Available: #{available}"
38
+ end
39
+ end
40
+
41
+ unless errors.empty?
42
+ raise SwarmCLI::ExecutionError, errors.join("\n")
43
+ end
44
+ end
45
+
46
+ # Convenience accessor
47
+ def tool_names
48
+ names = params[:tool_names]
49
+ return [] if names.nil? || names.empty?
50
+
51
+ # TTY::Option might return a string or array
52
+ names_array = names.is_a?(Array) ? names : [names]
53
+
54
+ # Support comma-separated tool names in addition to space-separated
55
+ # e.g., both "swarm mcp tools Read Write" and "swarm mcp tools Read,Write" work
56
+ names_array.flat_map { |name| name.to_s.split(",") }.map(&:strip).reject(&:empty?)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ class Options
5
+ include TTY::Option
6
+
7
+ usage do
8
+ program "swarm"
9
+ command "run"
10
+ desc "Execute a swarm with AI agents"
11
+ example "swarm run team.yml -p 'Build a REST API'"
12
+ example "echo 'Build a REST API' | swarm run team.yml"
13
+ example "swarm run team.yml -p 'Refactor code' --output-format json"
14
+ end
15
+
16
+ argument :config_file do
17
+ desc "Path to swarm configuration file (YAML)"
18
+ required
19
+ end
20
+
21
+ option :prompt do
22
+ short "-p"
23
+ long "--prompt PROMPT"
24
+ desc "Task prompt for the swarm (if not provided, reads from stdin)"
25
+ end
26
+
27
+ option :output_format do
28
+ long "--output-format FORMAT"
29
+ desc "Output format: 'human' (default) or 'json'"
30
+ default "human"
31
+ permit ["human", "json"]
32
+ end
33
+
34
+ option :help do
35
+ short "-h"
36
+ long "--help"
37
+ desc "Print usage"
38
+ end
39
+
40
+ option :version do
41
+ short "-v"
42
+ long "--version"
43
+ desc "Print version"
44
+ end
45
+
46
+ flag :quiet do
47
+ short "-q"
48
+ long "--quiet"
49
+ desc "Suppress progress output (human format only)"
50
+ end
51
+
52
+ flag :truncate do
53
+ long "--truncate"
54
+ desc "Truncate long outputs for concise view"
55
+ end
56
+
57
+ flag :verbose do
58
+ long "--verbose"
59
+ desc "Show system reminders and additional debug information"
60
+ end
61
+
62
+ def validate!
63
+ errors = []
64
+
65
+ # At least one of prompt or stdin must be provided
66
+ if !prompt && $stdin.tty?
67
+ errors << "Prompt is required. Use -p or provide input via stdin."
68
+ end
69
+
70
+ # Config file must exist
71
+ if config_file && !File.exist?(config_file)
72
+ errors << "Configuration file not found: #{config_file}"
73
+ end
74
+
75
+ unless errors.empty?
76
+ raise SwarmCLI::ExecutionError, errors.join("\n")
77
+ end
78
+ end
79
+
80
+ def prompt_text
81
+ @prompt_text ||= if prompt
82
+ prompt
83
+ elsif !$stdin.tty?
84
+ $stdin.read.strip
85
+ else
86
+ raise SwarmCLI::ExecutionError, "No prompt provided"
87
+ end
88
+ end
89
+
90
+ # Convenience accessors that delegate to params
91
+ def config_file
92
+ params[:config_file]
93
+ end
94
+
95
+ def prompt
96
+ params[:prompt]
97
+ end
98
+
99
+ def output_format
100
+ params[:output_format]
101
+ end
102
+
103
+ def quiet?
104
+ params[:quiet]
105
+ end
106
+
107
+ def truncate?
108
+ params[:truncate]
109
+ end
110
+
111
+ def verbose?
112
+ params[:verbose]
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ VERSION = "2.0.0-1"
5
+ end
data/lib/swarm_cli.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "pathname"
6
+ require "yaml"
7
+
8
+ require "pastel"
9
+ require "tty-box"
10
+ require "tty-screen"
11
+ require "tty/link"
12
+ require "tty/markdown"
13
+ require "tty/option"
14
+ require "tty/prompt"
15
+ require "tty/spinner"
16
+ require "tty/spinner/multi"
17
+ require "tty/tree"
18
+
19
+ require "swarm_sdk"
20
+
21
+ module SwarmCLI
22
+ end
23
+
24
+ require "zeitwerk"
25
+ loader = Zeitwerk::Loader.new
26
+ loader.push_dir("#{__dir__}/swarm_cli", namespace: SwarmCLI)
27
+ loader.inflector.inflect(
28
+ "cli" => "CLI",
29
+ )
30
+ loader.setup
31
+
32
+ module SwarmCLI
33
+ class Error < StandardError; end
34
+ class ConfigurationError < Error; end
35
+ class ExecutionError < Error; end
36
+ end
metadata ADDED
@@ -0,0 +1,283 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swarm_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Paulo Arruda
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-10-14 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: fast-mcp
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.6'
26
+ - !ruby/object:Gem::Dependency
27
+ name: pastel
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: swarm_sdk
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: tty-box
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: tty-cursor
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: tty-link
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: tty-markdown
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: tty-option
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: tty-prompt
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: tty-spinner
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: tty-tree
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :runtime
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ - !ruby/object:Gem::Dependency
167
+ name: zeitwerk
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ type: :runtime
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: csv
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: '0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: docx
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '0.10'
201
+ type: :runtime
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '0.10'
208
+ - !ruby/object:Gem::Dependency
209
+ name: pdf-reader
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '2.15'
215
+ type: :runtime
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '2.15'
222
+ - !ruby/object:Gem::Dependency
223
+ name: roo
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - "~>"
227
+ - !ruby/object:Gem::Version
228
+ version: 3.0.0
229
+ type: :runtime
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: 3.0.0
236
+ description: |
237
+ SwarmCLI provides a beautiful command-line interface for SwarmSDK, the lightweight multi-agent
238
+ AI orchestration framework. Built with the TTY toolkit, it offers an intuitive and interactive
239
+ way to define, manage, and execute AI agent swarms with progress indicators, formatted output,
240
+ and comprehensive help documentation.
241
+ email:
242
+ - parrudaj@gmail.com
243
+ executables:
244
+ - swarm
245
+ extensions: []
246
+ extra_rdoc_files: []
247
+ files:
248
+ - exe/swarm
249
+ - lib/swarm_cli.rb
250
+ - lib/swarm_cli/cli.rb
251
+ - lib/swarm_cli/commands/mcp_serve.rb
252
+ - lib/swarm_cli/commands/mcp_tools.rb
253
+ - lib/swarm_cli/commands/run.rb
254
+ - lib/swarm_cli/formatters/human_formatter.rb
255
+ - lib/swarm_cli/formatters/json_formatter.rb
256
+ - lib/swarm_cli/mcp_serve_options.rb
257
+ - lib/swarm_cli/mcp_tools_options.rb
258
+ - lib/swarm_cli/options.rb
259
+ - lib/swarm_cli/version.rb
260
+ homepage: https://github.com/parruda/claude-swarm
261
+ licenses:
262
+ - MIT
263
+ metadata:
264
+ source_code_uri: https://github.com/parruda/claude-swarm
265
+ changelog_uri: https://github.com/parruda/claude-swarm/blob/main/CHANGELOG.md
266
+ rdoc_options: []
267
+ require_paths:
268
+ - lib
269
+ required_ruby_version: !ruby/object:Gem::Requirement
270
+ requirements:
271
+ - - ">="
272
+ - !ruby/object:Gem::Version
273
+ version: 3.2.0
274
+ required_rubygems_version: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - ">="
277
+ - !ruby/object:Gem::Version
278
+ version: '0'
279
+ requirements: []
280
+ rubygems_version: 3.6.2
281
+ specification_version: 4
282
+ summary: Command-line interface for SwarmSDK
283
+ test_files: []