swarm_memory 2.1.4 → 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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. data/lib/swarm_memory.rb +7 -2
  4. metadata +6 -185
  5. data/lib/claude_swarm/base_executor.rb +0 -133
  6. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  7. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  8. data/lib/claude_swarm/cli.rb +0 -697
  9. data/lib/claude_swarm/commands/ps.rb +0 -215
  10. data/lib/claude_swarm/commands/show.rb +0 -139
  11. data/lib/claude_swarm/configuration.rb +0 -373
  12. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  13. data/lib/claude_swarm/json_handler.rb +0 -91
  14. data/lib/claude_swarm/mcp_generator.rb +0 -243
  15. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  16. data/lib/claude_swarm/openai/executor.rb +0 -256
  17. data/lib/claude_swarm/openai/responses.rb +0 -319
  18. data/lib/claude_swarm/orchestrator.rb +0 -878
  19. data/lib/claude_swarm/process_tracker.rb +0 -78
  20. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  21. data/lib/claude_swarm/session_path.rb +0 -42
  22. data/lib/claude_swarm/settings_generator.rb +0 -77
  23. data/lib/claude_swarm/system_utils.rb +0 -46
  24. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  25. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  27. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  28. data/lib/claude_swarm/version.rb +0 -5
  29. data/lib/claude_swarm/worktree_manager.rb +0 -475
  30. data/lib/claude_swarm/yaml_loader.rb +0 -22
  31. data/lib/claude_swarm.rb +0 -67
  32. data/lib/swarm_cli/cli.rb +0 -201
  33. data/lib/swarm_cli/command_registry.rb +0 -61
  34. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  35. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  36. data/lib/swarm_cli/commands/migrate.rb +0 -55
  37. data/lib/swarm_cli/commands/run.rb +0 -173
  38. data/lib/swarm_cli/config_loader.rb +0 -98
  39. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  40. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  41. data/lib/swarm_cli/interactive_repl.rb +0 -924
  42. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  43. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  44. data/lib/swarm_cli/migrate_options.rb +0 -54
  45. data/lib/swarm_cli/migrator.rb +0 -132
  46. data/lib/swarm_cli/options.rb +0 -151
  47. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  48. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  49. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  50. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  51. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  52. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  53. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  54. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  55. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  56. data/lib/swarm_cli/ui/icons.rb +0 -36
  57. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  58. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  59. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  60. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  61. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  62. data/lib/swarm_cli/version.rb +0 -5
  63. data/lib/swarm_cli.rb +0 -46
  64. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  65. data/lib/swarm_sdk/agent/builder.rb +0 -552
  66. data/lib/swarm_sdk/agent/chat.rb +0 -774
  67. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  68. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  69. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  70. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  71. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  72. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  73. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  75. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  76. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  77. data/lib/swarm_sdk/agent/context.rb +0 -116
  78. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  79. data/lib/swarm_sdk/agent/definition.rb +0 -477
  80. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  81. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  82. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  83. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  84. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  85. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  86. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  87. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  88. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  89. data/lib/swarm_sdk/configuration.rb +0 -135
  90. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  91. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  92. data/lib/swarm_sdk/context_compactor.rb +0 -335
  93. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  94. data/lib/swarm_sdk/context_management/context.rb +0 -328
  95. data/lib/swarm_sdk/defaults.rb +0 -196
  96. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  97. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  98. data/lib/swarm_sdk/hooks/context.rb +0 -197
  99. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  100. data/lib/swarm_sdk/hooks/error.rb +0 -29
  101. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  102. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  103. data/lib/swarm_sdk/hooks/result.rb +0 -150
  104. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  105. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  106. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  107. data/lib/swarm_sdk/log_collector.rb +0 -227
  108. data/lib/swarm_sdk/log_stream.rb +0 -127
  109. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  110. data/lib/swarm_sdk/model_aliases.json +0 -8
  111. data/lib/swarm_sdk/models.json +0 -1
  112. data/lib/swarm_sdk/models.rb +0 -120
  113. data/lib/swarm_sdk/node_context.rb +0 -245
  114. data/lib/swarm_sdk/observer/builder.rb +0 -81
  115. data/lib/swarm_sdk/observer/config.rb +0 -45
  116. data/lib/swarm_sdk/observer/manager.rb +0 -236
  117. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  118. data/lib/swarm_sdk/permissions/config.rb +0 -239
  119. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  120. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  121. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  122. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  123. data/lib/swarm_sdk/plugin.rb +0 -309
  124. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  125. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  126. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  127. data/lib/swarm_sdk/restore_result.rb +0 -65
  128. data/lib/swarm_sdk/result.rb +0 -123
  129. data/lib/swarm_sdk/snapshot.rb +0 -156
  130. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  131. data/lib/swarm_sdk/state_restorer.rb +0 -476
  132. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  133. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  134. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  135. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  136. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  137. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  138. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  139. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  140. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  141. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  142. data/lib/swarm_sdk/swarm.rb +0 -717
  143. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  144. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  145. data/lib/swarm_sdk/tools/bash.rb +0 -282
  146. data/lib/swarm_sdk/tools/clock.rb +0 -44
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  160. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  161. data/lib/swarm_sdk/tools/read.rb +0 -261
  162. data/lib/swarm_sdk/tools/registry.rb +0 -205
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  166. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  167. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  168. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  169. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  170. data/lib/swarm_sdk/tools/think.rb +0 -98
  171. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  172. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  173. data/lib/swarm_sdk/tools/write.rb +0 -112
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  182. data/lib/swarm_sdk/workflow.rb +0 -554
  183. data/lib/swarm_sdk.rb +0 -524
  184. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -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