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
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module Tools
5
+ # Write tool for writing content to files
6
+ #
7
+ # Creates new files or overwrites existing files.
8
+ # Enforces read-before-write rule for existing files.
9
+ # Includes validation and usage guidelines via system reminders.
10
+ class Write < RubyLLM::Tool
11
+ include PathResolver
12
+
13
+ description <<~DESC
14
+ Writes a file to the local filesystem.
15
+ This tool will overwrite the existing file if there is one at the provided path.
16
+ If this is an existing file, you MUST use the Read tool first to read the file's contents.
17
+ This tool will fail if you did not read the file first.
18
+ ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
19
+ NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
20
+ Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
21
+
22
+ IMPORTANT - Path Handling:
23
+ - Relative paths (e.g., "tmp/file.txt", "src/main.rb") are resolved relative to your agent's working directory
24
+ - Absolute paths (e.g., "/tmp/file.txt", "/etc/passwd") are treated as system absolute paths
25
+ - When the user says "tmp/file.txt" they mean the tmp directory in your working directory, NOT /tmp
26
+ - Only use absolute paths (starting with /) when explicitly referring to system-level paths
27
+ DESC
28
+
29
+ param :file_path,
30
+ type: "string",
31
+ desc: "Path to the file. Use relative paths (e.g., 'tmp/file.txt') for files in your working directory, or absolute paths (e.g., '/etc/passwd') for system files.",
32
+ required: true
33
+
34
+ param :content,
35
+ type: "string",
36
+ desc: "The content to write to the file",
37
+ required: true
38
+
39
+ # Initialize the Write tool for a specific agent
40
+ #
41
+ # @param agent_name [Symbol, String] The agent identifier
42
+ # @param directory [String] Agent's working directory
43
+ def initialize(agent_name:, directory:)
44
+ super()
45
+ @agent_name = agent_name.to_sym
46
+ @directory = File.expand_path(directory)
47
+ end
48
+
49
+ # Override name to return simple "Write" instead of full class path
50
+ def name
51
+ "Write"
52
+ end
53
+
54
+ def execute(file_path:, content:)
55
+ # Validate inputs
56
+ return validation_error("file_path is required") if file_path.nil? || file_path.to_s.strip.empty?
57
+ return validation_error("content is required") if content.nil?
58
+
59
+ # CRITICAL: Resolve path against agent directory
60
+ resolved_path = resolve_path(file_path)
61
+
62
+ # Check if file already exists (use resolved path)
63
+ file_exists = File.exist?(resolved_path)
64
+
65
+ # Enforce read-before-write for existing files (use resolved path)
66
+ if file_exists && !Stores::ReadTracker.file_read?(@agent_name, resolved_path)
67
+ return validation_error(
68
+ "Cannot write to existing file without reading it first. " \
69
+ "You must use the Read tool on '#{file_path}' before overwriting it. " \
70
+ "This ensures you have context about the file's current contents.",
71
+ )
72
+ end
73
+
74
+ # Create parent directory if it doesn't exist (use resolved path)
75
+ parent_dir = File.dirname(resolved_path)
76
+ FileUtils.mkdir_p(parent_dir) unless File.directory?(parent_dir)
77
+
78
+ # Write the file (use resolved path)
79
+ File.write(resolved_path, content, encoding: "UTF-8")
80
+
81
+ # Build success message
82
+ byte_size = content.bytesize
83
+ line_count = content.lines.count
84
+ action = file_exists ? "overwrote" : "created"
85
+
86
+ message = "Successfully #{action} file: #{file_path} (#{line_count} lines, #{byte_size} bytes)"
87
+
88
+ # Add system reminder for overwritten files
89
+ if file_exists
90
+ reminder = "<system-reminder>You overwrote an existing file. Make sure this was intentional and that you read the file first if you needed to preserve any content.</system-reminder>"
91
+ "#{message}\n\n#{reminder}"
92
+ else
93
+ message
94
+ end
95
+ rescue Errno::EACCES
96
+ error("Permission denied: Cannot write to file '#{file_path}'")
97
+ rescue Errno::EISDIR
98
+ error("Path is a directory, not a file.")
99
+ rescue Errno::ENOENT => e
100
+ error("Failed to create parent directory: #{e.message}")
101
+ rescue StandardError => e
102
+ error("Unexpected error writing file: #{e.class.name} - #{e.message}")
103
+ end
104
+
105
+ private
106
+
107
+ # Helper methods
108
+ def validation_error(message)
109
+ "<tool_use_error>InputValidationError: #{message}</tool_use_error>"
110
+ end
111
+
112
+ def error(message)
113
+ "Error: #{message}"
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ # Shared utility methods for SwarmSDK
5
+ module Utils
6
+ class << self
7
+ # Recursively convert all hash keys to symbols
8
+ #
9
+ # Handles nested hashes and arrays containing hashes.
10
+ #
11
+ # @param obj [Object] Object to symbolize (Hash, Array, or other)
12
+ # @return [Object] Object with symbolized keys (if applicable)
13
+ #
14
+ # @example
15
+ # Utils.symbolize_keys({ "name" => "test", "config" => { "key" => "value" } })
16
+ # # => { name: "test", config: { key: "value" } }
17
+ def symbolize_keys(obj)
18
+ case obj
19
+ when Hash
20
+ obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
21
+ when Array
22
+ obj.map { |item| symbolize_keys(item) }
23
+ else
24
+ obj
25
+ end
26
+ end
27
+
28
+ # Recursively convert all hash keys to strings
29
+ #
30
+ # Handles nested hashes and arrays containing hashes.
31
+ #
32
+ # @param obj [Object] Object to stringify (Hash, Array, or other)
33
+ # @return [Object] Object with stringified keys (if applicable)
34
+ #
35
+ # @example
36
+ # Utils.stringify_keys({ name: "test", config: { key: "value" } })
37
+ # # => { "name" => "test", "config" => { "key" => "value" } }
38
+ def stringify_keys(obj)
39
+ case obj
40
+ when Hash
41
+ obj.transform_keys(&:to_s).transform_values { |v| stringify_keys(v) }
42
+ when Array
43
+ obj.map { |item| stringify_keys(item) }
44
+ else
45
+ obj
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ VERSION = "2.1.1"
5
+ end
data/lib/swarm_sdk.rb ADDED
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+ require "digest"
5
+ require "English"
6
+ require "erb"
7
+ require "fileutils"
8
+ require "json"
9
+ require "logger"
10
+ require "pathname"
11
+ require "securerandom"
12
+ require "set"
13
+ require "yaml"
14
+
15
+ require "async"
16
+ require "async/semaphore"
17
+ require "ruby_llm"
18
+ require "ruby_llm/mcp"
19
+
20
+ require_relative "swarm_sdk/version"
21
+
22
+ require "zeitwerk"
23
+ loader = Zeitwerk::Loader.new
24
+ loader.push_dir("#{__dir__}/swarm_sdk", namespace: SwarmSDK)
25
+ loader.inflector.inflect(
26
+ "cli" => "CLI",
27
+ )
28
+ loader.setup
29
+
30
+ # Load plugin system explicitly (core infrastructure)
31
+ require_relative "swarm_sdk/plugin"
32
+ require_relative "swarm_sdk/plugin_registry"
33
+
34
+ # Load custom providers explicitly (Zeitwerk doesn't eager load by default)
35
+ require_relative "swarm_sdk/providers/openai_with_responses"
36
+
37
+ module SwarmSDK
38
+ class Error < StandardError; end
39
+ class ConfigurationError < Error; end
40
+ class AgentNotFoundError < Error; end
41
+ class CircularDependencyError < Error; end
42
+ class ToolExecutionError < Error; end
43
+ class LLMError < Error; end
44
+ class StateError < Error; end
45
+
46
+ class << self
47
+ # Settings for SwarmSDK (global configuration)
48
+ attr_accessor :settings
49
+
50
+ # Main entry point for DSL
51
+ def build(&block)
52
+ Swarm::Builder.build(&block)
53
+ end
54
+
55
+ # Configure SwarmSDK global settings
56
+ def configure
57
+ self.settings ||= Settings.new
58
+ yield(settings)
59
+ end
60
+
61
+ # Reset settings to defaults
62
+ def reset_settings!
63
+ self.settings = Settings.new
64
+ end
65
+
66
+ # Alias for backward compatibility
67
+ alias_method :configuration, :settings
68
+ alias_method :reset_configuration!, :reset_settings!
69
+ end
70
+
71
+ # Settings class for SwarmSDK global settings (not to be confused with Configuration for YAML loading)
72
+ class Settings
73
+ # WebFetch tool LLM processing configuration
74
+ attr_accessor :webfetch_provider, :webfetch_model, :webfetch_base_url, :webfetch_max_tokens
75
+
76
+ def initialize
77
+ @webfetch_provider = nil
78
+ @webfetch_model = nil
79
+ @webfetch_base_url = nil
80
+ @webfetch_max_tokens = 4096
81
+ end
82
+
83
+ # Check if WebFetch LLM processing is enabled
84
+ def webfetch_llm_enabled?
85
+ !@webfetch_provider.nil? && !@webfetch_model.nil?
86
+ end
87
+ end
88
+
89
+ # Initialize default settings
90
+ self.settings = Settings.new
91
+ end
92
+
93
+ # Automatically configure RubyLLM from environment variables
94
+ # This makes SwarmSDK "just work" when users set standard ENV variables
95
+ RubyLLM.configure do |config|
96
+ # Only set if config not already set (||= handles nil ENV values gracefully)
97
+
98
+ # OpenAI
99
+ config.openai_api_key ||= ENV["OPENAI_API_KEY"]
100
+ config.openai_api_base ||= ENV["OPENAI_API_BASE"]
101
+ config.openai_organization_id ||= ENV["OPENAI_ORG_ID"]
102
+ config.openai_project_id ||= ENV["OPENAI_PROJECT_ID"]
103
+
104
+ # Anthropic
105
+ config.anthropic_api_key ||= ENV["ANTHROPIC_API_KEY"]
106
+
107
+ # Google Gemini
108
+ config.gemini_api_key ||= ENV["GEMINI_API_KEY"]
109
+
110
+ # Google Vertex AI (note: vertexai, not vertex_ai)
111
+ config.vertexai_project_id ||= ENV["GOOGLE_CLOUD_PROJECT"] || ENV["VERTEXAI_PROJECT_ID"]
112
+ config.vertexai_location ||= ENV["GOOGLE_CLOUD_LOCATION"] || ENV["VERTEXAI_LOCATION"]
113
+
114
+ # DeepSeek
115
+ config.deepseek_api_key ||= ENV["DEEPSEEK_API_KEY"]
116
+
117
+ # Mistral
118
+ config.mistral_api_key ||= ENV["MISTRAL_API_KEY"]
119
+
120
+ # Perplexity
121
+ config.perplexity_api_key ||= ENV["PERPLEXITY_API_KEY"]
122
+
123
+ # OpenRouter
124
+ config.openrouter_api_key ||= ENV["OPENROUTER_API_KEY"]
125
+
126
+ # AWS Bedrock
127
+ config.bedrock_api_key ||= ENV["AWS_ACCESS_KEY_ID"]
128
+ config.bedrock_secret_key ||= ENV["AWS_SECRET_ACCESS_KEY"]
129
+ config.bedrock_region ||= ENV["AWS_REGION"]
130
+ config.bedrock_session_token ||= ENV["AWS_SESSION_TOKEN"]
131
+
132
+ # Ollama (local)
133
+ config.ollama_api_base ||= ENV["OLLAMA_API_BASE"]
134
+
135
+ # GPUStack (local)
136
+ config.gpustack_api_base ||= ENV["GPUSTACK_API_BASE"]
137
+ config.gpustack_api_key ||= ENV["GPUSTACK_API_KEY"]
138
+ end
139
+
140
+ # monkey patches
141
+ # ruby_llm/mcp
142
+ # - add `id` when sending "notifications/initialized" message: https://github.com/patvice/ruby_llm-mcp/issues/65
143
+ # - remove `to_sym` on MCP parameter type: https://github.com/patvice/ruby_llm-mcp/issues/62#issuecomment-3421488406
144
+ require "ruby_llm/mcp/notifications/initialize"
145
+ require "ruby_llm/mcp/parameter"
146
+
147
+ module RubyLLM
148
+ module MCP
149
+ module Notifications
150
+ class Initialize
151
+ def call
152
+ @coordinator.request(notification_body, add_id: true, wait_for_response: false)
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end