swarm_sdk 2.0.0.pre.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/lib/swarm_sdk/agent/builder.rb +333 -0
  3. data/lib/swarm_sdk/agent/chat/context_tracker.rb +271 -0
  4. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  5. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +99 -0
  6. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +114 -0
  7. data/lib/swarm_sdk/agent/chat.rb +779 -0
  8. data/lib/swarm_sdk/agent/context.rb +108 -0
  9. data/lib/swarm_sdk/agent/definition.rb +335 -0
  10. data/lib/swarm_sdk/configuration.rb +251 -0
  11. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  12. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  13. data/lib/swarm_sdk/context_compactor.rb +340 -0
  14. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  15. data/lib/swarm_sdk/hooks/context.rb +163 -0
  16. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  17. data/lib/swarm_sdk/hooks/error.rb +29 -0
  18. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  19. data/lib/swarm_sdk/hooks/registry.rb +143 -0
  20. data/lib/swarm_sdk/hooks/result.rb +150 -0
  21. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  22. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  23. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  24. data/lib/swarm_sdk/log_collector.rb +83 -0
  25. data/lib/swarm_sdk/log_stream.rb +69 -0
  26. data/lib/swarm_sdk/markdown_parser.rb +46 -0
  27. data/lib/swarm_sdk/permissions/config.rb +239 -0
  28. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  29. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  30. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  31. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  32. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +237 -0
  33. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  34. data/lib/swarm_sdk/result.rb +97 -0
  35. data/lib/swarm_sdk/swarm/agent_initializer.rb +224 -0
  36. data/lib/swarm_sdk/swarm/all_agents_builder.rb +62 -0
  37. data/lib/swarm_sdk/swarm/builder.rb +240 -0
  38. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  39. data/lib/swarm_sdk/swarm/tool_configurator.rb +267 -0
  40. data/lib/swarm_sdk/swarm.rb +837 -0
  41. data/lib/swarm_sdk/tools/bash.rb +274 -0
  42. data/lib/swarm_sdk/tools/delegate.rb +152 -0
  43. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  44. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  45. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  46. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  47. data/lib/swarm_sdk/tools/edit.rb +150 -0
  48. data/lib/swarm_sdk/tools/glob.rb +158 -0
  49. data/lib/swarm_sdk/tools/grep.rb +231 -0
  50. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  51. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  52. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  53. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  54. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  55. data/lib/swarm_sdk/tools/read.rb +251 -0
  56. data/lib/swarm_sdk/tools/registry.rb +73 -0
  57. data/lib/swarm_sdk/tools/scratchpad_list.rb +88 -0
  58. data/lib/swarm_sdk/tools/scratchpad_read.rb +59 -0
  59. data/lib/swarm_sdk/tools/scratchpad_write.rb +88 -0
  60. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  61. data/lib/swarm_sdk/tools/stores/scratchpad.rb +153 -0
  62. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  63. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  64. data/lib/swarm_sdk/tools/write.rb +117 -0
  65. data/lib/swarm_sdk/utils.rb +50 -0
  66. data/lib/swarm_sdk/version.rb +5 -0
  67. data/lib/swarm_sdk.rb +69 -0
  68. metadata +169 -0
@@ -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.0.0-2"
5
+ end
data/lib/swarm_sdk.rb ADDED
@@ -0,0 +1,69 @@
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
+ module SwarmSDK
21
+ end
22
+
23
+ require "zeitwerk"
24
+ loader = Zeitwerk::Loader.new
25
+ loader.push_dir("#{__dir__}/swarm_sdk", namespace: SwarmSDK)
26
+ loader.inflector.inflect(
27
+ "cli" => "CLI",
28
+ )
29
+ loader.setup
30
+
31
+ # Load custom providers explicitly (Zeitwerk doesn't eager load by default)
32
+ require_relative "swarm_sdk/providers/openai_with_responses"
33
+
34
+ module SwarmSDK
35
+ class Error < StandardError; end
36
+ class ConfigurationError < Error; end
37
+ class AgentNotFoundError < Error; end
38
+ class CircularDependencyError < Error; end
39
+ class ToolExecutionError < Error; end
40
+ class LLMError < Error; end
41
+ class StateError < Error; end
42
+
43
+ class << self
44
+ # Refresh RubyLLM model registry silently (without log output)
45
+ #
46
+ # By default, RubyLLM.models.refresh! outputs INFO level logs about
47
+ # fetching models from providers. This method temporarily raises the
48
+ # log level to suppress those messages, which is useful for CLI tools
49
+ # that want clean output.
50
+ #
51
+ # @example
52
+ # SwarmSDK.refresh_models_silently
53
+ #
54
+ # @return [void]
55
+ def refresh_models_silently
56
+ original_level = RubyLLM.logger.level
57
+ RubyLLM.logger.level = Logger::ERROR
58
+
59
+ RubyLLM.models.refresh!
60
+ ensure
61
+ RubyLLM.logger.level = original_level
62
+ end
63
+
64
+ # Main entry point for DSL
65
+ def build(&block)
66
+ Swarm::Builder.build(&block)
67
+ end
68
+ end
69
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swarm_sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0.pre.2
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: async
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby_llm
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.8'
40
+ - !ruby/object:Gem::Dependency
41
+ name: ruby_llm-mcp
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: zeitwerk
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.6'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.6'
68
+ description: |
69
+ SwarmSDK is a complete reimagining of Claude Swarm that runs all AI agents in a single process
70
+ using RubyLLM for LLM interactions. Define collaborative AI agents in simple Markdown files with
71
+ YAML frontmatter, and orchestrate them without the overhead of multiple processes or MCP
72
+ inter-process communication. Perfect for building lightweight, efficient AI agent teams with
73
+ specialized roles and capabilities.
74
+ email:
75
+ - parrudaj@gmail.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - lib/swarm_sdk.rb
81
+ - lib/swarm_sdk/agent/builder.rb
82
+ - lib/swarm_sdk/agent/chat.rb
83
+ - lib/swarm_sdk/agent/chat/context_tracker.rb
84
+ - lib/swarm_sdk/agent/chat/hook_integration.rb
85
+ - lib/swarm_sdk/agent/chat/logging_helpers.rb
86
+ - lib/swarm_sdk/agent/chat/system_reminder_injector.rb
87
+ - lib/swarm_sdk/agent/context.rb
88
+ - lib/swarm_sdk/agent/definition.rb
89
+ - lib/swarm_sdk/configuration.rb
90
+ - lib/swarm_sdk/context_compactor.rb
91
+ - lib/swarm_sdk/context_compactor/metrics.rb
92
+ - lib/swarm_sdk/context_compactor/token_counter.rb
93
+ - lib/swarm_sdk/hooks/adapter.rb
94
+ - lib/swarm_sdk/hooks/context.rb
95
+ - lib/swarm_sdk/hooks/definition.rb
96
+ - lib/swarm_sdk/hooks/error.rb
97
+ - lib/swarm_sdk/hooks/executor.rb
98
+ - lib/swarm_sdk/hooks/registry.rb
99
+ - lib/swarm_sdk/hooks/result.rb
100
+ - lib/swarm_sdk/hooks/shell_executor.rb
101
+ - lib/swarm_sdk/hooks/tool_call.rb
102
+ - lib/swarm_sdk/hooks/tool_result.rb
103
+ - lib/swarm_sdk/log_collector.rb
104
+ - lib/swarm_sdk/log_stream.rb
105
+ - lib/swarm_sdk/markdown_parser.rb
106
+ - lib/swarm_sdk/permissions/config.rb
107
+ - lib/swarm_sdk/permissions/error_formatter.rb
108
+ - lib/swarm_sdk/permissions/path_matcher.rb
109
+ - lib/swarm_sdk/permissions/validator.rb
110
+ - lib/swarm_sdk/permissions_builder.rb
111
+ - lib/swarm_sdk/prompts/base_system_prompt.md.erb
112
+ - lib/swarm_sdk/providers/openai_with_responses.rb
113
+ - lib/swarm_sdk/result.rb
114
+ - lib/swarm_sdk/swarm.rb
115
+ - lib/swarm_sdk/swarm/agent_initializer.rb
116
+ - lib/swarm_sdk/swarm/all_agents_builder.rb
117
+ - lib/swarm_sdk/swarm/builder.rb
118
+ - lib/swarm_sdk/swarm/mcp_configurator.rb
119
+ - lib/swarm_sdk/swarm/tool_configurator.rb
120
+ - lib/swarm_sdk/tools/bash.rb
121
+ - lib/swarm_sdk/tools/delegate.rb
122
+ - lib/swarm_sdk/tools/document_converters/base_converter.rb
123
+ - lib/swarm_sdk/tools/document_converters/docx_converter.rb
124
+ - lib/swarm_sdk/tools/document_converters/pdf_converter.rb
125
+ - lib/swarm_sdk/tools/document_converters/xlsx_converter.rb
126
+ - lib/swarm_sdk/tools/edit.rb
127
+ - lib/swarm_sdk/tools/glob.rb
128
+ - lib/swarm_sdk/tools/grep.rb
129
+ - lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb
130
+ - lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb
131
+ - lib/swarm_sdk/tools/image_formats/tiff_builder.rb
132
+ - lib/swarm_sdk/tools/multi_edit.rb
133
+ - lib/swarm_sdk/tools/path_resolver.rb
134
+ - lib/swarm_sdk/tools/read.rb
135
+ - lib/swarm_sdk/tools/registry.rb
136
+ - lib/swarm_sdk/tools/scratchpad_list.rb
137
+ - lib/swarm_sdk/tools/scratchpad_read.rb
138
+ - lib/swarm_sdk/tools/scratchpad_write.rb
139
+ - lib/swarm_sdk/tools/stores/read_tracker.rb
140
+ - lib/swarm_sdk/tools/stores/scratchpad.rb
141
+ - lib/swarm_sdk/tools/stores/todo_manager.rb
142
+ - lib/swarm_sdk/tools/todo_write.rb
143
+ - lib/swarm_sdk/tools/write.rb
144
+ - lib/swarm_sdk/utils.rb
145
+ - lib/swarm_sdk/version.rb
146
+ homepage: https://github.com/parruda/claude-swarm
147
+ licenses:
148
+ - MIT
149
+ metadata:
150
+ source_code_uri: https://github.com/parruda/claude-swarm
151
+ changelog_uri: https://github.com/parruda/claude-swarm/blob/main/CHANGELOG.md
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 3.2.0
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubygems_version: 3.6.2
167
+ specification_version: 4
168
+ summary: Lightweight multi-agent AI orchestration using RubyLLM
169
+ test_files: []