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.
- checksums.yaml +4 -4
- data/lib/swarm_memory/version.rb +1 -1
- metadata +5 -184
- data/lib/claude_swarm/base_executor.rb +0 -133
- data/lib/claude_swarm/claude_code_executor.rb +0 -349
- data/lib/claude_swarm/claude_mcp_server.rb +0 -78
- data/lib/claude_swarm/cli.rb +0 -697
- data/lib/claude_swarm/commands/ps.rb +0 -215
- data/lib/claude_swarm/commands/show.rb +0 -139
- data/lib/claude_swarm/configuration.rb +0 -373
- data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
- data/lib/claude_swarm/json_handler.rb +0 -91
- data/lib/claude_swarm/mcp_generator.rb +0 -230
- data/lib/claude_swarm/openai/chat_completion.rb +0 -256
- data/lib/claude_swarm/openai/executor.rb +0 -256
- data/lib/claude_swarm/openai/responses.rb +0 -319
- data/lib/claude_swarm/orchestrator.rb +0 -878
- data/lib/claude_swarm/process_tracker.rb +0 -78
- data/lib/claude_swarm/session_cost_calculator.rb +0 -209
- data/lib/claude_swarm/session_path.rb +0 -42
- data/lib/claude_swarm/settings_generator.rb +0 -77
- data/lib/claude_swarm/system_utils.rb +0 -46
- data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
- data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
- data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
- data/lib/claude_swarm/tools/task_tool.rb +0 -63
- data/lib/claude_swarm/version.rb +0 -5
- data/lib/claude_swarm/worktree_manager.rb +0 -475
- data/lib/claude_swarm/yaml_loader.rb +0 -22
- data/lib/claude_swarm.rb +0 -67
- data/lib/swarm_cli/cli.rb +0 -201
- data/lib/swarm_cli/command_registry.rb +0 -61
- data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
- data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
- data/lib/swarm_cli/commands/migrate.rb +0 -55
- data/lib/swarm_cli/commands/run.rb +0 -173
- data/lib/swarm_cli/config_loader.rb +0 -98
- data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
- data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
- data/lib/swarm_cli/interactive_repl.rb +0 -924
- data/lib/swarm_cli/mcp_serve_options.rb +0 -44
- data/lib/swarm_cli/mcp_tools_options.rb +0 -59
- data/lib/swarm_cli/migrate_options.rb +0 -54
- data/lib/swarm_cli/migrator.rb +0 -132
- data/lib/swarm_cli/options.rb +0 -151
- data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
- data/lib/swarm_cli/ui/components/content_block.rb +0 -120
- data/lib/swarm_cli/ui/components/divider.rb +0 -57
- data/lib/swarm_cli/ui/components/panel.rb +0 -62
- data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
- data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
- data/lib/swarm_cli/ui/formatters/number.rb +0 -58
- data/lib/swarm_cli/ui/formatters/text.rb +0 -77
- data/lib/swarm_cli/ui/formatters/time.rb +0 -73
- data/lib/swarm_cli/ui/icons.rb +0 -36
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
- data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
- data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
- data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
- data/lib/swarm_cli/version.rb +0 -5
- data/lib/swarm_cli.rb +0 -46
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
- data/lib/swarm_sdk/agent/builder.rb +0 -552
- data/lib/swarm_sdk/agent/chat.rb +0 -774
- data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
- data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
- data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
- data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
- data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
- data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
- data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
- data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
- data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
- data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
- data/lib/swarm_sdk/agent/context.rb +0 -116
- data/lib/swarm_sdk/agent/context_manager.rb +0 -315
- data/lib/swarm_sdk/agent/definition.rb +0 -477
- data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
- data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
- data/lib/swarm_sdk/builders/base_builder.rb +0 -409
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
- data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
- data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
- data/lib/swarm_sdk/concerns/validatable.rb +0 -55
- data/lib/swarm_sdk/configuration/parser.rb +0 -353
- data/lib/swarm_sdk/configuration/translator.rb +0 -255
- data/lib/swarm_sdk/configuration.rb +0 -135
- data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
- data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
- data/lib/swarm_sdk/context_compactor.rb +0 -335
- data/lib/swarm_sdk/context_management/builder.rb +0 -128
- data/lib/swarm_sdk/context_management/context.rb +0 -328
- data/lib/swarm_sdk/defaults.rb +0 -196
- data/lib/swarm_sdk/events_to_messages.rb +0 -199
- data/lib/swarm_sdk/hooks/adapter.rb +0 -359
- data/lib/swarm_sdk/hooks/context.rb +0 -197
- data/lib/swarm_sdk/hooks/definition.rb +0 -80
- data/lib/swarm_sdk/hooks/error.rb +0 -29
- data/lib/swarm_sdk/hooks/executor.rb +0 -146
- data/lib/swarm_sdk/hooks/registry.rb +0 -147
- data/lib/swarm_sdk/hooks/result.rb +0 -150
- data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
- data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
- data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
- data/lib/swarm_sdk/log_collector.rb +0 -227
- data/lib/swarm_sdk/log_stream.rb +0 -127
- data/lib/swarm_sdk/markdown_parser.rb +0 -75
- data/lib/swarm_sdk/model_aliases.json +0 -8
- data/lib/swarm_sdk/models.json +0 -1
- data/lib/swarm_sdk/models.rb +0 -120
- data/lib/swarm_sdk/node_context.rb +0 -245
- data/lib/swarm_sdk/observer/builder.rb +0 -81
- data/lib/swarm_sdk/observer/config.rb +0 -45
- data/lib/swarm_sdk/observer/manager.rb +0 -236
- data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
- data/lib/swarm_sdk/permissions/config.rb +0 -239
- data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
- data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
- data/lib/swarm_sdk/permissions/validator.rb +0 -173
- data/lib/swarm_sdk/permissions_builder.rb +0 -122
- data/lib/swarm_sdk/plugin.rb +0 -309
- data/lib/swarm_sdk/plugin_registry.rb +0 -101
- data/lib/swarm_sdk/proc_helpers.rb +0 -53
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
- data/lib/swarm_sdk/restore_result.rb +0 -65
- data/lib/swarm_sdk/result.rb +0 -123
- data/lib/swarm_sdk/snapshot.rb +0 -156
- data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
- data/lib/swarm_sdk/state_restorer.rb +0 -476
- data/lib/swarm_sdk/state_snapshot.rb +0 -334
- data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
- data/lib/swarm_sdk/swarm/builder.rb +0 -249
- data/lib/swarm_sdk/swarm/executor.rb +0 -213
- data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
- data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
- data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
- data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
- data/lib/swarm_sdk/swarm.rb +0 -717
- data/lib/swarm_sdk/swarm_loader.rb +0 -145
- data/lib/swarm_sdk/swarm_registry.rb +0 -136
- data/lib/swarm_sdk/tools/bash.rb +0 -282
- data/lib/swarm_sdk/tools/clock.rb +0 -44
- data/lib/swarm_sdk/tools/delegate.rb +0 -267
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
- data/lib/swarm_sdk/tools/edit.rb +0 -145
- data/lib/swarm_sdk/tools/glob.rb +0 -166
- data/lib/swarm_sdk/tools/grep.rb +0 -235
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
- data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
- data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
- data/lib/swarm_sdk/tools/read.rb +0 -261
- data/lib/swarm_sdk/tools/registry.rb +0 -205
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
- data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
- data/lib/swarm_sdk/tools/think.rb +0 -98
- data/lib/swarm_sdk/tools/todo_write.rb +0 -235
- data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
- data/lib/swarm_sdk/tools/write.rb +0 -112
- data/lib/swarm_sdk/utils.rb +0 -68
- data/lib/swarm_sdk/validation_result.rb +0 -33
- data/lib/swarm_sdk/version.rb +0 -5
- data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
- data/lib/swarm_sdk/workflow/builder.rb +0 -143
- data/lib/swarm_sdk/workflow/executor.rb +0 -497
- data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
- data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
- data/lib/swarm_sdk/workflow.rb +0 -554
- 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
|
data/lib/swarm_sdk/tools/bash.rb
DELETED
|
@@ -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
|