claude_swarm 1.0.1 → 1.0.4
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/.claude/commands/release.md +1 -1
- data/.claude/hooks/lint-code-files.rb +65 -0
- data/.rubocop.yml +22 -2
- data/CHANGELOG.md +14 -1
- data/CLAUDE.md +1 -1
- data/CONTRIBUTING.md +69 -0
- data/README.md +27 -2
- data/Rakefile +71 -3
- data/analyze_coverage.rb +94 -0
- data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
- data/docs/v2/README.md +308 -0
- data/docs/v2/guides/claude-code-agents.md +262 -0
- data/docs/v2/guides/complete-tutorial.md +3088 -0
- data/docs/v2/guides/getting-started.md +1456 -0
- data/docs/v2/guides/memory-adapters.md +998 -0
- data/docs/v2/guides/plugins.md +816 -0
- data/docs/v2/guides/quick-start-cli.md +1745 -0
- data/docs/v2/guides/rails-integration.md +1902 -0
- data/docs/v2/guides/swarm-memory.md +599 -0
- data/docs/v2/reference/cli.md +729 -0
- data/docs/v2/reference/ruby-dsl.md +2154 -0
- data/docs/v2/reference/yaml.md +1835 -0
- data/docs-team-swarm.yml +2222 -0
- data/examples/learning-assistant/assistant.md +7 -0
- data/examples/learning-assistant/example-memories/concept-example.md +90 -0
- data/examples/learning-assistant/example-memories/experience-example.md +66 -0
- data/examples/learning-assistant/example-memories/fact-example.md +76 -0
- data/examples/learning-assistant/example-memories/memory-index.md +78 -0
- data/examples/learning-assistant/example-memories/skill-example.md +168 -0
- data/examples/learning-assistant/learning_assistant.rb +34 -0
- data/examples/learning-assistant/learning_assistant.yml +20 -0
- data/examples/v2/dsl/01_basic.rb +44 -0
- data/examples/v2/dsl/02_core_parameters.rb +59 -0
- data/examples/v2/dsl/03_capabilities.rb +71 -0
- data/examples/v2/dsl/04_llm_parameters.rb +56 -0
- data/examples/v2/dsl/05_advanced_flags.rb +73 -0
- data/examples/v2/dsl/06_permissions.rb +80 -0
- data/examples/v2/dsl/07_mcp_server.rb +62 -0
- data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
- data/examples/v2/dsl/09_agent_hooks.rb +67 -0
- data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
- data/examples/v2/dsl/11_delegation.rb +60 -0
- data/examples/v2/dsl/12_complete_integration.rb +137 -0
- data/examples/v2/file_tools_swarm.yml +102 -0
- data/examples/v2/hooks/01_basic_hooks.rb +133 -0
- data/examples/v2/hooks/02_usage_tracking.rb +201 -0
- data/examples/v2/hooks/03_production_monitoring.rb +429 -0
- data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
- data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
- data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
- data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
- data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
- data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
- data/examples/v2/hooks/swarm_summary.sh +44 -0
- data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
- data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
- data/examples/v2/hooks/validate_bash.rb +59 -0
- data/examples/v2/multi_directory_permissions.yml +221 -0
- data/examples/v2/node_context_demo.rb +127 -0
- data/examples/v2/node_workflow.rb +173 -0
- data/examples/v2/path_resolution_demo.rb +216 -0
- data/examples/v2/simple-swarm-v2.rb +90 -0
- data/examples/v2/simple-swarm-v2.yml +62 -0
- data/examples/v2/swarm.yml +71 -0
- data/examples/v2/swarm_with_hooks.yml +61 -0
- data/examples/v2/swarm_with_hooks_simple.yml +25 -0
- data/examples/v2/think_tool_demo.rb +62 -0
- data/exe/swarm +6 -0
- data/lib/claude_swarm/claude_mcp_server.rb +0 -6
- data/lib/claude_swarm/cli.rb +10 -3
- data/lib/claude_swarm/commands/ps.rb +19 -20
- data/lib/claude_swarm/commands/show.rb +1 -1
- data/lib/claude_swarm/configuration.rb +10 -12
- data/lib/claude_swarm/mcp_generator.rb +10 -1
- data/lib/claude_swarm/orchestrator.rb +73 -49
- data/lib/claude_swarm/system_utils.rb +37 -11
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +1 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +7 -2
- data/lib/swarm_cli/cli.rb +201 -0
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
- data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
- data/lib/swarm_cli/commands/migrate.rb +55 -0
- data/lib/swarm_cli/commands/run.rb +173 -0
- data/lib/swarm_cli/config_loader.rb +97 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
- data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
- data/lib/swarm_cli/interactive_repl.rb +918 -0
- data/lib/swarm_cli/mcp_serve_options.rb +44 -0
- data/lib/swarm_cli/mcp_tools_options.rb +59 -0
- data/lib/swarm_cli/migrate_options.rb +54 -0
- data/lib/swarm_cli/migrator.rb +132 -0
- data/lib/swarm_cli/options.rb +151 -0
- data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
- data/lib/swarm_cli/ui/components/content_block.rb +120 -0
- data/lib/swarm_cli/ui/components/divider.rb +57 -0
- data/lib/swarm_cli/ui/components/panel.rb +62 -0
- data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
- data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
- data/lib/swarm_cli/ui/formatters/number.rb +58 -0
- data/lib/swarm_cli/ui/formatters/text.rb +77 -0
- data/lib/swarm_cli/ui/formatters/time.rb +73 -0
- data/lib/swarm_cli/ui/icons.rb +59 -0
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
- data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
- data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
- data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
- data/lib/swarm_cli/version.rb +5 -0
- data/lib/swarm_cli.rb +44 -0
- data/lib/swarm_memory/adapters/base.rb +141 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
- data/lib/swarm_memory/chat_extension.rb +34 -0
- data/lib/swarm_memory/cli/commands.rb +306 -0
- data/lib/swarm_memory/core/entry.rb +37 -0
- data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
- data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
- data/lib/swarm_memory/core/path_normalizer.rb +75 -0
- data/lib/swarm_memory/core/semantic_index.rb +244 -0
- data/lib/swarm_memory/core/storage.rb +288 -0
- data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
- data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
- data/lib/swarm_memory/dsl/memory_config.rb +113 -0
- data/lib/swarm_memory/embeddings/embedder.rb +36 -0
- data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
- data/lib/swarm_memory/errors.rb +21 -0
- data/lib/swarm_memory/integration/cli_registration.rb +30 -0
- data/lib/swarm_memory/integration/configuration.rb +43 -0
- data/lib/swarm_memory/integration/registration.rb +31 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
- data/lib/swarm_memory/optimization/analyzer.rb +244 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
- data/lib/swarm_memory/prompts/memory.md.erb +109 -0
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +42 -0
- data/lib/swarm_memory/search/text_similarity.rb +80 -0
- data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
- data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
- data/lib/swarm_memory/tools/load_skill.rb +313 -0
- data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
- data/lib/swarm_memory/tools/memory_delete.rb +99 -0
- data/lib/swarm_memory/tools/memory_edit.rb +185 -0
- data/lib/swarm_memory/tools/memory_glob.rb +160 -0
- data/lib/swarm_memory/tools/memory_grep.rb +247 -0
- data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
- data/lib/swarm_memory/tools/memory_read.rb +123 -0
- data/lib/swarm_memory/tools/memory_write.rb +231 -0
- data/lib/swarm_memory/utils.rb +50 -0
- data/lib/swarm_memory/version.rb +5 -0
- data/lib/swarm_memory.rb +166 -0
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
- data/lib/swarm_sdk/agent/builder.rb +461 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
- data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
- data/lib/swarm_sdk/agent/chat.rb +1159 -0
- data/lib/swarm_sdk/agent/context.rb +112 -0
- data/lib/swarm_sdk/agent/context_manager.rb +309 -0
- data/lib/swarm_sdk/agent/definition.rb +556 -0
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
- data/lib/swarm_sdk/configuration.rb +296 -0
- data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
- data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
- data/lib/swarm_sdk/context_compactor.rb +340 -0
- data/lib/swarm_sdk/hooks/adapter.rb +359 -0
- data/lib/swarm_sdk/hooks/context.rb +197 -0
- data/lib/swarm_sdk/hooks/definition.rb +80 -0
- data/lib/swarm_sdk/hooks/error.rb +29 -0
- data/lib/swarm_sdk/hooks/executor.rb +146 -0
- data/lib/swarm_sdk/hooks/registry.rb +147 -0
- data/lib/swarm_sdk/hooks/result.rb +150 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
- data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
- data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
- data/lib/swarm_sdk/log_collector.rb +51 -0
- data/lib/swarm_sdk/log_stream.rb +69 -0
- data/lib/swarm_sdk/markdown_parser.rb +75 -0
- data/lib/swarm_sdk/model_aliases.json +5 -0
- data/lib/swarm_sdk/models.json +1 -0
- data/lib/swarm_sdk/models.rb +120 -0
- data/lib/swarm_sdk/node/agent_config.rb +49 -0
- data/lib/swarm_sdk/node/builder.rb +439 -0
- data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
- data/lib/swarm_sdk/node_context.rb +170 -0
- data/lib/swarm_sdk/node_orchestrator.rb +384 -0
- data/lib/swarm_sdk/permissions/config.rb +239 -0
- data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
- data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
- data/lib/swarm_sdk/permissions/validator.rb +173 -0
- data/lib/swarm_sdk/permissions_builder.rb +122 -0
- data/lib/swarm_sdk/plugin.rb +147 -0
- data/lib/swarm_sdk/plugin_registry.rb +101 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
- data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
- data/lib/swarm_sdk/result.rb +97 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
- data/lib/swarm_sdk/swarm/builder.rb +586 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
- data/lib/swarm_sdk/swarm.rb +982 -0
- data/lib/swarm_sdk/tools/bash.rb +274 -0
- data/lib/swarm_sdk/tools/clock.rb +44 -0
- data/lib/swarm_sdk/tools/delegate.rb +164 -0
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
- data/lib/swarm_sdk/tools/edit.rb +150 -0
- data/lib/swarm_sdk/tools/glob.rb +158 -0
- data/lib/swarm_sdk/tools/grep.rb +228 -0
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
- data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
- data/lib/swarm_sdk/tools/read.rb +251 -0
- data/lib/swarm_sdk/tools/registry.rb +93 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
- data/lib/swarm_sdk/tools/think.rb +95 -0
- data/lib/swarm_sdk/tools/todo_write.rb +216 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
- data/lib/swarm_sdk/tools/write.rb +117 -0
- data/lib/swarm_sdk/utils.rb +50 -0
- data/lib/swarm_sdk/version.rb +5 -0
- data/lib/swarm_sdk.rb +157 -0
- data/llm.v2.txt +13407 -0
- data/rubocop/cop/security/no_reflection_methods.rb +47 -0
- data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
- data/swarm_cli.gemspec +57 -0
- data/swarm_memory.gemspec +28 -0
- data/swarm_sdk.gemspec +41 -0
- data/team.yml +1 -1
- data/team_full.yml +1875 -0
- data/{team_v2.yml → team_sdk.yml} +121 -52
- metadata +249 -6
- data/EXAMPLES.md +0 -164
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
module Tools
|
|
5
|
+
# Tool for reading content from memory storage
|
|
6
|
+
#
|
|
7
|
+
# Retrieves content stored by this agent using memory_write.
|
|
8
|
+
# Each agent has its own isolated memory storage.
|
|
9
|
+
class MemoryRead < RubyLLM::Tool
|
|
10
|
+
description <<~DESC
|
|
11
|
+
Read content from your memory storage and retrieve all associated metadata.
|
|
12
|
+
|
|
13
|
+
REQUIRED: Provide the file_path parameter - the path to the memory entry you want to read.
|
|
14
|
+
|
|
15
|
+
**Parameters:**
|
|
16
|
+
- file_path (REQUIRED): Path to memory entry - MUST start with concept/, fact/, skill/, or experience/
|
|
17
|
+
|
|
18
|
+
**MEMORY STRUCTURE - EXACTLY 4 Top-Level Categories (NEVER create others):**
|
|
19
|
+
ALL paths MUST start with one of these 4 fixed categories:
|
|
20
|
+
- concept/{domain}/{name}.md - Abstract ideas (e.g., concept/ruby/classes.md)
|
|
21
|
+
- fact/{subfolder}/{name}.md - Concrete info (e.g., fact/people/john.md)
|
|
22
|
+
- skill/{domain}/{name}.md - Procedures (e.g., skill/debugging/api-errors.md)
|
|
23
|
+
- experience/{name}.md - Lessons (e.g., experience/fixed-bug.md)
|
|
24
|
+
|
|
25
|
+
INVALID: documentation/, reference/, tutorial/, parallel/, analysis/, notes/
|
|
26
|
+
|
|
27
|
+
**Returns:**
|
|
28
|
+
JSON with two fields:
|
|
29
|
+
- content: Markdown content with line numbers (same format as Read tool)
|
|
30
|
+
- metadata: All metadata (title, type, tags, tools, permissions, confidence, etc.)
|
|
31
|
+
|
|
32
|
+
**Examples:**
|
|
33
|
+
- MemoryRead(file_path: "concept/ruby/classes.md") - Read a concept
|
|
34
|
+
- MemoryRead(file_path: "fact/people/john.md") - Read a fact
|
|
35
|
+
- MemoryRead(file_path: "skill/debugging/api-errors.md") - Read a skill before loading it
|
|
36
|
+
|
|
37
|
+
**Important:**
|
|
38
|
+
- Always read entries before editing them with MemoryEdit or MemoryMultiEdit
|
|
39
|
+
- Line numbers in output are for reference only - don't include them when editing
|
|
40
|
+
- Each read is tracked to enforce read-before-edit patterns
|
|
41
|
+
DESC
|
|
42
|
+
|
|
43
|
+
param :file_path,
|
|
44
|
+
desc: "Path to read from memory - MUST start with concept/, fact/, skill/, or experience/ (e.g., 'concept/ruby/classes.md', 'skill/debugging/api.md')",
|
|
45
|
+
required: true
|
|
46
|
+
|
|
47
|
+
# Initialize with storage instance and agent name
|
|
48
|
+
#
|
|
49
|
+
# @param storage [Core::Storage] Storage instance
|
|
50
|
+
# @param agent_name [String, Symbol] Agent identifier
|
|
51
|
+
def initialize(storage:, agent_name:)
|
|
52
|
+
super()
|
|
53
|
+
@storage = storage
|
|
54
|
+
@agent_name = agent_name.to_sym
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Override name to return simple "MemoryRead"
|
|
58
|
+
def name
|
|
59
|
+
"MemoryRead"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Execute the tool
|
|
63
|
+
#
|
|
64
|
+
# @param file_path [String] Path to read from
|
|
65
|
+
# @return [String] JSON with content and metadata
|
|
66
|
+
def execute(file_path:)
|
|
67
|
+
# Register this read in the tracker
|
|
68
|
+
Core::StorageReadTracker.register_read(@agent_name, file_path)
|
|
69
|
+
|
|
70
|
+
# Read full entry with metadata
|
|
71
|
+
entry = @storage.read_entry(file_path: file_path)
|
|
72
|
+
|
|
73
|
+
# Always return JSON format (metadata always exists - at minimum title)
|
|
74
|
+
format_as_json(entry)
|
|
75
|
+
rescue ArgumentError => e
|
|
76
|
+
validation_error(e.message)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def validation_error(message)
|
|
82
|
+
"<tool_use_error>InputValidationError: #{message}</tool_use_error>"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Format entry as JSON with content and metadata
|
|
86
|
+
#
|
|
87
|
+
# Returns a clean JSON format separating content from metadata.
|
|
88
|
+
# This prevents agents from mimicking metadata format when writing.
|
|
89
|
+
#
|
|
90
|
+
# Content includes line numbers (same format as Read tool).
|
|
91
|
+
# Metadata always includes at least title (from Entry).
|
|
92
|
+
# Additional metadata comes from the metadata hash (type, tags, tools, etc.)
|
|
93
|
+
#
|
|
94
|
+
# @param entry [Core::Entry] Entry with content and metadata
|
|
95
|
+
# @return [String] Pretty-printed JSON
|
|
96
|
+
def format_as_json(entry)
|
|
97
|
+
# Build metadata hash with title included
|
|
98
|
+
metadata_hash = { "title" => entry.title }
|
|
99
|
+
metadata_hash.merge!(entry.metadata) if entry.metadata
|
|
100
|
+
|
|
101
|
+
result = {
|
|
102
|
+
content: format_with_line_numbers(entry.content),
|
|
103
|
+
metadata: metadata_hash,
|
|
104
|
+
}
|
|
105
|
+
JSON.pretty_generate(result)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Format content with line numbers (same format as Read tool)
|
|
109
|
+
#
|
|
110
|
+
# @param content [String] Content to format
|
|
111
|
+
# @return [String] Content with line numbers
|
|
112
|
+
def format_with_line_numbers(content)
|
|
113
|
+
lines = content.lines
|
|
114
|
+
output_lines = lines.each_with_index.map do |line, idx|
|
|
115
|
+
line_number = idx + 1
|
|
116
|
+
display_line = line.chomp
|
|
117
|
+
"#{line_number.to_s.rjust(6)}→#{display_line}"
|
|
118
|
+
end
|
|
119
|
+
output_lines.join("\n")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
module Tools
|
|
5
|
+
# Tool for writing content to memory storage
|
|
6
|
+
#
|
|
7
|
+
# Stores content and metadata in persistent, per-agent memory storage.
|
|
8
|
+
# Each agent has its own isolated memory storage that persists across sessions.
|
|
9
|
+
class MemoryWrite < RubyLLM::Tool
|
|
10
|
+
description <<~DESC
|
|
11
|
+
Store content in persistent memory with structured metadata for semantic search and retrieval.
|
|
12
|
+
|
|
13
|
+
IMPORTANT: Content must be 250 words or less. If content exceeds this limit, extract key entities: concepts, experiences, facts, skills,
|
|
14
|
+
then split into multiple focused memories (each under 250 words) that capture ALL important details.
|
|
15
|
+
Link related memories using the 'related' metadata field with memory:// URIs.
|
|
16
|
+
|
|
17
|
+
CRITICAL: ALL 8 required parameters MUST be provided. Do NOT skip any. If you're missing information, ask the user or infer reasonable defaults.
|
|
18
|
+
|
|
19
|
+
REQUIRED PARAMETERS (provide ALL 8):
|
|
20
|
+
1. file_path - Where to store (e.g., 'concept/ruby/classes.md', 'skill/debugging/trace-errors.md')
|
|
21
|
+
2. content - Pure markdown content (no frontmatter)
|
|
22
|
+
3. title - Brief descriptive title
|
|
23
|
+
4. type - Entry category: "concept", "fact", "skill", or "experience"
|
|
24
|
+
5. confidence - How sure you are: "high", "medium", or "low"
|
|
25
|
+
6. tags - JSON string of array of search keywords (e.g., '["ruby", "classes", "oop"]') - be comprehensive!
|
|
26
|
+
7. related - JSON string of array of related memory paths (e.g., '["memory://concept/ruby/modules.md", "memory://concept/ruby/classes.md"]') or '[]' if none
|
|
27
|
+
8. domain - Category like 'programming/ruby', 'people', 'debugging'
|
|
28
|
+
9. source - Where this came from: "user", "documentation", "experimentation", or "inference"
|
|
29
|
+
|
|
30
|
+
OPTIONAL (for skills only):
|
|
31
|
+
- tools - JSON string of array of tool names needed (e.g., '["Read", "Edit", "Bash"]') or '[]' if none
|
|
32
|
+
- permissions - Tool restrictions hash or {}
|
|
33
|
+
|
|
34
|
+
PATH STRUCTURE (EXACTLY 4 TOP-LEVEL CATEGORIES - NEVER CREATE OTHERS):
|
|
35
|
+
Memory has EXACTLY 4 fixed top-level categories. ALL paths MUST start with one of these:
|
|
36
|
+
|
|
37
|
+
1. concept/{domain}/{name}.md - Abstract ideas (e.g., concept/ruby/classes.md)
|
|
38
|
+
2. fact/{subfolder}/{name}.md - Concrete info (e.g., fact/people/john.md)
|
|
39
|
+
3. skill/{domain}/{name}.md - How-to procedures (e.g., skill/debugging/api-errors.md)
|
|
40
|
+
4. experience/{name}.md - Lessons learned (e.g., experience/fixed-cors-bug.md)
|
|
41
|
+
|
|
42
|
+
INVALID (do NOT create): documentation/, reference/, tutorial/, knowledge/, notes/
|
|
43
|
+
These categories do NOT exist. Use concept/, fact/, skill/, or experience/ instead.
|
|
44
|
+
|
|
45
|
+
TAGS ARE CRITICAL: Think "What would I search for in 6 months?" For skills especially, be VERY comprehensive with tags - they're your search index.
|
|
46
|
+
|
|
47
|
+
EXAMPLES:
|
|
48
|
+
- For concept: tags: ['ruby', 'oop', 'classes', 'inheritance', 'methods']
|
|
49
|
+
- For skill: tags: ['debugging', 'api', 'http', 'errors', 'trace', 'network', 'rest']
|
|
50
|
+
DESC
|
|
51
|
+
|
|
52
|
+
param :file_path,
|
|
53
|
+
desc: "Path with .md extension (e.g., 'concept/ruby/classes.md', 'fact/people/john.md')",
|
|
54
|
+
required: true
|
|
55
|
+
|
|
56
|
+
param :content,
|
|
57
|
+
desc: "Content to store (pure markdown, no frontmatter needed)",
|
|
58
|
+
required: true
|
|
59
|
+
|
|
60
|
+
param :title,
|
|
61
|
+
desc: "Brief title describing the content",
|
|
62
|
+
required: true
|
|
63
|
+
|
|
64
|
+
# Metadata parameters (stored in .yml sidecar)
|
|
65
|
+
param :type,
|
|
66
|
+
desc: "Entry type: concept, fact, skill, or experience (matches category: concept/, fact/, skill/, experience/)",
|
|
67
|
+
required: true
|
|
68
|
+
|
|
69
|
+
param :confidence,
|
|
70
|
+
desc: "Confidence level: high, medium, or low (defaults to 'medium' if not specified)",
|
|
71
|
+
required: false
|
|
72
|
+
|
|
73
|
+
param :tags,
|
|
74
|
+
type: "string",
|
|
75
|
+
desc: "JSON string of array of tag strings for searching (e.g., '[\"ruby\", \"oop\"]')",
|
|
76
|
+
required: true
|
|
77
|
+
|
|
78
|
+
param :related,
|
|
79
|
+
type: "string",
|
|
80
|
+
desc: "JSON string of array of related memory path strings (e.g., '[\"memory://concept/ruby/modules.md\", \"memory://concept/ruby/classes.md\"]')",
|
|
81
|
+
required: true
|
|
82
|
+
|
|
83
|
+
param :domain,
|
|
84
|
+
desc: "Category/subcategory (e.g., 'programming/ruby', 'people')",
|
|
85
|
+
required: true
|
|
86
|
+
|
|
87
|
+
param :source,
|
|
88
|
+
desc: "Source of information: user, documentation, experimentation, or inference (defaults to 'user' if not specified)",
|
|
89
|
+
required: false
|
|
90
|
+
|
|
91
|
+
param :tools,
|
|
92
|
+
type: "string",
|
|
93
|
+
desc: "JSON string of array of tool name strings required for this skill (e.g., '[\"Read\", \"Edit\", \"Bash\"]'). Only for type: skill",
|
|
94
|
+
required: false
|
|
95
|
+
|
|
96
|
+
param :permissions,
|
|
97
|
+
type: "object",
|
|
98
|
+
desc: "Tool permission restrictions (same format as swarm config). Only for type: skill",
|
|
99
|
+
required: false
|
|
100
|
+
|
|
101
|
+
# Initialize with storage instance
|
|
102
|
+
#
|
|
103
|
+
# @param storage [Core::Storage] Storage instance
|
|
104
|
+
# @param agent_name [String, Symbol] Agent identifier
|
|
105
|
+
def initialize(storage:, agent_name:)
|
|
106
|
+
super()
|
|
107
|
+
@storage = storage
|
|
108
|
+
@agent_name = agent_name.to_sym
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Override name to return simple "MemoryWrite"
|
|
112
|
+
def name
|
|
113
|
+
"MemoryWrite"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Execute the tool
|
|
117
|
+
#
|
|
118
|
+
# @param file_path [String] Path to store content (.md file)
|
|
119
|
+
# @param content [String] Content to store (pure markdown)
|
|
120
|
+
# @param title [String] Brief title
|
|
121
|
+
# @param type [String, nil] Entry type
|
|
122
|
+
# @param confidence [String, nil] Confidence level
|
|
123
|
+
# @param tags [Array, nil] Tags
|
|
124
|
+
# @param related [Array, nil] Related paths
|
|
125
|
+
# @param domain [String, nil] Domain
|
|
126
|
+
# @param source [String, nil] Source
|
|
127
|
+
# @param tools [Array, nil] Tools required (for skills)
|
|
128
|
+
# @param permissions [Hash, nil] Tool permissions (for skills)
|
|
129
|
+
# @return [String] Success message
|
|
130
|
+
def execute(
|
|
131
|
+
file_path:,
|
|
132
|
+
content:,
|
|
133
|
+
title:,
|
|
134
|
+
type:,
|
|
135
|
+
confidence: nil,
|
|
136
|
+
tags:,
|
|
137
|
+
related:,
|
|
138
|
+
domain:,
|
|
139
|
+
source: nil,
|
|
140
|
+
tools: nil,
|
|
141
|
+
permissions: nil
|
|
142
|
+
)
|
|
143
|
+
# Validate content length (250 word limit)
|
|
144
|
+
word_count = content.split(/\s+/).size
|
|
145
|
+
if word_count > 250
|
|
146
|
+
return validation_error(
|
|
147
|
+
"Content exceeds 250-word limit (#{word_count} words). " \
|
|
148
|
+
"Please extract the key entities and concepts from this content, then split it into multiple smaller, " \
|
|
149
|
+
"focused memories (each under 250 words) that still capture ALL the important details. " \
|
|
150
|
+
"Link related memories together using the 'related' metadata field with memory:// URIs. " \
|
|
151
|
+
"Each memory should cover one specific aspect or concept while preserving completeness.",
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Build metadata hash from params
|
|
156
|
+
# Handle both JSON strings (from LLMs) and Ruby arrays (from tests/code)
|
|
157
|
+
metadata = {}
|
|
158
|
+
metadata["type"] = type if type
|
|
159
|
+
metadata["confidence"] = confidence || "medium" # Default to medium
|
|
160
|
+
metadata["tags"] = parse_array_param(tags) if tags
|
|
161
|
+
metadata["related"] = parse_array_param(related) if related
|
|
162
|
+
metadata["domain"] = domain if domain
|
|
163
|
+
metadata["source"] = source || "user" # Default to user
|
|
164
|
+
metadata["tools"] = parse_array_param(tools) if tools
|
|
165
|
+
metadata["permissions"] = parse_object_param(permissions) if permissions
|
|
166
|
+
|
|
167
|
+
# Write to storage (metadata passed separately, not in content)
|
|
168
|
+
entry = @storage.write(
|
|
169
|
+
file_path: file_path,
|
|
170
|
+
content: content,
|
|
171
|
+
title: title,
|
|
172
|
+
metadata: metadata,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
"Stored at memory://#{file_path} (#{format_bytes(entry.size)})"
|
|
176
|
+
rescue ArgumentError => e
|
|
177
|
+
validation_error(e.message)
|
|
178
|
+
rescue JSON::ParserError => e
|
|
179
|
+
validation_error("Invalid tool parameter JSON format: #{e.message}")
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
def validation_error(message)
|
|
185
|
+
"<tool_use_error>InputValidationError: #{message}</tool_use_error>"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Parse array parameter (handles both JSON strings and Ruby arrays)
|
|
189
|
+
#
|
|
190
|
+
# @param value [String, Array] JSON string or Ruby array
|
|
191
|
+
# @return [Array] Parsed array
|
|
192
|
+
def parse_array_param(value)
|
|
193
|
+
return value if value.is_a?(Array)
|
|
194
|
+
return [] if value.nil? || value.to_s.strip.empty?
|
|
195
|
+
|
|
196
|
+
JSON.parse(value)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Parse object parameter (handles both JSON strings and Ruby hashes)
|
|
200
|
+
#
|
|
201
|
+
# @param value [String, Hash] JSON string or Ruby hash
|
|
202
|
+
# @return [Hash] Parsed hash
|
|
203
|
+
def parse_object_param(value)
|
|
204
|
+
return value if value.is_a?(Hash)
|
|
205
|
+
return {} if value.nil? || value.to_s.strip.empty?
|
|
206
|
+
|
|
207
|
+
begin
|
|
208
|
+
JSON.parse(value)
|
|
209
|
+
rescue JSON::ParserError => e
|
|
210
|
+
# Handle common JSON errors gracefully
|
|
211
|
+
warn("Warning: Failed to parse object parameter: #{e.message}. Returning empty object.")
|
|
212
|
+
{}
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Format bytes to human-readable size
|
|
217
|
+
#
|
|
218
|
+
# @param bytes [Integer] Number of bytes
|
|
219
|
+
# @return [String] Formatted size
|
|
220
|
+
def format_bytes(bytes)
|
|
221
|
+
if bytes >= 1_000_000
|
|
222
|
+
"#{(bytes.to_f / 1_000_000).round(1)}MB"
|
|
223
|
+
elsif bytes >= 1_000
|
|
224
|
+
"#{(bytes.to_f / 1_000).round(1)}KB"
|
|
225
|
+
else
|
|
226
|
+
"#{bytes}B"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
# Shared utility methods for SwarmMemory
|
|
5
|
+
module Utils
|
|
6
|
+
class << self
|
|
7
|
+
# Recursively convert all hash keys to strings
|
|
8
|
+
#
|
|
9
|
+
# Handles nested hashes and arrays containing hashes.
|
|
10
|
+
#
|
|
11
|
+
# @param obj [Object] Object to stringify (Hash, Array, or other)
|
|
12
|
+
# @return [Object] Object with stringified keys (if applicable)
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# Utils.stringify_keys({ name: "test", config: { key: "value" } })
|
|
16
|
+
# # => { "name" => "test", "config" => { "key" => "value" } }
|
|
17
|
+
def stringify_keys(obj)
|
|
18
|
+
case obj
|
|
19
|
+
when Hash
|
|
20
|
+
obj.transform_keys(&:to_s).transform_values { |v| stringify_keys(v) }
|
|
21
|
+
when Array
|
|
22
|
+
obj.map { |item| stringify_keys(item) }
|
|
23
|
+
else
|
|
24
|
+
obj
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Recursively convert all hash keys to symbols
|
|
29
|
+
#
|
|
30
|
+
# Handles nested hashes and arrays containing hashes.
|
|
31
|
+
#
|
|
32
|
+
# @param obj [Object] Object to symbolize (Hash, Array, or other)
|
|
33
|
+
# @return [Object] Object with symbolized keys (if applicable)
|
|
34
|
+
#
|
|
35
|
+
# @example
|
|
36
|
+
# Utils.symbolize_keys({ "name" => "test", "config" => { "key" => "value" } })
|
|
37
|
+
# # => { name: "test", config: { key: "value" } }
|
|
38
|
+
def symbolize_keys(obj)
|
|
39
|
+
case obj
|
|
40
|
+
when Hash
|
|
41
|
+
obj.transform_keys(&:to_sym).transform_values { |v| symbolize_keys(v) }
|
|
42
|
+
when Array
|
|
43
|
+
obj.map { |item| symbolize_keys(item) }
|
|
44
|
+
else
|
|
45
|
+
obj
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/swarm_memory.rb
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load dependencies first (before Zeitwerk)
|
|
4
|
+
require "json"
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
require "time"
|
|
8
|
+
require "date"
|
|
9
|
+
require "set"
|
|
10
|
+
|
|
11
|
+
require "async"
|
|
12
|
+
require "async/semaphore"
|
|
13
|
+
require "swarm_sdk"
|
|
14
|
+
require "ruby_llm"
|
|
15
|
+
|
|
16
|
+
# Try to load informers (optional, for embeddings)
|
|
17
|
+
begin
|
|
18
|
+
require "informers"
|
|
19
|
+
rescue LoadError
|
|
20
|
+
# Informers not available - embeddings will be disabled
|
|
21
|
+
warn("Warning: informers gem not found. Semantic search will be unavailable. Run: gem install informers")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Load errors and version first
|
|
25
|
+
require_relative "swarm_memory/errors"
|
|
26
|
+
require_relative "swarm_memory/version"
|
|
27
|
+
|
|
28
|
+
# Setup Zeitwerk loader
|
|
29
|
+
require "zeitwerk"
|
|
30
|
+
loader = Zeitwerk::Loader.new
|
|
31
|
+
loader.push_dir("#{__dir__}/swarm_memory", namespace: SwarmMemory)
|
|
32
|
+
loader.setup
|
|
33
|
+
|
|
34
|
+
# Explicitly load DSL components and extensions to inject into SwarmSDK
|
|
35
|
+
# These must be loaded after Zeitwerk but before anything uses them
|
|
36
|
+
require_relative "swarm_memory/dsl/memory_config"
|
|
37
|
+
require_relative "swarm_memory/dsl/builder_extension"
|
|
38
|
+
require_relative "swarm_memory/chat_extension"
|
|
39
|
+
|
|
40
|
+
module SwarmMemory
|
|
41
|
+
class << self
|
|
42
|
+
# Registry for custom adapters
|
|
43
|
+
def adapter_registry
|
|
44
|
+
@adapter_registry ||= {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Register a custom adapter
|
|
48
|
+
#
|
|
49
|
+
# @param name [Symbol] Adapter name
|
|
50
|
+
# @param klass [Class] Adapter class (must inherit from Adapters::Base)
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# SwarmMemory.register_adapter(:activerecord, ActiveRecordMemoryAdapter)
|
|
54
|
+
def register_adapter(name, klass)
|
|
55
|
+
unless klass < Adapters::Base
|
|
56
|
+
raise ArgumentError, "Adapter must inherit from SwarmMemory::Adapters::Base"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
adapter_registry[name.to_sym] = klass
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Get adapter class by name
|
|
63
|
+
#
|
|
64
|
+
# @param name [Symbol] Adapter name
|
|
65
|
+
# @return [Class] Adapter class
|
|
66
|
+
# @raise [ArgumentError] If adapter is not found
|
|
67
|
+
def adapter_for(name)
|
|
68
|
+
name = name.to_sym
|
|
69
|
+
|
|
70
|
+
# Check built-in adapters first
|
|
71
|
+
case name
|
|
72
|
+
when :filesystem
|
|
73
|
+
Adapters::FilesystemAdapter
|
|
74
|
+
else
|
|
75
|
+
# Check registry
|
|
76
|
+
adapter_registry[name] || raise(ArgumentError, "Unknown adapter: #{name}. Available: #{available_adapters.join(", ")}")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get list of available adapters
|
|
81
|
+
#
|
|
82
|
+
# @return [Array<Symbol>] List of registered adapter names
|
|
83
|
+
def available_adapters
|
|
84
|
+
[:filesystem] + adapter_registry.keys
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Create individual tool instance
|
|
88
|
+
# Called by SwarmSDK's ToolConfigurator
|
|
89
|
+
#
|
|
90
|
+
# @param tool_name [Symbol] Tool name
|
|
91
|
+
# @param storage [SwarmMemory::Core::Storage] Storage instance
|
|
92
|
+
# @param agent_name [String, Symbol] Agent identifier
|
|
93
|
+
# @param options [Hash] Additional options for special tools like LoadSkill
|
|
94
|
+
# @option options [SwarmSDK::Agent::Chat] :chat Chat instance (for LoadSkill)
|
|
95
|
+
# @option options [SwarmSDK::ToolConfigurator] :tool_configurator Tool configurator (for LoadSkill)
|
|
96
|
+
# @option options [SwarmSDK::Agent::Definition] :agent_definition Agent definition (for LoadSkill)
|
|
97
|
+
# @return [RubyLLM::Tool] Configured tool instance
|
|
98
|
+
def create_tool(tool_name, storage:, agent_name:, **options)
|
|
99
|
+
# Validate storage is present
|
|
100
|
+
if storage.nil?
|
|
101
|
+
raise ConfigurationError,
|
|
102
|
+
"Cannot create #{tool_name} tool: memory storage is nil. " \
|
|
103
|
+
"Did you configure memory for this agent? " \
|
|
104
|
+
"Add: memory { directory '.swarm/agent-memory' }"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
case tool_name.to_sym
|
|
108
|
+
when :MemoryWrite
|
|
109
|
+
Tools::MemoryWrite.new(storage: storage, agent_name: agent_name)
|
|
110
|
+
when :MemoryRead
|
|
111
|
+
Tools::MemoryRead.new(storage: storage, agent_name: agent_name)
|
|
112
|
+
when :MemoryEdit
|
|
113
|
+
Tools::MemoryEdit.new(storage: storage, agent_name: agent_name)
|
|
114
|
+
when :MemoryMultiEdit
|
|
115
|
+
Tools::MemoryMultiEdit.new(storage: storage, agent_name: agent_name)
|
|
116
|
+
when :MemoryDelete
|
|
117
|
+
Tools::MemoryDelete.new(storage: storage)
|
|
118
|
+
when :MemoryGlob
|
|
119
|
+
Tools::MemoryGlob.new(storage: storage)
|
|
120
|
+
when :MemoryGrep
|
|
121
|
+
Tools::MemoryGrep.new(storage: storage)
|
|
122
|
+
when :MemoryDefrag
|
|
123
|
+
Tools::MemoryDefrag.new(storage: storage)
|
|
124
|
+
when :LoadSkill
|
|
125
|
+
# LoadSkill requires additional context for tool swapping
|
|
126
|
+
Tools::LoadSkill.new(
|
|
127
|
+
storage: storage,
|
|
128
|
+
agent_name: agent_name,
|
|
129
|
+
chat: options[:chat],
|
|
130
|
+
tool_configurator: options[:tool_configurator],
|
|
131
|
+
agent_definition: options[:agent_definition],
|
|
132
|
+
)
|
|
133
|
+
else
|
|
134
|
+
raise ConfigurationError, "Unknown memory tool: #{tool_name}"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Convenience method for creating all memory tools at once
|
|
139
|
+
# Useful for direct RubyLLM usage (not via SwarmSDK)
|
|
140
|
+
#
|
|
141
|
+
# @param storage [SwarmMemory::Core::Storage] Storage instance
|
|
142
|
+
# @param agent_name [String, Symbol] Agent identifier
|
|
143
|
+
# @return [Array<RubyLLM::Tool>] All configured memory tools
|
|
144
|
+
def tools_for(storage:, agent_name:)
|
|
145
|
+
[
|
|
146
|
+
Tools::MemoryWrite.new(storage: storage, agent_name: agent_name),
|
|
147
|
+
Tools::MemoryRead.new(storage: storage, agent_name: agent_name),
|
|
148
|
+
Tools::MemoryEdit.new(storage: storage, agent_name: agent_name),
|
|
149
|
+
Tools::MemoryMultiEdit.new(storage: storage, agent_name: agent_name),
|
|
150
|
+
Tools::MemoryDelete.new(storage: storage),
|
|
151
|
+
Tools::MemoryGlob.new(storage: storage),
|
|
152
|
+
Tools::MemoryGrep.new(storage: storage),
|
|
153
|
+
Tools::MemoryDefrag.new(storage: storage),
|
|
154
|
+
]
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Auto-register with SwarmSDK when loaded
|
|
160
|
+
require_relative "swarm_memory/integration/sdk_plugin"
|
|
161
|
+
require_relative "swarm_memory/integration/registration"
|
|
162
|
+
SwarmMemory::Integration::Registration.register!
|
|
163
|
+
|
|
164
|
+
# Auto-register CLI commands with SwarmCLI when loaded
|
|
165
|
+
require_relative "swarm_memory/integration/cli_registration"
|
|
166
|
+
SwarmMemory::Integration::CliRegistration.register!
|