swarm_memory 2.1.5 → 2.1.6

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. metadata +5 -184
  4. data/lib/claude_swarm/base_executor.rb +0 -133
  5. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  6. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  7. data/lib/claude_swarm/cli.rb +0 -697
  8. data/lib/claude_swarm/commands/ps.rb +0 -215
  9. data/lib/claude_swarm/commands/show.rb +0 -139
  10. data/lib/claude_swarm/configuration.rb +0 -373
  11. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  12. data/lib/claude_swarm/json_handler.rb +0 -91
  13. data/lib/claude_swarm/mcp_generator.rb +0 -230
  14. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  15. data/lib/claude_swarm/openai/executor.rb +0 -256
  16. data/lib/claude_swarm/openai/responses.rb +0 -319
  17. data/lib/claude_swarm/orchestrator.rb +0 -878
  18. data/lib/claude_swarm/process_tracker.rb +0 -78
  19. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  20. data/lib/claude_swarm/session_path.rb +0 -42
  21. data/lib/claude_swarm/settings_generator.rb +0 -77
  22. data/lib/claude_swarm/system_utils.rb +0 -46
  23. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  24. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  25. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  27. data/lib/claude_swarm/version.rb +0 -5
  28. data/lib/claude_swarm/worktree_manager.rb +0 -475
  29. data/lib/claude_swarm/yaml_loader.rb +0 -22
  30. data/lib/claude_swarm.rb +0 -67
  31. data/lib/swarm_cli/cli.rb +0 -201
  32. data/lib/swarm_cli/command_registry.rb +0 -61
  33. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  34. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  35. data/lib/swarm_cli/commands/migrate.rb +0 -55
  36. data/lib/swarm_cli/commands/run.rb +0 -173
  37. data/lib/swarm_cli/config_loader.rb +0 -98
  38. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  39. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  40. data/lib/swarm_cli/interactive_repl.rb +0 -924
  41. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  42. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  43. data/lib/swarm_cli/migrate_options.rb +0 -54
  44. data/lib/swarm_cli/migrator.rb +0 -132
  45. data/lib/swarm_cli/options.rb +0 -151
  46. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  47. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  48. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  49. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  50. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  51. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  52. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  53. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  54. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  55. data/lib/swarm_cli/ui/icons.rb +0 -36
  56. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  57. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  58. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  59. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  60. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  61. data/lib/swarm_cli/version.rb +0 -5
  62. data/lib/swarm_cli.rb +0 -46
  63. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  64. data/lib/swarm_sdk/agent/builder.rb +0 -552
  65. data/lib/swarm_sdk/agent/chat.rb +0 -774
  66. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  67. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  68. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  69. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  70. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  71. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  72. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  73. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  75. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  76. data/lib/swarm_sdk/agent/context.rb +0 -116
  77. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  78. data/lib/swarm_sdk/agent/definition.rb +0 -477
  79. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  80. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  81. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  82. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  83. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  84. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  85. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  86. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  87. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  88. data/lib/swarm_sdk/configuration.rb +0 -135
  89. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  90. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  91. data/lib/swarm_sdk/context_compactor.rb +0 -335
  92. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  93. data/lib/swarm_sdk/context_management/context.rb +0 -328
  94. data/lib/swarm_sdk/defaults.rb +0 -196
  95. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  96. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  97. data/lib/swarm_sdk/hooks/context.rb +0 -197
  98. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  99. data/lib/swarm_sdk/hooks/error.rb +0 -29
  100. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  101. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  102. data/lib/swarm_sdk/hooks/result.rb +0 -150
  103. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  104. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  105. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  106. data/lib/swarm_sdk/log_collector.rb +0 -227
  107. data/lib/swarm_sdk/log_stream.rb +0 -127
  108. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  109. data/lib/swarm_sdk/model_aliases.json +0 -8
  110. data/lib/swarm_sdk/models.json +0 -1
  111. data/lib/swarm_sdk/models.rb +0 -120
  112. data/lib/swarm_sdk/node_context.rb +0 -245
  113. data/lib/swarm_sdk/observer/builder.rb +0 -81
  114. data/lib/swarm_sdk/observer/config.rb +0 -45
  115. data/lib/swarm_sdk/observer/manager.rb +0 -236
  116. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  117. data/lib/swarm_sdk/permissions/config.rb +0 -239
  118. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  119. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  120. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  121. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  122. data/lib/swarm_sdk/plugin.rb +0 -309
  123. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  124. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  125. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  126. data/lib/swarm_sdk/restore_result.rb +0 -65
  127. data/lib/swarm_sdk/result.rb +0 -123
  128. data/lib/swarm_sdk/snapshot.rb +0 -156
  129. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  130. data/lib/swarm_sdk/state_restorer.rb +0 -476
  131. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  132. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  133. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  134. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  135. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  136. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  141. data/lib/swarm_sdk/swarm.rb +0 -717
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/bash.rb +0 -282
  145. data/lib/swarm_sdk/tools/clock.rb +0 -44
  146. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  147. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  148. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  149. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  150. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  151. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  152. data/lib/swarm_sdk/tools/edit.rb +0 -145
  153. data/lib/swarm_sdk/tools/glob.rb +0 -166
  154. data/lib/swarm_sdk/tools/grep.rb +0 -235
  155. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  156. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
  157. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  158. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  159. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  160. data/lib/swarm_sdk/tools/read.rb +0 -261
  161. data/lib/swarm_sdk/tools/registry.rb +0 -205
  162. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  165. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  166. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  167. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  168. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  169. data/lib/swarm_sdk/tools/think.rb +0 -98
  170. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  171. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  172. data/lib/swarm_sdk/tools/write.rb +0 -112
  173. data/lib/swarm_sdk/utils.rb +0 -68
  174. data/lib/swarm_sdk/validation_result.rb +0 -33
  175. data/lib/swarm_sdk/version.rb +0 -5
  176. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  177. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  178. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  179. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  180. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  181. data/lib/swarm_sdk/workflow.rb +0 -554
  182. data/lib/swarm_sdk.rb +0 -524
@@ -1,145 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Loader for creating swarm instances from multiple sources
5
- #
6
- # SwarmLoader loads swarm configurations from:
7
- # - Files: .rb (DSL) or .yml (YAML)
8
- # - YAML strings: Direct YAML content
9
- # - DSL blocks: Inline Ruby blocks
10
- #
11
- # All loaded swarms get hierarchical swarm_id and parent_swarm_id.
12
- #
13
- # ## Features
14
- # - Supports Ruby DSL (.rb files or blocks)
15
- # - Supports YAML (.yml/.yaml files or strings)
16
- # - Sets hierarchical swarm_id based on parent + registration name
17
- # - Isolates loading in separate context
18
- # - Proper error handling for missing/invalid sources
19
- #
20
- # ## Examples
21
- #
22
- # # From file
23
- # swarm = SwarmLoader.load_from_file(
24
- # "./swarms/code_review.rb",
25
- # swarm_id: "main/code_review",
26
- # parent_swarm_id: "main"
27
- # )
28
- #
29
- # # From YAML string
30
- # swarm = SwarmLoader.load_from_yaml_string(
31
- # "version: 2\nswarm:\n name: Test\n...",
32
- # swarm_id: "main/testing",
33
- # parent_swarm_id: "main"
34
- # )
35
- #
36
- # # From block
37
- # swarm = SwarmLoader.load_from_block(
38
- # proc { id "team"; name "Team"; agent :dev { ... } },
39
- # swarm_id: "main/team",
40
- # parent_swarm_id: "main"
41
- # )
42
- #
43
- class SwarmLoader
44
- class << self
45
- # Load a swarm from a file (.rb or .yml)
46
- #
47
- # @param file_path [String] Path to swarm file
48
- # @param swarm_id [String] Hierarchical swarm ID to assign
49
- # @param parent_swarm_id [String] Parent swarm ID
50
- # @return [Swarm] Loaded swarm instance with overridden IDs
51
- # @raise [ConfigurationError] If file not found or unsupported type
52
- def load_from_file(file_path, swarm_id:, parent_swarm_id:)
53
- path = Pathname.new(file_path).expand_path
54
-
55
- raise ConfigurationError, "Swarm file not found: #{path}" unless path.exist?
56
-
57
- # Determine file type and load
58
- case path.extname
59
- when ".rb"
60
- load_from_ruby_file(path, swarm_id, parent_swarm_id)
61
- when ".yml", ".yaml"
62
- load_from_yaml_file(path, swarm_id, parent_swarm_id)
63
- else
64
- raise ConfigurationError, "Unsupported swarm file type: #{path.extname}. Use .rb, .yml, or .yaml"
65
- end
66
- end
67
-
68
- # Load a swarm from YAML string
69
- #
70
- # @param yaml_content [String] YAML configuration content
71
- # @param swarm_id [String] Hierarchical swarm ID to assign
72
- # @param parent_swarm_id [String] Parent swarm ID
73
- # @return [Swarm] Loaded swarm instance with overridden IDs
74
- # @raise [ConfigurationError] If YAML is invalid
75
- def load_from_yaml_string(yaml_content, swarm_id:, parent_swarm_id:)
76
- # Use Configuration to parse YAML string
77
- config = Configuration.new(yaml_content, base_dir: Dir.pwd)
78
- config.load_and_validate
79
- swarm = config.to_swarm
80
-
81
- # Override swarm_id and parent_swarm_id
82
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
83
-
84
- swarm
85
- end
86
-
87
- # Load a swarm from DSL block
88
- #
89
- # @param block [Proc] Block containing SwarmSDK DSL
90
- # @param swarm_id [String] Hierarchical swarm ID to assign
91
- # @param parent_swarm_id [String] Parent swarm ID
92
- # @return [Swarm] Loaded swarm instance with overridden IDs
93
- def load_from_block(block, swarm_id:, parent_swarm_id:)
94
- # Execute block in Builder context
95
- builder = Swarm::Builder.new
96
- builder.instance_eval(&block)
97
- swarm = builder.build_swarm
98
-
99
- # Override swarm_id and parent_swarm_id
100
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
101
-
102
- swarm
103
- end
104
-
105
- private
106
-
107
- # Load swarm from Ruby DSL file
108
- #
109
- # @param path [Pathname] Path to .rb file
110
- # @param swarm_id [String] Swarm ID to assign
111
- # @param parent_swarm_id [String] Parent swarm ID
112
- # @return [Swarm] Loaded swarm with overridden IDs
113
- def load_from_ruby_file(path, swarm_id, parent_swarm_id)
114
- content = File.read(path)
115
-
116
- # Execute DSL in isolated context
117
- # The DSL should return a swarm via SwarmSDK.build { ... }
118
- swarm = eval(content, binding, path.to_s) # rubocop:disable Security/Eval
119
-
120
- # Override swarm_id and parent_swarm_id
121
- # These must be set after build to ensure hierarchical structure
122
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
123
-
124
- swarm
125
- end
126
-
127
- # Load swarm from YAML file
128
- #
129
- # @param path [Pathname] Path to .yml file
130
- # @param swarm_id [String] Swarm ID to assign
131
- # @param parent_swarm_id [String] Parent swarm ID
132
- # @return [Swarm] Loaded swarm with overridden IDs
133
- def load_from_yaml_file(path, swarm_id, parent_swarm_id)
134
- # Use Configuration to load and convert YAML to swarm
135
- config = Configuration.load_file(path.to_s)
136
- swarm = config.to_swarm
137
-
138
- # Override swarm_id and parent_swarm_id
139
- swarm.override_swarm_ids(swarm_id: swarm_id, parent_swarm_id: parent_swarm_id)
140
-
141
- swarm
142
- end
143
- end
144
- end
145
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- # Registry for managing sub-swarms in composable swarms
5
- #
6
- # SwarmRegistry handles lazy loading, caching, and lifecycle management
7
- # of child swarms registered via the `swarms` DSL block.
8
- #
9
- # ## Features
10
- # - Lazy loading: Sub-swarms are only loaded when first accessed
11
- # - Caching: Loaded swarms are cached for reuse
12
- # - Hierarchical IDs: Sub-swarms get IDs based on parent + registration name
13
- # - Context control: keep_context determines if swarm state persists
14
- # - Lifecycle management: Cleanup cascades through all sub-swarms
15
- #
16
- # ## Example
17
- #
18
- # registry = SwarmRegistry.new(parent_swarm_id: "main_app")
19
- # registry.register("code_review", file: "./swarms/code_review.rb", keep_context: true)
20
- #
21
- # # Lazy load on first access
22
- # swarm = registry.load_swarm("code_review")
23
- # # => Swarm with swarm_id = "main_app/code_review"
24
- #
25
- # # Reset if keep_context: false
26
- # registry.reset_if_needed("code_review")
27
- #
28
- class SwarmRegistry
29
- # Initialize a new swarm registry
30
- #
31
- # @param parent_swarm_id [String] ID of the parent swarm
32
- def initialize(parent_swarm_id:)
33
- @parent_swarm_id = parent_swarm_id
34
- @registered_swarms = {}
35
- # Format: { "code_review" => { file: "...", keep_context: true, instance: nil } }
36
- end
37
-
38
- # Register a sub-swarm for lazy loading
39
- #
40
- # @param name [String] Registration name for the swarm
41
- # @param source [Hash] Source specification with :type and :value
42
- # - { type: :file, value: "./path/to/swarm.rb" }
43
- # - { type: :yaml, value: "version: 2\n..." }
44
- # - { type: :block, value: Proc }
45
- # @param keep_context [Boolean] Whether to preserve conversation state between calls (default: true)
46
- # @return [void]
47
- # @raise [ArgumentError] If swarm with same name already registered
48
- def register(name, source:, keep_context: true)
49
- raise ArgumentError, "Swarm '#{name}' already registered" if @registered_swarms.key?(name)
50
-
51
- @registered_swarms[name] = {
52
- source: source,
53
- keep_context: keep_context,
54
- instance: nil, # Lazy load
55
- }
56
- end
57
-
58
- # Check if a swarm is registered
59
- #
60
- # @param name [String] Swarm registration name
61
- # @return [Boolean] True if swarm is registered
62
- def registered?(name)
63
- @registered_swarms.key?(name)
64
- end
65
-
66
- # Load a registered swarm (lazy load + cache)
67
- #
68
- # Loads the swarm from its source (file, yaml, or block) on first access, then caches it.
69
- # Sets hierarchical swarm_id based on parent_swarm_id + registration name.
70
- #
71
- # @param name [String] Swarm registration name
72
- # @return [Swarm] Loaded swarm instance
73
- # @raise [ConfigurationError] If swarm not registered
74
- def load_swarm(name)
75
- entry = @registered_swarms[name]
76
- raise ConfigurationError, "Swarm '#{name}' not registered" unless entry
77
-
78
- # Return cached instance if exists
79
- return entry[:instance] if entry[:instance]
80
-
81
- # Load from appropriate source
82
- swarm_id = "#{@parent_swarm_id}/#{name}" # Hierarchical
83
- source = entry[:source]
84
-
85
- swarm = case source[:type]
86
- when :file
87
- SwarmLoader.load_from_file(
88
- source[:value],
89
- swarm_id: swarm_id,
90
- parent_swarm_id: @parent_swarm_id,
91
- )
92
- when :yaml
93
- SwarmLoader.load_from_yaml_string(
94
- source[:value],
95
- swarm_id: swarm_id,
96
- parent_swarm_id: @parent_swarm_id,
97
- )
98
- when :block
99
- SwarmLoader.load_from_block(
100
- source[:value],
101
- swarm_id: swarm_id,
102
- parent_swarm_id: @parent_swarm_id,
103
- )
104
- else
105
- raise ConfigurationError, "Unknown source type: #{source[:type]}"
106
- end
107
-
108
- entry[:instance] = swarm
109
- swarm
110
- end
111
-
112
- # Reset swarm context if keep_context: false
113
- #
114
- # @param name [String] Swarm registration name
115
- # @return [void]
116
- def reset_if_needed(name)
117
- entry = @registered_swarms[name]
118
- return if entry[:keep_context]
119
-
120
- entry[:instance]&.reset_context!
121
- end
122
-
123
- # Cleanup all registered swarms
124
- #
125
- # Stops all loaded swarm instances and clears the registry.
126
- # Should be called when parent swarm is done.
127
- #
128
- # @return [void]
129
- def shutdown_all
130
- @registered_swarms.each_value do |entry|
131
- entry[:instance]&.cleanup
132
- end
133
- @registered_swarms.clear
134
- end
135
- end
136
- end
@@ -1,282 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Tools
5
- # Bash tool for executing shell commands
6
- #
7
- # Executes commands in a persistent shell session with timeout support.
8
- # Provides comprehensive guidance on proper usage patterns.
9
- class Bash < RubyLLM::Tool
10
- # Factory pattern: declare what parameters this tool needs for instantiation
11
- class << self
12
- def creation_requirements
13
- [:directory]
14
- end
15
- end
16
-
17
- def initialize(directory:)
18
- super()
19
- @directory = File.expand_path(directory)
20
- end
21
-
22
- def name
23
- "Bash"
24
- end
25
-
26
- description <<~DESC
27
- Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
28
-
29
- IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
30
-
31
- Before executing the command, please follow these steps:
32
-
33
- 1. Directory Verification:
34
- - If the command will create new directories or files, first use `ls` to verify the parent directory exists and is the correct location
35
- - For example, before running "mkdir foo/bar", first use `ls foo` to check that "foo" exists and is the intended parent directory
36
-
37
- 2. Command Execution:
38
- - Always quote file paths that contain spaces with double quotes (e.g., cd "path with spaces/file.txt")
39
- - Examples of proper quoting:
40
- - cd "/Users/name/My Documents" (correct)
41
- - cd /Users/name/My Documents (incorrect - will fail)
42
- - python "/path/with spaces/script.py" (correct)
43
- - python /path/with spaces/script.py (incorrect - will fail)
44
- - After ensuring proper quoting, execute the command.
45
- - Capture the output of the command.
46
-
47
- Usage notes:
48
- - The command argument is required.
49
- - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).
50
- - It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
51
- - If the output exceeds 30000 characters, output will be truncated before being returned to you.
52
- - Avoid using Bash with the `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or `echo` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
53
- - File search: Use Glob (NOT find or ls)
54
- - Content search: Use Grep (NOT grep or rg)
55
- - Read files: Use Read (NOT cat/head/tail)
56
- - Edit files: Use Edit (NOT sed/awk)
57
- - Write files: Use Write (NOT echo >/cat <<EOF)
58
- - Communication: Output text directly (NOT echo/printf)
59
- - When issuing multiple commands:
60
- - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
61
- - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together (e.g., `git add . && git commit -m "message" && git push`). For instance, if one operation must complete before another starts (like mkdir before cp, Write before Bash for git operations, or git add before git commit), run these operations sequentially instead.
62
- - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
63
- - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
64
- - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it.
65
- <good-example>
66
- pytest /foo/bar/tests
67
- </good-example>
68
- <bad-example>
69
- cd /foo/bar && pytest tests
70
- </bad-example>
71
- DESC
72
-
73
- param :command,
74
- type: "string",
75
- desc: "The command to execute",
76
- required: true
77
-
78
- param :description,
79
- type: "string",
80
- desc: "Clear, concise description of what this command does in 5-10 words, in active voice. Examples:\nInput: ls\nOutput: List files in current directory\n\nInput: git status\nOutput: Show working tree status\n\nInput: npm install\nOutput: Install package dependencies\n\nInput: mkdir foo\nOutput: Create directory 'foo'",
81
- required: false
82
-
83
- param :timeout,
84
- type: "number",
85
- desc: "Optional timeout in milliseconds (max 600000)",
86
- required: false
87
-
88
- # Backward compatibility aliases - use Defaults module for new code
89
- DEFAULT_TIMEOUT_MS = Defaults::Timeouts::BASH_COMMAND_MS
90
- MAX_TIMEOUT_MS = Defaults::Timeouts::BASH_COMMAND_MAX_MS
91
- MAX_OUTPUT_LENGTH = Defaults::Limits::OUTPUT_CHARACTERS
92
-
93
- # Commands that are ALWAYS blocked for safety reasons
94
- # These cannot be overridden by permissions configuration
95
- ALWAYS_BLOCKED_COMMANDS = [
96
- %r{^rm\s+-rf\s+/$}, # rm -rf / - delete root filesystem
97
- ].freeze
98
-
99
- def execute(command:, description: nil, timeout: nil)
100
- # Validate inputs
101
- return validation_error("command is required") if command.nil? || command.empty?
102
-
103
- # Check against always-blocked commands
104
- blocked_pattern = ALWAYS_BLOCKED_COMMANDS.find { |pattern| pattern.match?(command) }
105
- if blocked_pattern
106
- return blocked_command_error(command, blocked_pattern)
107
- end
108
-
109
- # Validate and set timeout
110
- timeout_ms = timeout || DEFAULT_TIMEOUT_MS
111
- timeout_ms = [timeout_ms, MAX_TIMEOUT_MS].min
112
- timeout_seconds = timeout_ms / 1000.0
113
-
114
- # Execute command with timeout
115
- stdout = +""
116
- stderr = +""
117
- exit_status = nil
118
-
119
- begin
120
- require "open3"
121
- require "timeout"
122
-
123
- Timeout.timeout(timeout_seconds) do
124
- # CRITICAL: Change to agent's directory for subprocess
125
- # This is SAFE because Open3.popen3 creates a subprocess
126
- # The subprocess inherits the directory, but the parent fiber is unaffected
127
- Dir.chdir(@directory) do
128
- Open3.popen3(command) do |stdin, out, err, wait_thr|
129
- stdin.close # Close stdin since we don't send input
130
-
131
- # Read stdout and stderr
132
- stdout = out.read || ""
133
- stderr = err.read || ""
134
- exit_status = wait_thr.value.exitstatus
135
- end
136
- end
137
- end
138
- rescue Timeout::Error
139
- return format_timeout_error(command, timeout_seconds)
140
- rescue Errno::ENOENT => e
141
- return error("Command not found or executable not in PATH: #{e.message}")
142
- rescue Errno::EACCES
143
- return error("Permission denied: Cannot execute command '#{command}'")
144
- rescue StandardError => e
145
- return error("Failed to execute command: #{e.class.name} - #{e.message}")
146
- end
147
-
148
- # Build output
149
- output = format_command_output(command, description, stdout, stderr, exit_status)
150
-
151
- # Truncate if too long
152
- if output.length > MAX_OUTPUT_LENGTH
153
- truncated = output[0...MAX_OUTPUT_LENGTH]
154
- truncated += "\n\n<system-reminder>Output truncated at #{MAX_OUTPUT_LENGTH} characters. The full output was #{output.length} characters.</system-reminder>"
155
- output = truncated
156
- end
157
-
158
- # Add usage reminders for certain patterns
159
- output = add_usage_reminders(output, command)
160
-
161
- output
162
- rescue StandardError => e
163
- error("Unexpected error executing command: #{e.class.name} - #{e.message}")
164
- end
165
-
166
- private
167
-
168
- def validation_error(message)
169
- "<tool_use_error>InputValidationError: #{message}</tool_use_error>"
170
- end
171
-
172
- def error(message)
173
- "Error: #{message}"
174
- end
175
-
176
- def blocked_command_error(command, pattern)
177
- <<~ERROR
178
- Error: Command blocked for safety reasons.
179
- Command: #{command}
180
- Pattern: #{pattern.source}
181
-
182
- <system-reminder>
183
- SECURITY BLOCK: This command is permanently blocked for safety reasons and cannot be executed.
184
-
185
- This is a built-in safety feature of the Bash tool that cannot be overridden by any configuration.
186
- The command matches a pattern that could cause catastrophic system damage.
187
-
188
- DO NOT attempt to:
189
- - Modify the command slightly to bypass this check
190
- - Ask the user to allow this command
191
- - Work around this restriction in any way
192
-
193
- If you need to perform a similar operation safely, consider:
194
- - Using a more specific path instead of system-wide operations
195
- - Using dedicated tools for file operations
196
- - Asking the user for guidance on a safer approach
197
-
198
- This is an UNRECOVERABLE error. You must inform the user that this command cannot be executed for safety reasons.
199
- </system-reminder>
200
- ERROR
201
- end
202
-
203
- def format_timeout_error(command, timeout_seconds)
204
- <<~ERROR
205
- Error: Command timed out after #{timeout_seconds} seconds.
206
- Command: #{command}
207
-
208
- <system-reminder>The command exceeded the timeout limit. Consider:
209
- 1. Breaking the command into smaller steps
210
- 2. Increasing the timeout parameter
211
- 3. Running long-running commands in the background if supported
212
- </system-reminder>
213
- ERROR
214
- end
215
-
216
- def format_command_output(command, description, stdout, stderr, exit_status)
217
- parts = []
218
-
219
- # Add description if provided
220
- parts << "Running: #{description}" if description
221
-
222
- # Add command
223
- parts << "$ #{command}"
224
- parts << ""
225
-
226
- # Add exit status
227
- parts << "Exit code: #{exit_status}"
228
-
229
- # Add stdout if present
230
- if stdout && !stdout.empty?
231
- parts << ""
232
- parts << "STDOUT:"
233
- parts << stdout.chomp
234
- end
235
-
236
- # Add stderr if present
237
- if stderr && !stderr.empty?
238
- parts << ""
239
- parts << "STDERR:"
240
- parts << stderr.chomp
241
- end
242
-
243
- # Add warning for non-zero exit
244
- if exit_status != 0
245
- parts << ""
246
- parts << "<system-reminder>Command exited with non-zero status (#{exit_status}). Check STDERR for error details.</system-reminder>"
247
- end
248
-
249
- parts.join("\n")
250
- end
251
-
252
- def add_usage_reminders(output, command)
253
- reminders = []
254
-
255
- # Detect file operation commands that should use dedicated tools
256
- if command.match?(/\b(cat|head|tail|less|more)\s+/)
257
- reminders << "You used a command to read a file. Consider using the Read tool instead for better formatting and error handling."
258
- end
259
-
260
- if command.match?(/\b(grep|rg|ag)\s+/)
261
- reminders << "You used grep/ripgrep to search files. Consider using the Grep tool instead for structured results."
262
- end
263
-
264
- if command.match?(/\b(find|locate)\s+/)
265
- reminders << "You used find to locate files. Consider using the Glob tool instead for pattern-based file matching."
266
- end
267
-
268
- if command.match?(/\b(sed|awk)\s+/) && !command.include?("|")
269
- reminders << "You used sed/awk for file editing. Consider using the Edit tool instead for safer, tracked file modifications."
270
- end
271
-
272
- if command.match?(/\becho\s+.*>\s*/) || command.match?(/\bcat\s*<</)
273
- reminders << "You used echo/cat with redirection to write a file. Consider using the Write tool instead for proper file creation."
274
- end
275
-
276
- return output if reminders.empty?
277
-
278
- output + "\n\n<system-reminder>\n#{reminders.join("\n\n")}\n</system-reminder>"
279
- end
280
- end
281
- end
282
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmSDK
4
- module Tools
5
- # Clock tool provides current date and time information
6
- #
7
- # Returns current temporal information in a consistent format.
8
- # Agents use this when they need to know what day/time it is.
9
- class Clock < RubyLLM::Tool
10
- description <<~DESC
11
- Get current date and time.
12
-
13
- Returns:
14
- - Current date (YYYY-MM-DD format)
15
- - Current time (HH:MM:SS format)
16
- - Day of week (Monday, Tuesday, etc.)
17
- - ISO 8601 timestamp (full datetime)
18
-
19
- Use this when you need to know what day it is, what time it is,
20
- or to store temporal information (e.g., "As of 2025-10-20...").
21
-
22
- No parameters needed - just call Clock() to get complete temporal information.
23
- DESC
24
-
25
- # No parameters needed
26
-
27
- # Override name to return simple "Clock"
28
- def name
29
- "Clock"
30
- end
31
-
32
- def execute
33
- now = Time.now
34
-
35
- <<~RESULT.chomp
36
- Current date: #{now.strftime("%Y-%m-%d")}
37
- Current time: #{now.strftime("%H:%M:%S")}
38
- Day of week: #{now.strftime("%A")}
39
- ISO 8601: #{now.iso8601}
40
- RESULT
41
- end
42
- end
43
- end
44
- end