claude_swarm 1.0.1 → 1.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.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +1 -1
  3. data/.claude/hooks/lint-code-files.rb +65 -0
  4. data/.rubocop.yml +22 -2
  5. data/CHANGELOG.md +14 -1
  6. data/CLAUDE.md +1 -1
  7. data/CONTRIBUTING.md +69 -0
  8. data/README.md +27 -2
  9. data/Rakefile +71 -3
  10. data/analyze_coverage.rb +94 -0
  11. data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
  12. data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
  13. data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
  14. data/docs/v2/README.md +308 -0
  15. data/docs/v2/guides/claude-code-agents.md +262 -0
  16. data/docs/v2/guides/complete-tutorial.md +3088 -0
  17. data/docs/v2/guides/getting-started.md +1456 -0
  18. data/docs/v2/guides/memory-adapters.md +998 -0
  19. data/docs/v2/guides/plugins.md +816 -0
  20. data/docs/v2/guides/quick-start-cli.md +1745 -0
  21. data/docs/v2/guides/rails-integration.md +1902 -0
  22. data/docs/v2/guides/swarm-memory.md +599 -0
  23. data/docs/v2/reference/cli.md +729 -0
  24. data/docs/v2/reference/ruby-dsl.md +2154 -0
  25. data/docs/v2/reference/yaml.md +1835 -0
  26. data/docs-team-swarm.yml +2222 -0
  27. data/examples/learning-assistant/assistant.md +7 -0
  28. data/examples/learning-assistant/example-memories/concept-example.md +90 -0
  29. data/examples/learning-assistant/example-memories/experience-example.md +66 -0
  30. data/examples/learning-assistant/example-memories/fact-example.md +76 -0
  31. data/examples/learning-assistant/example-memories/memory-index.md +78 -0
  32. data/examples/learning-assistant/example-memories/skill-example.md +168 -0
  33. data/examples/learning-assistant/learning_assistant.rb +34 -0
  34. data/examples/learning-assistant/learning_assistant.yml +20 -0
  35. data/examples/v2/dsl/01_basic.rb +44 -0
  36. data/examples/v2/dsl/02_core_parameters.rb +59 -0
  37. data/examples/v2/dsl/03_capabilities.rb +71 -0
  38. data/examples/v2/dsl/04_llm_parameters.rb +56 -0
  39. data/examples/v2/dsl/05_advanced_flags.rb +73 -0
  40. data/examples/v2/dsl/06_permissions.rb +80 -0
  41. data/examples/v2/dsl/07_mcp_server.rb +62 -0
  42. data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
  43. data/examples/v2/dsl/09_agent_hooks.rb +67 -0
  44. data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
  45. data/examples/v2/dsl/11_delegation.rb +60 -0
  46. data/examples/v2/dsl/12_complete_integration.rb +137 -0
  47. data/examples/v2/file_tools_swarm.yml +102 -0
  48. data/examples/v2/hooks/01_basic_hooks.rb +133 -0
  49. data/examples/v2/hooks/02_usage_tracking.rb +201 -0
  50. data/examples/v2/hooks/03_production_monitoring.rb +429 -0
  51. data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
  52. data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
  53. data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
  54. data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
  55. data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
  56. data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
  57. data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
  58. data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
  59. data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
  60. data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
  61. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
  62. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
  63. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
  64. data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
  65. data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
  66. data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
  67. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
  68. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
  69. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
  70. data/examples/v2/hooks/swarm_summary.sh +44 -0
  71. data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
  72. data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
  73. data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
  74. data/examples/v2/hooks/validate_bash.rb +59 -0
  75. data/examples/v2/multi_directory_permissions.yml +221 -0
  76. data/examples/v2/node_context_demo.rb +127 -0
  77. data/examples/v2/node_workflow.rb +173 -0
  78. data/examples/v2/path_resolution_demo.rb +216 -0
  79. data/examples/v2/simple-swarm-v2.rb +90 -0
  80. data/examples/v2/simple-swarm-v2.yml +62 -0
  81. data/examples/v2/swarm.yml +71 -0
  82. data/examples/v2/swarm_with_hooks.yml +61 -0
  83. data/examples/v2/swarm_with_hooks_simple.yml +25 -0
  84. data/examples/v2/think_tool_demo.rb +62 -0
  85. data/exe/swarm +6 -0
  86. data/lib/claude_swarm/claude_mcp_server.rb +0 -6
  87. data/lib/claude_swarm/cli.rb +10 -3
  88. data/lib/claude_swarm/commands/ps.rb +19 -20
  89. data/lib/claude_swarm/commands/show.rb +1 -1
  90. data/lib/claude_swarm/configuration.rb +10 -12
  91. data/lib/claude_swarm/mcp_generator.rb +10 -1
  92. data/lib/claude_swarm/orchestrator.rb +73 -49
  93. data/lib/claude_swarm/system_utils.rb +37 -11
  94. data/lib/claude_swarm/version.rb +1 -1
  95. data/lib/claude_swarm/worktree_manager.rb +1 -0
  96. data/lib/claude_swarm/yaml_loader.rb +22 -0
  97. data/lib/claude_swarm.rb +6 -2
  98. data/lib/swarm_cli/cli.rb +201 -0
  99. data/lib/swarm_cli/command_registry.rb +61 -0
  100. data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
  101. data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
  102. data/lib/swarm_cli/commands/migrate.rb +55 -0
  103. data/lib/swarm_cli/commands/run.rb +173 -0
  104. data/lib/swarm_cli/config_loader.rb +97 -0
  105. data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
  106. data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
  107. data/lib/swarm_cli/interactive_repl.rb +918 -0
  108. data/lib/swarm_cli/mcp_serve_options.rb +44 -0
  109. data/lib/swarm_cli/mcp_tools_options.rb +59 -0
  110. data/lib/swarm_cli/migrate_options.rb +54 -0
  111. data/lib/swarm_cli/migrator.rb +132 -0
  112. data/lib/swarm_cli/options.rb +151 -0
  113. data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
  114. data/lib/swarm_cli/ui/components/content_block.rb +120 -0
  115. data/lib/swarm_cli/ui/components/divider.rb +57 -0
  116. data/lib/swarm_cli/ui/components/panel.rb +62 -0
  117. data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
  118. data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
  119. data/lib/swarm_cli/ui/formatters/number.rb +58 -0
  120. data/lib/swarm_cli/ui/formatters/text.rb +77 -0
  121. data/lib/swarm_cli/ui/formatters/time.rb +73 -0
  122. data/lib/swarm_cli/ui/icons.rb +59 -0
  123. data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
  124. data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
  125. data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
  126. data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
  127. data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
  128. data/lib/swarm_cli/version.rb +5 -0
  129. data/lib/swarm_cli.rb +44 -0
  130. data/lib/swarm_memory/adapters/base.rb +141 -0
  131. data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
  132. data/lib/swarm_memory/chat_extension.rb +34 -0
  133. data/lib/swarm_memory/cli/commands.rb +306 -0
  134. data/lib/swarm_memory/core/entry.rb +37 -0
  135. data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
  136. data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
  137. data/lib/swarm_memory/core/path_normalizer.rb +75 -0
  138. data/lib/swarm_memory/core/semantic_index.rb +244 -0
  139. data/lib/swarm_memory/core/storage.rb +288 -0
  140. data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
  141. data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
  142. data/lib/swarm_memory/dsl/memory_config.rb +113 -0
  143. data/lib/swarm_memory/embeddings/embedder.rb +36 -0
  144. data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
  145. data/lib/swarm_memory/errors.rb +21 -0
  146. data/lib/swarm_memory/integration/cli_registration.rb +30 -0
  147. data/lib/swarm_memory/integration/configuration.rb +43 -0
  148. data/lib/swarm_memory/integration/registration.rb +31 -0
  149. data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
  150. data/lib/swarm_memory/optimization/analyzer.rb +244 -0
  151. data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
  152. data/lib/swarm_memory/prompts/memory.md.erb +109 -0
  153. data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
  154. data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
  155. data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
  156. data/lib/swarm_memory/search/semantic_search.rb +112 -0
  157. data/lib/swarm_memory/search/text_search.rb +42 -0
  158. data/lib/swarm_memory/search/text_similarity.rb +80 -0
  159. data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
  160. data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
  161. data/lib/swarm_memory/tools/load_skill.rb +313 -0
  162. data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
  163. data/lib/swarm_memory/tools/memory_delete.rb +99 -0
  164. data/lib/swarm_memory/tools/memory_edit.rb +185 -0
  165. data/lib/swarm_memory/tools/memory_glob.rb +160 -0
  166. data/lib/swarm_memory/tools/memory_grep.rb +247 -0
  167. data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
  168. data/lib/swarm_memory/tools/memory_read.rb +123 -0
  169. data/lib/swarm_memory/tools/memory_write.rb +231 -0
  170. data/lib/swarm_memory/utils.rb +50 -0
  171. data/lib/swarm_memory/version.rb +5 -0
  172. data/lib/swarm_memory.rb +166 -0
  173. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
  174. data/lib/swarm_sdk/agent/builder.rb +461 -0
  175. data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
  176. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  177. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
  178. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
  179. data/lib/swarm_sdk/agent/chat.rb +1159 -0
  180. data/lib/swarm_sdk/agent/context.rb +112 -0
  181. data/lib/swarm_sdk/agent/context_manager.rb +309 -0
  182. data/lib/swarm_sdk/agent/definition.rb +556 -0
  183. data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
  184. data/lib/swarm_sdk/configuration.rb +296 -0
  185. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  186. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  187. data/lib/swarm_sdk/context_compactor.rb +340 -0
  188. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  189. data/lib/swarm_sdk/hooks/context.rb +197 -0
  190. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  191. data/lib/swarm_sdk/hooks/error.rb +29 -0
  192. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  193. data/lib/swarm_sdk/hooks/registry.rb +147 -0
  194. data/lib/swarm_sdk/hooks/result.rb +150 -0
  195. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  196. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  197. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  198. data/lib/swarm_sdk/log_collector.rb +51 -0
  199. data/lib/swarm_sdk/log_stream.rb +69 -0
  200. data/lib/swarm_sdk/markdown_parser.rb +75 -0
  201. data/lib/swarm_sdk/model_aliases.json +5 -0
  202. data/lib/swarm_sdk/models.json +1 -0
  203. data/lib/swarm_sdk/models.rb +120 -0
  204. data/lib/swarm_sdk/node/agent_config.rb +49 -0
  205. data/lib/swarm_sdk/node/builder.rb +439 -0
  206. data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
  207. data/lib/swarm_sdk/node_context.rb +170 -0
  208. data/lib/swarm_sdk/node_orchestrator.rb +384 -0
  209. data/lib/swarm_sdk/permissions/config.rb +239 -0
  210. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  211. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  212. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  213. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  214. data/lib/swarm_sdk/plugin.rb +147 -0
  215. data/lib/swarm_sdk/plugin_registry.rb +101 -0
  216. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
  217. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  218. data/lib/swarm_sdk/result.rb +97 -0
  219. data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
  220. data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
  221. data/lib/swarm_sdk/swarm/builder.rb +586 -0
  222. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  223. data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
  224. data/lib/swarm_sdk/swarm.rb +982 -0
  225. data/lib/swarm_sdk/tools/bash.rb +274 -0
  226. data/lib/swarm_sdk/tools/clock.rb +44 -0
  227. data/lib/swarm_sdk/tools/delegate.rb +164 -0
  228. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  229. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  230. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
  231. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  232. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  233. data/lib/swarm_sdk/tools/edit.rb +150 -0
  234. data/lib/swarm_sdk/tools/glob.rb +158 -0
  235. data/lib/swarm_sdk/tools/grep.rb +228 -0
  236. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  237. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  238. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  239. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  240. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  241. data/lib/swarm_sdk/tools/read.rb +251 -0
  242. data/lib/swarm_sdk/tools/registry.rb +93 -0
  243. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
  244. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
  245. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
  246. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  247. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
  248. data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
  249. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  250. data/lib/swarm_sdk/tools/think.rb +95 -0
  251. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  252. data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
  253. data/lib/swarm_sdk/tools/write.rb +117 -0
  254. data/lib/swarm_sdk/utils.rb +50 -0
  255. data/lib/swarm_sdk/version.rb +5 -0
  256. data/lib/swarm_sdk.rb +157 -0
  257. data/llm.v2.txt +13407 -0
  258. data/rubocop/cop/security/no_reflection_methods.rb +47 -0
  259. data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
  260. data/swarm_cli.gemspec +57 -0
  261. data/swarm_memory.gemspec +28 -0
  262. data/swarm_sdk.gemspec +41 -0
  263. data/team.yml +1 -1
  264. data/team_full.yml +1875 -0
  265. data/{team_v2.yml → team_sdk.yml} +121 -52
  266. metadata +247 -4
  267. data/EXAMPLES.md +0 -164
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
@@ -3,6 +3,7 @@
3
3
  module ClaudeSwarm
4
4
  class WorktreeManager
5
5
  include SystemUtils
6
+
6
7
  attr_reader :shared_worktree_name, :created_worktrees
7
8
 
8
9
  def initialize(cli_worktree_option = nil, session_id: nil)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeSwarm
4
+ # Provides consistent YAML loading across the application
5
+ module YamlLoader
6
+ class << self
7
+ # Load a YAML configuration file (enables aliases for configuration flexibility)
8
+ # @param file_path [String] Path to the configuration file
9
+ # @return [Hash] The loaded configuration
10
+ # @raise [ClaudeSwarm::Error] Re-raises with a more descriptive error message
11
+ def load_config_file(file_path)
12
+ YAML.load_file(file_path, aliases: true)
13
+ rescue Errno::ENOENT
14
+ raise ClaudeSwarm::Error, "Configuration file not found: #{file_path}"
15
+ rescue Psych::SyntaxError => e
16
+ raise ClaudeSwarm::Error, "Invalid YAML syntax in #{file_path}: #{e.message}"
17
+ rescue Psych::BadAlias => e
18
+ raise ClaudeSwarm::Error, "Invalid YAML alias in #{file_path}: #{e.message}"
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/claude_swarm.rb CHANGED
@@ -28,13 +28,14 @@ require "thor"
28
28
 
29
29
  # Zeitwerk setup
30
30
  require "zeitwerk"
31
- loader = Zeitwerk::Loader.for_gem
31
+ loader = Zeitwerk::Loader.new
32
+ loader.tag = "claude_swarm"
33
+
32
34
  loader.ignore("#{__dir__}/claude_swarm/templates")
33
35
  loader.inflector.inflect(
34
36
  "cli" => "CLI",
35
37
  "openai" => "OpenAI",
36
38
  )
37
- loader.setup
38
39
 
39
40
  module ClaudeSwarm
40
41
  class Error < StandardError; end
@@ -65,3 +66,6 @@ module ClaudeSwarm
65
66
  end
66
67
  end
67
68
  end
69
+
70
+ loader.push_dir("#{__dir__}/claude_swarm", namespace: ClaudeSwarm)
71
+ loader.setup
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ class CLI
5
+ class << self
6
+ def start(args)
7
+ new(args).run
8
+ end
9
+ end
10
+
11
+ def initialize(args)
12
+ @args = args
13
+ end
14
+
15
+ def run
16
+ # Handle special cases first
17
+ if @args.empty? || @args.include?("--help") || @args.include?("-h")
18
+ print_help
19
+ exit(0)
20
+ end
21
+
22
+ if @args.include?("--version") || @args.include?("-v")
23
+ print_version
24
+ exit(0)
25
+ end
26
+
27
+ # Extract command
28
+ command = @args.first
29
+
30
+ # Route to command
31
+ case command
32
+ when "run"
33
+ run_command(@args[1..])
34
+ when "mcp"
35
+ mcp_command(@args[1..])
36
+ when "migrate"
37
+ migrate_command(@args[1..])
38
+ else
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
48
+ end
49
+ rescue StandardError => e
50
+ $stderr.puts "Fatal error: #{e.message}"
51
+ exit(1)
52
+ end
53
+
54
+ private
55
+
56
+ def mcp_command(args)
57
+ # MCP has subcommands
58
+ subcommand = args.first
59
+
60
+ case subcommand
61
+ when "serve"
62
+ mcp_serve_command(args[1..])
63
+ when "tools"
64
+ mcp_tools_command(args[1..])
65
+ else
66
+ $stderr.puts "Unknown mcp subcommand: #{subcommand}"
67
+ $stderr.puts
68
+ $stderr.puts "Available mcp subcommands:"
69
+ $stderr.puts " serve Start an MCP server exposing swarm lead agent"
70
+ $stderr.puts " tools Start an MCP server exposing SwarmSDK tools"
71
+ exit(1)
72
+ end
73
+ end
74
+
75
+ def mcp_serve_command(args)
76
+ # Parse options
77
+ options = McpServeOptions.new
78
+ options.parse(args)
79
+
80
+ # Execute mcp serve command
81
+ Commands::McpServe.new(options).execute
82
+ rescue TTY::Option::InvalidParameter, TTY::Option::InvalidArgument => e
83
+ $stderr.puts "Error: #{e.message}"
84
+ $stderr.puts
85
+ $stderr.puts options.help
86
+ exit(1)
87
+ end
88
+
89
+ def mcp_tools_command(args)
90
+ # Parse options
91
+ options = McpToolsOptions.new
92
+ options.parse(args)
93
+
94
+ # Execute mcp tools command
95
+ Commands::McpTools.new(options).execute
96
+ rescue TTY::Option::InvalidParameter, TTY::Option::InvalidArgument => e
97
+ $stderr.puts "Error: #{e.message}"
98
+ $stderr.puts
99
+ $stderr.puts options.help
100
+ exit(1)
101
+ end
102
+
103
+ def run_command(args)
104
+ # Parse options
105
+ options = Options.new
106
+ options.parse(args)
107
+
108
+ # Execute run command
109
+ Commands::Run.new(options).execute
110
+ rescue TTY::Option::InvalidParameter, TTY::Option::InvalidArgument => e
111
+ $stderr.puts "Error: #{e.message}"
112
+ $stderr.puts
113
+ $stderr.puts options.help
114
+ exit(1)
115
+ end
116
+
117
+ def migrate_command(args)
118
+ # Parse options
119
+ options = MigrateOptions.new
120
+ options.parse(args)
121
+
122
+ # Execute migrate command
123
+ Commands::Migrate.new(options).execute
124
+ rescue TTY::Option::InvalidParameter, TTY::Option::InvalidArgument => e
125
+ $stderr.puts "Error: #{e.message}"
126
+ $stderr.puts
127
+ $stderr.puts options.help
128
+ exit(1)
129
+ end
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
+
139
+ def print_help
140
+ puts
141
+ puts "SwarmCLI v#{VERSION} - AI Agent Orchestration"
142
+ puts
143
+ puts "Usage:"
144
+ puts " swarm run CONFIG_FILE -p PROMPT [options]"
145
+ puts " swarm migrate INPUT_FILE [--output OUTPUT_FILE]"
146
+ puts " swarm mcp serve CONFIG_FILE"
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
+
154
+ puts
155
+ puts "Commands:"
156
+ puts " run Execute a swarm with AI agents"
157
+ puts " migrate Migrate Claude Swarm v1 config to SwarmSDK v2 format"
158
+ puts " mcp serve Start an MCP server exposing swarm lead agent"
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
+
166
+ puts
167
+ puts "Options:"
168
+ puts " -p, --prompt PROMPT Task prompt for the swarm"
169
+ puts " -o, --output FILE Output file for migrated config (default: stdout)"
170
+ puts " --output-format FORMAT Output format: 'human' or 'json' (default: human)"
171
+ puts " -q, --quiet Suppress progress output (human format only)"
172
+ puts " --truncate Truncate long outputs for concise view"
173
+ puts " --verbose Show system reminders and additional debug information"
174
+ puts " -h, --help Print help"
175
+ puts " -v, --version Print version"
176
+ puts
177
+ puts "Examples:"
178
+ puts " swarm run team.yml -p 'Build a REST API'"
179
+ puts " echo 'Build a REST API' | swarm run team.yml"
180
+ puts " swarm run team.yml -p 'Refactor code' --output-format json"
181
+ puts " swarm migrate old-config.yml"
182
+ puts " swarm migrate old-config.yml --output new-config.yml"
183
+ puts " swarm mcp serve team.yml"
184
+ puts " swarm mcp tools # Expose all SwarmSDK tools"
185
+ puts " swarm mcp tools Bash Grep Read # Space-separated tools"
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
+
194
+ puts
195
+ end
196
+
197
+ def print_version
198
+ puts "SwarmCLI v#{VERSION}"
199
+ end
200
+ end
201
+ end
@@ -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
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ module Commands
5
+ # McpServe command starts an MCP server that exposes the swarm's lead agent as a tool.
6
+ #
7
+ # Usage:
8
+ # swarm mcp serve config.yml
9
+ #
10
+ # The server uses stdio transport and exposes a "swarm" tool that executes tasks
11
+ # through the configured lead agent.
12
+ class McpServe
13
+ attr_reader :options
14
+
15
+ def initialize(options)
16
+ @options = options
17
+ end
18
+
19
+ def execute
20
+ # Validate options
21
+ options.validate!
22
+
23
+ # Load swarm configuration to validate it
24
+ config_path = options.config_file
25
+ unless File.exist?(config_path)
26
+ $stderr.puts "Error: Configuration file not found: #{config_path}"
27
+ exit(1)
28
+ end
29
+
30
+ # Validate the swarm configuration
31
+ begin
32
+ SwarmSDK::Swarm.load(config_path)
33
+ rescue SwarmSDK::ConfigurationError => e
34
+ $stderr.puts "Error: Invalid swarm configuration: #{e.message}"
35
+ exit(1)
36
+ end
37
+
38
+ # MCP servers should be quiet - stdout is reserved for MCP protocol
39
+ # Errors will still be logged to stderr
40
+
41
+ # Start the MCP server
42
+ start_mcp_server(config_path)
43
+ rescue Interrupt
44
+ # User cancelled (Ctrl+C) - silent exit
45
+ exit(130)
46
+ rescue StandardError => e
47
+ # Unexpected errors - always log to stderr
48
+ $stderr.puts "Fatal error: #{e.message}"
49
+ $stderr.puts e.backtrace.first(5).join("\n") if ENV["DEBUG"]
50
+ exit(1)
51
+ end
52
+
53
+ private
54
+
55
+ def start_mcp_server(config_path)
56
+ require "fast_mcp"
57
+
58
+ # Create the server
59
+ server = FastMcp::Server.new(
60
+ name: "swarm-mcp-server",
61
+ version: SwarmCLI::VERSION,
62
+ )
63
+
64
+ # Register the swarm tool
65
+ tool_class = create_swarm_tool_class(config_path)
66
+ server.register_tool(tool_class)
67
+
68
+ # Start with stdio transport (default)
69
+ server.start
70
+ end
71
+
72
+ def create_swarm_tool_class(config_path)
73
+ # Create a tool class dynamically with the config path bound
74
+ Class.new(FastMcp::Tool) do
75
+ # Explicit tool name required for anonymous classes
76
+ tool_name "task"
77
+
78
+ description "Execute tasks through the SwarmSDK lead agent"
79
+
80
+ arguments do
81
+ required(:task).filled(:string).description("The task or prompt to execute")
82
+ optional(:description).filled(:string).description("Brief description of the task")
83
+ optional(:thinking_budget).filled(:string, included_in?: ["think", "think hard", "think harder", "ultrathink"]).description("Thinking budget level")
84
+ end
85
+
86
+ # Store config path as class variable
87
+ @config_path = config_path
88
+
89
+ class << self
90
+ attr_accessor :config_path
91
+ end
92
+
93
+ define_method(:call) do |task:, description: nil, thinking_budget: nil|
94
+ # Load swarm for each execution (ensures fresh state)
95
+ swarm = SwarmSDK::Swarm.load(self.class.config_path)
96
+
97
+ # Build prompt with thinking budget if provided
98
+ prompt = task
99
+ if thinking_budget
100
+ prompt = "<thinking_budget>#{thinking_budget}</thinking_budget>\n\n#{task}"
101
+ end
102
+
103
+ # Execute the task (description is metadata only, not passed to execute)
104
+ result = swarm.execute(prompt)
105
+
106
+ # Check for errors
107
+ if result.failure?
108
+ {
109
+ success: false,
110
+ error: result.error.message,
111
+ task: task,
112
+ description: description,
113
+ }
114
+ else
115
+ # On success, return just the content string
116
+ result.content
117
+ end
118
+ rescue StandardError => e
119
+ {
120
+ success: false,
121
+ error: e.message,
122
+ task: task,
123
+ description: description,
124
+ }
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ module Commands
5
+ # McpTools command starts an MCP server that exposes SwarmSDK tools.
6
+ #
7
+ # Usage:
8
+ # swarm mcp tools # Expose all available tools
9
+ # swarm mcp tools Bash Grep # Expose only Bash and Grep
10
+ #
11
+ # The server uses stdio transport and exposes SwarmSDK tools as MCP tools.
12
+ class McpTools
13
+ attr_reader :options
14
+
15
+ def initialize(options)
16
+ @options = options
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)
20
+ end
21
+
22
+ def execute
23
+ # Validate options
24
+ options.validate!
25
+
26
+ # Determine which tools to expose
27
+ tools_to_expose = determine_tools
28
+
29
+ if tools_to_expose.empty?
30
+ $stderr.puts "Error: No tools available to expose"
31
+ exit(1)
32
+ end
33
+
34
+ # Start the MCP server
35
+ start_mcp_server(tools_to_expose)
36
+ rescue Interrupt
37
+ # User cancelled (Ctrl+C) - silent exit
38
+ exit(130)
39
+ rescue StandardError => e
40
+ # Unexpected errors - always log to stderr
41
+ $stderr.puts "Fatal error: #{e.message}"
42
+ $stderr.puts e.backtrace.first(5).join("\n") if ENV["DEBUG"]
43
+ exit(1)
44
+ end
45
+
46
+ private
47
+
48
+ def determine_tools
49
+ if options.tool_names.any?
50
+ # Use specified tools
51
+ options.tool_names.map(&:to_sym)
52
+ else
53
+ # Default: expose all available tools
54
+ SwarmSDK::Tools::Registry.available_names
55
+ end
56
+ end
57
+
58
+ def start_mcp_server(tool_names)
59
+ require "fast_mcp"
60
+
61
+ # Create the server
62
+ server = FastMcp::Server.new(
63
+ name: "swarm-tools-server",
64
+ version: SwarmCLI::VERSION,
65
+ )
66
+
67
+ # Register each tool
68
+ tool_names.each do |tool_name|
69
+ tool_class = create_mcp_tool_wrapper(tool_name)
70
+ server.register_tool(tool_class)
71
+ end
72
+
73
+ # Start with stdio transport (default)
74
+ server.start
75
+ end
76
+
77
+ def create_special_tool_instance(tool_name)
78
+ case tool_name
79
+ when :Read
80
+ SwarmSDK::Tools::Read.create_for_agent(:mcp)
81
+ when :Write
82
+ SwarmSDK::Tools::Write.create_for_agent(:mcp)
83
+ when :Edit
84
+ SwarmSDK::Tools::Edit.create_for_agent(:mcp)
85
+ when :MultiEdit
86
+ SwarmSDK::Tools::MultiEdit.create_for_agent(:mcp)
87
+ when :TodoWrite
88
+ SwarmSDK::Tools::TodoWrite.create_for_agent(:mcp)
89
+ when :ScratchpadWrite
90
+ SwarmSDK::Tools::ScratchpadWrite.create_for_scratchpad(@scratchpad)
91
+ when :ScratchpadRead
92
+ SwarmSDK::Tools::ScratchpadRead.create_for_scratchpad(@scratchpad)
93
+ when :ScratchpadList
94
+ SwarmSDK::Tools::ScratchpadList.create_for_scratchpad(@scratchpad)
95
+ else
96
+ raise "Unknown special tool: #{tool_name}"
97
+ end
98
+ end
99
+
100
+ def create_mcp_tool_wrapper(tool_name)
101
+ sdk_tool_class_or_special = SwarmSDK::Tools::Registry.get(tool_name)
102
+
103
+ # Get the actual tool instance for special tools
104
+ sdk_tool = if sdk_tool_class_or_special == :special
105
+ create_special_tool_instance(tool_name)
106
+ else
107
+ sdk_tool_class_or_special.new
108
+ end
109
+
110
+ # Get tool metadata
111
+ tool_description = sdk_tool.respond_to?(:description) ? sdk_tool.description : "SwarmSDK #{tool_name} tool"
112
+ tool_params = sdk_tool.class.respond_to?(:parameters) ? sdk_tool.class.parameters : {}
113
+
114
+ # Create an MCP tool wrapper
115
+ Class.new(FastMcp::Tool) do
116
+ tool_name tool_name.to_s
117
+ description tool_description
118
+
119
+ # Map RubyLLM parameters to fast-mcp arguments
120
+ arguments do
121
+ tool_params.each do |param_name, param_obj|
122
+ param_type = param_obj.type == "integer" ? :integer : :string
123
+ if param_obj.required
124
+ required(param_name).filled(param_type).description(param_obj.description || "")
125
+ else
126
+ optional(param_name).filled(param_type).description(param_obj.description || "")
127
+ end
128
+ end
129
+ end
130
+
131
+ # Capture sdk_tool in closure
132
+ define_method(:call) do |**kwargs|
133
+ result = sdk_tool.execute(**kwargs)
134
+
135
+ # Return string output for MCP
136
+ if result.is_a?(Hash)
137
+ result[:output] || result[:content] || result[:files]&.join("\n") || result.to_s
138
+ else
139
+ result.to_s
140
+ end
141
+ rescue StandardError => e
142
+ "Error: #{e.message}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmCLI
4
+ module Commands
5
+ # Migrate command converts Claude Swarm v1 configurations to SwarmSDK v2 format.
6
+ #
7
+ # Usage:
8
+ # swarm migrate old-config.yml
9
+ # swarm migrate old-config.yml --output new-config.yml
10
+ #
11
+ class Migrate
12
+ attr_reader :options
13
+
14
+ def initialize(options)
15
+ @options = options
16
+ end
17
+
18
+ def execute
19
+ # Validate options
20
+ options.validate!
21
+
22
+ # Create migrator
23
+ migrator = Migrator.new(options.input_file)
24
+
25
+ # Perform migration
26
+ migrated_yaml = migrator.migrate
27
+
28
+ # Write to output file or stdout
29
+ if options.output
30
+ File.write(options.output, migrated_yaml)
31
+ $stderr.puts "✓ Migration complete! Converted configuration saved to: #{options.output}"
32
+ else
33
+ puts migrated_yaml
34
+ end
35
+
36
+ exit(0)
37
+ rescue SwarmCLI::ExecutionError => e
38
+ handle_error(e)
39
+ exit(1)
40
+ rescue Interrupt
41
+ $stderr.puts "\n\nMigration cancelled by user"
42
+ exit(130)
43
+ rescue StandardError => e
44
+ handle_error(e)
45
+ exit(1)
46
+ end
47
+
48
+ private
49
+
50
+ def handle_error(error)
51
+ $stderr.puts "Error: #{error.message}"
52
+ end
53
+ end
54
+ end
55
+ end