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.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +1 -1
  3. data/.claude/hooks/lint-code-files.rb +65 -0
  4. data/.rubocop.yml +22 -2
  5. data/CHANGELOG.md +14 -1
  6. data/CLAUDE.md +1 -1
  7. data/CONTRIBUTING.md +69 -0
  8. data/README.md +27 -2
  9. data/Rakefile +71 -3
  10. data/analyze_coverage.rb +94 -0
  11. data/docs/v2/CHANGELOG.swarm_cli.md +43 -0
  12. data/docs/v2/CHANGELOG.swarm_memory.md +379 -0
  13. data/docs/v2/CHANGELOG.swarm_sdk.md +362 -0
  14. data/docs/v2/README.md +308 -0
  15. data/docs/v2/guides/claude-code-agents.md +262 -0
  16. data/docs/v2/guides/complete-tutorial.md +3088 -0
  17. data/docs/v2/guides/getting-started.md +1456 -0
  18. data/docs/v2/guides/memory-adapters.md +998 -0
  19. data/docs/v2/guides/plugins.md +816 -0
  20. data/docs/v2/guides/quick-start-cli.md +1745 -0
  21. data/docs/v2/guides/rails-integration.md +1902 -0
  22. data/docs/v2/guides/swarm-memory.md +599 -0
  23. data/docs/v2/reference/cli.md +729 -0
  24. data/docs/v2/reference/ruby-dsl.md +2154 -0
  25. data/docs/v2/reference/yaml.md +1835 -0
  26. data/docs-team-swarm.yml +2222 -0
  27. data/examples/learning-assistant/assistant.md +7 -0
  28. data/examples/learning-assistant/example-memories/concept-example.md +90 -0
  29. data/examples/learning-assistant/example-memories/experience-example.md +66 -0
  30. data/examples/learning-assistant/example-memories/fact-example.md +76 -0
  31. data/examples/learning-assistant/example-memories/memory-index.md +78 -0
  32. data/examples/learning-assistant/example-memories/skill-example.md +168 -0
  33. data/examples/learning-assistant/learning_assistant.rb +34 -0
  34. data/examples/learning-assistant/learning_assistant.yml +20 -0
  35. data/examples/v2/dsl/01_basic.rb +44 -0
  36. data/examples/v2/dsl/02_core_parameters.rb +59 -0
  37. data/examples/v2/dsl/03_capabilities.rb +71 -0
  38. data/examples/v2/dsl/04_llm_parameters.rb +56 -0
  39. data/examples/v2/dsl/05_advanced_flags.rb +73 -0
  40. data/examples/v2/dsl/06_permissions.rb +80 -0
  41. data/examples/v2/dsl/07_mcp_server.rb +62 -0
  42. data/examples/v2/dsl/08_swarm_hooks.rb +53 -0
  43. data/examples/v2/dsl/09_agent_hooks.rb +67 -0
  44. data/examples/v2/dsl/10_all_agents_hooks.rb +67 -0
  45. data/examples/v2/dsl/11_delegation.rb +60 -0
  46. data/examples/v2/dsl/12_complete_integration.rb +137 -0
  47. data/examples/v2/file_tools_swarm.yml +102 -0
  48. data/examples/v2/hooks/01_basic_hooks.rb +133 -0
  49. data/examples/v2/hooks/02_usage_tracking.rb +201 -0
  50. data/examples/v2/hooks/03_production_monitoring.rb +429 -0
  51. data/examples/v2/hooks/agent_stop_exit_0.yml +21 -0
  52. data/examples/v2/hooks/agent_stop_exit_1.yml +21 -0
  53. data/examples/v2/hooks/agent_stop_exit_2.yml +26 -0
  54. data/examples/v2/hooks/multiple_hooks_all_pass.yml +37 -0
  55. data/examples/v2/hooks/multiple_hooks_first_fails.yml +37 -0
  56. data/examples/v2/hooks/multiple_hooks_second_fails.yml +37 -0
  57. data/examples/v2/hooks/multiple_hooks_warnings.yml +37 -0
  58. data/examples/v2/hooks/post_tool_use_exit_0.yml +24 -0
  59. data/examples/v2/hooks/post_tool_use_exit_1.yml +24 -0
  60. data/examples/v2/hooks/post_tool_use_exit_2.yml +24 -0
  61. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_0.yml +26 -0
  62. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_1.yml +26 -0
  63. data/examples/v2/hooks/post_tool_use_multi_matcher_exit_2.yml +26 -0
  64. data/examples/v2/hooks/pre_tool_use_exit_0.yml +24 -0
  65. data/examples/v2/hooks/pre_tool_use_exit_1.yml +24 -0
  66. data/examples/v2/hooks/pre_tool_use_exit_2.yml +24 -0
  67. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_0.yml +26 -0
  68. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_1.yml +26 -0
  69. data/examples/v2/hooks/pre_tool_use_multi_matcher_exit_2.yml +27 -0
  70. data/examples/v2/hooks/swarm_summary.sh +44 -0
  71. data/examples/v2/hooks/user_prompt_exit_0.yml +21 -0
  72. data/examples/v2/hooks/user_prompt_exit_1.yml +21 -0
  73. data/examples/v2/hooks/user_prompt_exit_2.yml +21 -0
  74. data/examples/v2/hooks/validate_bash.rb +59 -0
  75. data/examples/v2/multi_directory_permissions.yml +221 -0
  76. data/examples/v2/node_context_demo.rb +127 -0
  77. data/examples/v2/node_workflow.rb +173 -0
  78. data/examples/v2/path_resolution_demo.rb +216 -0
  79. data/examples/v2/simple-swarm-v2.rb +90 -0
  80. data/examples/v2/simple-swarm-v2.yml +62 -0
  81. data/examples/v2/swarm.yml +71 -0
  82. data/examples/v2/swarm_with_hooks.yml +61 -0
  83. data/examples/v2/swarm_with_hooks_simple.yml +25 -0
  84. data/examples/v2/think_tool_demo.rb +62 -0
  85. data/exe/swarm +6 -0
  86. data/lib/claude_swarm/claude_mcp_server.rb +0 -6
  87. data/lib/claude_swarm/cli.rb +10 -3
  88. data/lib/claude_swarm/commands/ps.rb +19 -20
  89. data/lib/claude_swarm/commands/show.rb +1 -1
  90. data/lib/claude_swarm/configuration.rb +10 -12
  91. data/lib/claude_swarm/mcp_generator.rb +10 -1
  92. data/lib/claude_swarm/orchestrator.rb +73 -49
  93. data/lib/claude_swarm/system_utils.rb +37 -11
  94. data/lib/claude_swarm/version.rb +1 -1
  95. data/lib/claude_swarm/worktree_manager.rb +1 -0
  96. data/lib/claude_swarm/yaml_loader.rb +22 -0
  97. data/lib/claude_swarm.rb +7 -2
  98. data/lib/swarm_cli/cli.rb +201 -0
  99. data/lib/swarm_cli/command_registry.rb +61 -0
  100. data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
  101. data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
  102. data/lib/swarm_cli/commands/migrate.rb +55 -0
  103. data/lib/swarm_cli/commands/run.rb +173 -0
  104. data/lib/swarm_cli/config_loader.rb +97 -0
  105. data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
  106. data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
  107. data/lib/swarm_cli/interactive_repl.rb +918 -0
  108. data/lib/swarm_cli/mcp_serve_options.rb +44 -0
  109. data/lib/swarm_cli/mcp_tools_options.rb +59 -0
  110. data/lib/swarm_cli/migrate_options.rb +54 -0
  111. data/lib/swarm_cli/migrator.rb +132 -0
  112. data/lib/swarm_cli/options.rb +151 -0
  113. data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
  114. data/lib/swarm_cli/ui/components/content_block.rb +120 -0
  115. data/lib/swarm_cli/ui/components/divider.rb +57 -0
  116. data/lib/swarm_cli/ui/components/panel.rb +62 -0
  117. data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
  118. data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
  119. data/lib/swarm_cli/ui/formatters/number.rb +58 -0
  120. data/lib/swarm_cli/ui/formatters/text.rb +77 -0
  121. data/lib/swarm_cli/ui/formatters/time.rb +73 -0
  122. data/lib/swarm_cli/ui/icons.rb +59 -0
  123. data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
  124. data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
  125. data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
  126. data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
  127. data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
  128. data/lib/swarm_cli/version.rb +5 -0
  129. data/lib/swarm_cli.rb +44 -0
  130. data/lib/swarm_memory/adapters/base.rb +141 -0
  131. data/lib/swarm_memory/adapters/filesystem_adapter.rb +845 -0
  132. data/lib/swarm_memory/chat_extension.rb +34 -0
  133. data/lib/swarm_memory/cli/commands.rb +306 -0
  134. data/lib/swarm_memory/core/entry.rb +37 -0
  135. data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
  136. data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
  137. data/lib/swarm_memory/core/path_normalizer.rb +75 -0
  138. data/lib/swarm_memory/core/semantic_index.rb +244 -0
  139. data/lib/swarm_memory/core/storage.rb +288 -0
  140. data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
  141. data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
  142. data/lib/swarm_memory/dsl/memory_config.rb +113 -0
  143. data/lib/swarm_memory/embeddings/embedder.rb +36 -0
  144. data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
  145. data/lib/swarm_memory/errors.rb +21 -0
  146. data/lib/swarm_memory/integration/cli_registration.rb +30 -0
  147. data/lib/swarm_memory/integration/configuration.rb +43 -0
  148. data/lib/swarm_memory/integration/registration.rb +31 -0
  149. data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
  150. data/lib/swarm_memory/optimization/analyzer.rb +244 -0
  151. data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
  152. data/lib/swarm_memory/prompts/memory.md.erb +109 -0
  153. data/lib/swarm_memory/prompts/memory_assistant.md.erb +181 -0
  154. data/lib/swarm_memory/prompts/memory_researcher.md.erb +281 -0
  155. data/lib/swarm_memory/prompts/memory_retrieval.md.erb +78 -0
  156. data/lib/swarm_memory/search/semantic_search.rb +112 -0
  157. data/lib/swarm_memory/search/text_search.rb +42 -0
  158. data/lib/swarm_memory/search/text_similarity.rb +80 -0
  159. data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
  160. data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
  161. data/lib/swarm_memory/tools/load_skill.rb +313 -0
  162. data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
  163. data/lib/swarm_memory/tools/memory_delete.rb +99 -0
  164. data/lib/swarm_memory/tools/memory_edit.rb +185 -0
  165. data/lib/swarm_memory/tools/memory_glob.rb +160 -0
  166. data/lib/swarm_memory/tools/memory_grep.rb +247 -0
  167. data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
  168. data/lib/swarm_memory/tools/memory_read.rb +123 -0
  169. data/lib/swarm_memory/tools/memory_write.rb +231 -0
  170. data/lib/swarm_memory/utils.rb +50 -0
  171. data/lib/swarm_memory/version.rb +5 -0
  172. data/lib/swarm_memory.rb +166 -0
  173. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
  174. data/lib/swarm_sdk/agent/builder.rb +461 -0
  175. data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
  176. data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
  177. data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
  178. data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
  179. data/lib/swarm_sdk/agent/chat.rb +1159 -0
  180. data/lib/swarm_sdk/agent/context.rb +112 -0
  181. data/lib/swarm_sdk/agent/context_manager.rb +309 -0
  182. data/lib/swarm_sdk/agent/definition.rb +556 -0
  183. data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
  184. data/lib/swarm_sdk/configuration.rb +296 -0
  185. data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
  186. data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
  187. data/lib/swarm_sdk/context_compactor.rb +340 -0
  188. data/lib/swarm_sdk/hooks/adapter.rb +359 -0
  189. data/lib/swarm_sdk/hooks/context.rb +197 -0
  190. data/lib/swarm_sdk/hooks/definition.rb +80 -0
  191. data/lib/swarm_sdk/hooks/error.rb +29 -0
  192. data/lib/swarm_sdk/hooks/executor.rb +146 -0
  193. data/lib/swarm_sdk/hooks/registry.rb +147 -0
  194. data/lib/swarm_sdk/hooks/result.rb +150 -0
  195. data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
  196. data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
  197. data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
  198. data/lib/swarm_sdk/log_collector.rb +51 -0
  199. data/lib/swarm_sdk/log_stream.rb +69 -0
  200. data/lib/swarm_sdk/markdown_parser.rb +75 -0
  201. data/lib/swarm_sdk/model_aliases.json +5 -0
  202. data/lib/swarm_sdk/models.json +1 -0
  203. data/lib/swarm_sdk/models.rb +120 -0
  204. data/lib/swarm_sdk/node/agent_config.rb +49 -0
  205. data/lib/swarm_sdk/node/builder.rb +439 -0
  206. data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
  207. data/lib/swarm_sdk/node_context.rb +170 -0
  208. data/lib/swarm_sdk/node_orchestrator.rb +384 -0
  209. data/lib/swarm_sdk/permissions/config.rb +239 -0
  210. data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
  211. data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
  212. data/lib/swarm_sdk/permissions/validator.rb +173 -0
  213. data/lib/swarm_sdk/permissions_builder.rb +122 -0
  214. data/lib/swarm_sdk/plugin.rb +147 -0
  215. data/lib/swarm_sdk/plugin_registry.rb +101 -0
  216. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
  217. data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
  218. data/lib/swarm_sdk/result.rb +97 -0
  219. data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
  220. data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
  221. data/lib/swarm_sdk/swarm/builder.rb +586 -0
  222. data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
  223. data/lib/swarm_sdk/swarm/tool_configurator.rb +419 -0
  224. data/lib/swarm_sdk/swarm.rb +982 -0
  225. data/lib/swarm_sdk/tools/bash.rb +274 -0
  226. data/lib/swarm_sdk/tools/clock.rb +44 -0
  227. data/lib/swarm_sdk/tools/delegate.rb +164 -0
  228. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
  229. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
  230. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
  231. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
  232. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
  233. data/lib/swarm_sdk/tools/edit.rb +150 -0
  234. data/lib/swarm_sdk/tools/glob.rb +158 -0
  235. data/lib/swarm_sdk/tools/grep.rb +228 -0
  236. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
  237. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
  238. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
  239. data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
  240. data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
  241. data/lib/swarm_sdk/tools/read.rb +251 -0
  242. data/lib/swarm_sdk/tools/registry.rb +93 -0
  243. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
  244. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
  245. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
  246. data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
  247. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
  248. data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
  249. data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
  250. data/lib/swarm_sdk/tools/think.rb +95 -0
  251. data/lib/swarm_sdk/tools/todo_write.rb +216 -0
  252. data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
  253. data/lib/swarm_sdk/tools/write.rb +117 -0
  254. data/lib/swarm_sdk/utils.rb +50 -0
  255. data/lib/swarm_sdk/version.rb +5 -0
  256. data/lib/swarm_sdk.rb +157 -0
  257. data/llm.v2.txt +13407 -0
  258. data/rubocop/cop/security/no_reflection_methods.rb +47 -0
  259. data/rubocop/cop/security/no_ruby_llm_logger.rb +32 -0
  260. data/swarm_cli.gemspec +57 -0
  261. data/swarm_memory.gemspec +28 -0
  262. data/swarm_sdk.gemspec +41 -0
  263. data/team.yml +1 -1
  264. data/team_full.yml +1875 -0
  265. data/{team_v2.yml → team_sdk.yml} +121 -52
  266. metadata +249 -6
  267. data/EXAMPLES.md +0 -164
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmMemory
4
+ module Search
5
+ # Semantic search using embedding similarity
6
+ #
7
+ # Finds entries similar to a query based on embedding vectors
8
+ # rather than exact text matching.
9
+ class SemanticSearch
10
+ # Initialize semantic search
11
+ #
12
+ # @param adapter [Adapters::Base] Storage adapter
13
+ # @param embedder [Embeddings::Embedder] Embedder for generating query vectors
14
+ def initialize(adapter:, embedder:)
15
+ @adapter = adapter
16
+ @embedder = embedder
17
+ end
18
+
19
+ # Search for entries similar to query
20
+ #
21
+ # @param query [String] Search query
22
+ # @param top_k [Integer] Number of results to return
23
+ # @param threshold [Float] Minimum similarity threshold (0.0-1.0)
24
+ # @return [Array<Hash>] Ranked results with similarity scores
25
+ #
26
+ # @example
27
+ # results = search.find_similar(
28
+ # query: "How do I test Ruby code?",
29
+ # top_k: 5,
30
+ # threshold: 0.7
31
+ # )
32
+ # # => [
33
+ # # { path: "skills/testing/minitest", similarity: 0.92, title: "..." },
34
+ # # { path: "concepts/ruby/testing", similarity: 0.85, title: "..." }
35
+ # # ]
36
+ def find_similar(query:, top_k: 5, threshold: 0.7)
37
+ raise ArgumentError, "query is required" if query.nil? || query.to_s.strip.empty?
38
+
39
+ # Generate query embedding
40
+ query_embedding = @embedder.embed(query)
41
+
42
+ # Get all entries with embeddings
43
+ all_entries = @adapter.all_entries
44
+ entries_with_embeddings = all_entries.select { |_, entry| entry.embedded? }
45
+
46
+ return [] if entries_with_embeddings.empty?
47
+
48
+ # Calculate similarities
49
+ similarities = entries_with_embeddings.map do |path, entry|
50
+ similarity = TextSimilarity.cosine(query_embedding, entry.embedding)
51
+
52
+ {
53
+ path: path,
54
+ title: entry.title,
55
+ similarity: similarity,
56
+ updated_at: entry.updated_at,
57
+ }
58
+ end
59
+
60
+ # Filter by threshold and sort by similarity (descending)
61
+ results = similarities
62
+ .select { |r| r[:similarity] >= threshold }
63
+ .sort_by { |r| -r[:similarity] }
64
+ .take(top_k)
65
+
66
+ results
67
+ end
68
+
69
+ # Find entries similar to a given entry
70
+ #
71
+ # @param file_path [String] Path to reference entry
72
+ # @param top_k [Integer] Number of results to return
73
+ # @param threshold [Float] Minimum similarity threshold
74
+ # @return [Array<Hash>] Ranked results (excluding the reference entry)
75
+ def find_similar_to_entry(file_path:, top_k: 5, threshold: 0.7)
76
+ # Get reference entry
77
+ reference_entry = @adapter.read_entry(file_path: file_path)
78
+
79
+ unless reference_entry.embedded?
80
+ raise SearchError, "Entry #{file_path} has no embedding. Cannot perform semantic search."
81
+ end
82
+
83
+ # Get all entries with embeddings (excluding reference)
84
+ all_entries = @adapter.all_entries
85
+ entries_with_embeddings = all_entries
86
+ .select { |path, entry| path != file_path && entry.embedded? }
87
+
88
+ return [] if entries_with_embeddings.empty?
89
+
90
+ # Calculate similarities
91
+ similarities = entries_with_embeddings.map do |path, entry|
92
+ similarity = TextSimilarity.cosine(reference_entry.embedding, entry.embedding)
93
+
94
+ {
95
+ path: path,
96
+ title: entry.title,
97
+ similarity: similarity,
98
+ updated_at: entry.updated_at,
99
+ }
100
+ end
101
+
102
+ # Filter by threshold and sort by similarity (descending)
103
+ results = similarities
104
+ .select { |r| r[:similarity] >= threshold }
105
+ .sort_by { |r| -r[:similarity] }
106
+ .take(top_k)
107
+
108
+ results
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmMemory
4
+ module Search
5
+ # Text-based search operations (glob and grep)
6
+ #
7
+ # Provides a clean API for text-based search that wraps adapter operations.
8
+ # This layer could add additional logic like query parsing, ranking, etc.
9
+ class TextSearch
10
+ # Initialize text search
11
+ #
12
+ # @param adapter [Adapters::Base] Storage adapter
13
+ def initialize(adapter:)
14
+ @adapter = adapter
15
+ end
16
+
17
+ # Search by glob pattern
18
+ #
19
+ # @param pattern [String] Glob pattern
20
+ # @return [Array<Hash>] Matching entries
21
+ def glob(pattern:)
22
+ @adapter.glob(pattern: pattern)
23
+ end
24
+
25
+ # Search by content pattern
26
+ #
27
+ # @param pattern [String] Regex pattern
28
+ # @param case_insensitive [Boolean] Case-insensitive search
29
+ # @param output_mode [String] Output mode
30
+ # @param path [String, nil] Optional path prefix filter
31
+ # @return [Array<Hash>] Search results
32
+ def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches", path: nil)
33
+ @adapter.grep(
34
+ pattern: pattern,
35
+ case_insensitive: case_insensitive,
36
+ output_mode: output_mode,
37
+ path: path,
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmMemory
4
+ module Search
5
+ # Text similarity calculations using multiple algorithms
6
+ #
7
+ # Provides both Jaccard (word overlap) and cosine similarity metrics.
8
+ class TextSimilarity
9
+ class << self
10
+ # Calculate Jaccard similarity between two texts
11
+ #
12
+ # Jaccard similarity measures the overlap of word sets.
13
+ # Score ranges from 0.0 (no overlap) to 1.0 (identical).
14
+ #
15
+ # @param text1 [String] First text
16
+ # @param text2 [String] Second text
17
+ # @return [Float] Similarity score 0.0-1.0
18
+ #
19
+ # @example
20
+ # TextSimilarity.jaccard("ruby classes", "ruby modules")
21
+ # # => 0.33 (1 shared word out of 3 total unique words)
22
+ def jaccard(text1, text2)
23
+ words1 = tokenize(text1)
24
+ words2 = tokenize(text2)
25
+
26
+ return 0.0 if words1.empty? && words2.empty?
27
+ return 0.0 if words1.empty? || words2.empty?
28
+
29
+ intersection = (words1 & words2).size
30
+ union = (words1 | words2).size
31
+
32
+ return 0.0 if union.zero?
33
+
34
+ intersection.to_f / union
35
+ end
36
+
37
+ # Calculate cosine similarity between two embedding vectors
38
+ #
39
+ # Cosine similarity measures the angle between vectors.
40
+ # Score ranges from -1.0 to 1.0 (0.0-1.0 for normalized embeddings).
41
+ #
42
+ # @param vec1 [Array<Float>] First embedding vector
43
+ # @param vec2 [Array<Float>] Second embedding vector
44
+ # @return [Float] Similarity score -1.0 to 1.0
45
+ #
46
+ # @example
47
+ # vec1 = [0.1, 0.2, 0.3]
48
+ # vec2 = [0.2, 0.3, 0.4]
49
+ # TextSimilarity.cosine(vec1, vec2)
50
+ # # => 0.99 (very similar)
51
+ def cosine(vec1, vec2)
52
+ raise ArgumentError, "Vectors must have same length" if vec1.size != vec2.size
53
+ return 0.0 if vec1.empty?
54
+
55
+ dot_product = vec1.zip(vec2).sum { |a, b| a * b }
56
+ magnitude1 = Math.sqrt(vec1.sum { |x| x * x })
57
+ magnitude2 = Math.sqrt(vec2.sum { |x| x * x })
58
+
59
+ return 0.0 if magnitude1.zero? || magnitude2.zero?
60
+
61
+ dot_product / (magnitude1 * magnitude2)
62
+ end
63
+
64
+ private
65
+
66
+ # Tokenize text into normalized words
67
+ #
68
+ # @param text [String] Text to tokenize
69
+ # @return [Set<String>] Set of normalized words
70
+ def tokenize(text)
71
+ text
72
+ .downcase
73
+ .scan(/\w+/) # Extract words only
74
+ .reject { |w| w.length < 3 } # Remove very short words (a, is, to, etc.)
75
+ .to_set
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,101 @@
1
+ # Deep Learning Protocol
2
+
3
+ Use this when asked to learn deeply about a topic.
4
+
5
+ ## CRITICAL: Use Memory Tools ONLY
6
+
7
+ **NEVER use Write, Edit, or file tools during learning.**
8
+
9
+ All knowledge MUST be stored using Memory* tools:
10
+ - MemoryWrite - Store concepts, facts, skills
11
+ - MemoryRead - Recall what you've learned
12
+ - MemoryGrep - Search your knowledge
13
+ - MemoryEdit - Update existing knowledge
14
+
15
+ **Do NOT:**
16
+ - Write to disk files (Write, Edit, MultiEdit)
17
+ - Create temporary files
18
+ - Store knowledge anywhere except Memory
19
+
20
+ **Only use Memory* tools for persistence.**
21
+
22
+ ## Steps
23
+
24
+ 1. **Define Scope**
25
+ - What does "knowing this" mean?
26
+ - What should I be able to do with this knowledge?
27
+ - What are the boundaries of this topic?
28
+
29
+ 2. **Research Broadly** (Overview)
30
+ - Official documentation (WebFetch)
31
+ - Key concepts and terminology
32
+ - Main use cases and applications
33
+ - **Use MemoryWrite** to store concept entries for high-level understanding
34
+
35
+ 3. **Research Deeply** (Specifics)
36
+ - API details, syntax, configuration
37
+ - Best practices and patterns
38
+ - Common pitfalls and anti-patterns
39
+ - **Use MemoryWrite** to store fact entries for concrete details
40
+
41
+ 4. **Build Practical Skills**
42
+ - How to set up / configure
43
+ - How to use for common tasks
44
+ - How to debug and troubleshoot
45
+ - **Use MemoryWrite** to store skill entries with comprehensive tags
46
+
47
+ 5. **Self-Test Understanding**
48
+ - Use Think: "Can I explain this clearly without sources?"
49
+ - Use Think: "Can I create a tutorial for this?"
50
+ - Use Think: "What questions can't I answer yet?"
51
+ - If unclear → gaps exist, continue researching
52
+
53
+ 6. **Check Coverage**
54
+ - MemoryGrep(pattern: "{topic keyword}")
55
+ - Verify you have: concepts (how it works) + facts (specifics) + skills (how to use)
56
+ - Missing category? → incomplete, fill gaps
57
+
58
+ 7. **Identify Knowledge Gaps**
59
+ - What aspects haven't I covered?
60
+ - What questions would an expert be able to answer that I can't?
61
+ - What real-world scenarios haven't I considered?
62
+ - List gaps explicitly
63
+
64
+ 8. **Fill Gaps**
65
+ - Research each identified gap
66
+ - Store new knowledge systematically
67
+ - Re-test understanding
68
+
69
+ 9. **Verify Completion**
70
+ - Can explain topic clearly? ✓
71
+ - Can apply it practically? ✓
72
+ - Have concepts, facts, AND skills? ✓
73
+ - Can handle edge cases? ✓
74
+ - All gaps filled? ✓
75
+
76
+ 10. **Report Completion**
77
+ - Summarize what you learned (naturally, without mentioning memory)
78
+ - Indicate you're ready to apply this knowledge
79
+ - NEVER disclose memory system details
80
+
81
+ ## Success Criteria
82
+
83
+ You're done learning when:
84
+ - You have stored concepts explaining how it works
85
+ - You have stored facts with concrete details
86
+ - You have created practical skills for using it
87
+ - You can explain the topic clearly using Think
88
+ - You can identify appropriate use cases
89
+ - You can anticipate common problems
90
+ - MemoryGrep shows comprehensive coverage
91
+
92
+ ## Self-Testing Questions
93
+
94
+ Ask yourself:
95
+ - "What is X and how does it work?" (concept understanding)
96
+ - "What are the key APIs/tools/commands?" (factual knowledge)
97
+ - "How would I use X to solve Y?" (practical skills)
98
+ - "What are common mistakes?" (experience)
99
+ - "When should I NOT use X?" (boundaries)
100
+
101
+ If you can't answer confidently, you need more research.
@@ -0,0 +1,14 @@
1
+ title: Deep Learning Protocol
2
+ type: skill
3
+ confidence: high
4
+ tags:
5
+ - learning
6
+ - research
7
+ - knowledge-acquisition
8
+ - self-improvement
9
+ - meta
10
+ related: []
11
+ domain: meta/learning
12
+ source: built-in
13
+ tools: []
14
+ permissions: {}
@@ -0,0 +1,313 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmMemory
4
+ module Tools
5
+ # Tool for loading skills from memory and dynamically swapping agent tools
6
+ #
7
+ # LoadSkill reads a skill from memory, validates it, and swaps the agent's
8
+ # mutable tools to match the skill's requirements. Immutable tools (Memory*,
9
+ # Think, LoadSkill) are always preserved.
10
+ #
11
+ # Skills must:
12
+ # - Be stored in the skill/ hierarchy
13
+ # - Have type: 'skill' in metadata
14
+ # - Include tools array in metadata (optional)
15
+ # - Include permissions hash in metadata (optional)
16
+ class LoadSkill < RubyLLM::Tool
17
+ description <<~DESC
18
+ Load a skill from memory and dynamically adapt your toolset to execute it.
19
+
20
+ REQUIRED: Provide the file_path parameter - path to the skill in memory (must start with 'skill/').
21
+
22
+ **Parameters:**
23
+ - file_path (REQUIRED): Path to skill in memory - MUST start with 'skill/' (e.g., 'skill/debug-react-perf', 'skill/meta/deep-learning')
24
+
25
+ **What Happens When You Load a Skill:**
26
+
27
+ 1. **Tool Swapping**: Your mutable tools are replaced with the skill's required tools
28
+ - Immutable tools (Memory*, LoadSkill) always remain available
29
+ - Skill's tool list completely replaces your current mutable tools
30
+
31
+ 2. **Permissions Applied**: Tool permissions from skill metadata are applied
32
+ - Skill permissions override agent default permissions
33
+ - Allows/denies specific tool actions as defined in skill
34
+
35
+ 3. **Skill Content Returned**: Returns the skill's step-by-step instructions
36
+ - Read and follow the instructions carefully
37
+ - Instructions are formatted with line numbers
38
+
39
+ 4. **System Reminder Injected**: You'll see your complete updated toolset
40
+ - Lists all tools now available to you
41
+ - Only use tools from the updated list
42
+
43
+ **Skill Requirements:**
44
+
45
+ Skills MUST:
46
+ - Be stored in skill/ hierarchy ONLY (skill/ is one of exactly 4 memory categories)
47
+ - Path MUST start with 'skill/' (e.g., 'skill/debugging/api.md', 'skill/meta/deep-learning.md')
48
+ - Have type: 'skill' in metadata
49
+ - Optionally specify tools array in metadata
50
+ - Optionally specify permissions hash in metadata
51
+
52
+ **MEMORY CATEGORIES (4 Fixed Only):**
53
+ concept/, fact/, skill/, experience/ - NO OTHER top-level categories exist
54
+
55
+ **Skill Metadata Example:**
56
+ ```yaml
57
+ type: skill
58
+ tools: [Read, Edit, Bash, Grep]
59
+ permissions:
60
+ Bash:
61
+ allow_commands: ["npm", "pytest", "bundle"]
62
+ deny_commands: ["rm", "sudo"]
63
+ tags: [debugging, react, performance]
64
+ ```
65
+
66
+ **Usage Flow:**
67
+
68
+ ```
69
+ # 1. Find available skills (skill/ is one of 4 fixed memory categories)
70
+ MemoryGlob(pattern: "skill/**")
71
+
72
+ # 2. Read skill to understand it
73
+ MemoryRead(file_path: "skill/debugging/api-errors.md")
74
+
75
+ # 3. Load skill to adapt tools and get instructions
76
+ LoadSkill(file_path: "skill/debugging/api-errors.md")
77
+
78
+ # 4. Follow the skill's instructions using your updated toolset
79
+ ```
80
+
81
+ **Examples:**
82
+
83
+ ```
84
+ # Load a debugging skill
85
+ LoadSkill(file_path: "skill/debugging/api-errors.md")
86
+
87
+ # Load a meta-skill (skills about skills)
88
+ LoadSkill(file_path: "skill/meta/deep-learning.md")
89
+
90
+ # Load a testing skill
91
+ LoadSkill(file_path: "skill/testing/unit-tests.md")
92
+ ```
93
+
94
+ **Important Notes:**
95
+
96
+ - **Read Before Loading**: Use MemoryRead first to see what the skill does
97
+ - **Tool Swap**: Loading a skill changes your available tools - be aware of this
98
+ - **Immutable Tools**: Memory tools and LoadSkill are NEVER removed
99
+ - **Follow Instructions**: The skill content provides step-by-step guidance
100
+ - **One Skill at a Time**: Loading a new skill replaces the previous skill's toolset
101
+ - **Skill Validation**: Tool will fail if path doesn't start with 'skill/' or entry isn't type: 'skill'
102
+
103
+ **Skill Types:**
104
+
105
+ 1. **Task Skills**: Specific procedures (debugging, testing, refactoring)
106
+ 2. **Meta-Skills**: Skills about skills (deep-learning, skill-creation)
107
+ 3. **Domain Skills**: Specialized knowledge (frontend, backend, data-analysis)
108
+
109
+ **Creating Your Own Skills:**
110
+
111
+ Skills are just memory entries with special metadata. To create one:
112
+ 1. Write step-by-step instructions in markdown
113
+ 2. Store in skill/ hierarchy
114
+ 3. Add metadata: type='skill', tools array, optional permissions
115
+ 4. Test by loading and following instructions
116
+
117
+ **Common Use Cases:**
118
+
119
+ - Following established procedures consistently
120
+ - Accessing specialized toolsets for specific tasks
121
+ - Learning new workflows via step-by-step guidance
122
+ - Enforcing tool restrictions for safety
123
+ - Standardizing approaches across sessions
124
+ DESC
125
+
126
+ param :file_path,
127
+ desc: "Path to skill - MUST start with 'skill/' (one of 4 fixed memory categories). Examples: 'skill/debugging/api-errors.md', 'skill/meta/deep-learning.md'",
128
+ required: true
129
+
130
+ # Initialize with all context needed for tool swapping
131
+ #
132
+ # @param storage [Core::Storage] Memory storage
133
+ # @param agent_name [Symbol] Agent identifier
134
+ # @param chat [SwarmSDK::Agent::Chat] The agent's chat instance
135
+ # @param tool_configurator [SwarmSDK::ToolConfigurator] For creating tools
136
+ # @param agent_definition [SwarmSDK::Agent::Definition] For permissions
137
+ def initialize(storage:, agent_name:, chat:, tool_configurator:, agent_definition:)
138
+ super()
139
+ @storage = storage
140
+ @agent_name = agent_name
141
+ @chat = chat
142
+ @tool_configurator = tool_configurator
143
+ @agent_definition = agent_definition
144
+
145
+ # Mark memory tools and LoadSkill as immutable
146
+ # This ensures they won't be removed during skill swapping
147
+ @chat.mark_tools_immutable(
148
+ "MemoryWrite",
149
+ "MemoryRead",
150
+ "MemoryEdit",
151
+ "MemoryMultiEdit",
152
+ "MemoryDelete",
153
+ "MemoryGlob",
154
+ "MemoryGrep",
155
+ "MemoryDefrag",
156
+ "LoadSkill",
157
+ )
158
+ end
159
+
160
+ # Override name to return simple "LoadSkill"
161
+ def name
162
+ "LoadSkill"
163
+ end
164
+
165
+ # Execute the tool
166
+ #
167
+ # @param file_path [String] Path to skill in memory
168
+ # @return [String] Skill content with line numbers, or error message
169
+ def execute(file_path:)
170
+ # 1. Validate path starts with skill/
171
+ unless file_path.start_with?("skill/")
172
+ return validation_error("Skills must be stored in skill/ hierarchy. Got: #{file_path}")
173
+ end
174
+
175
+ # 2. Read entry with metadata
176
+ begin
177
+ entry = @storage.read_entry(file_path: file_path)
178
+ rescue ArgumentError => e
179
+ return validation_error(e.message)
180
+ end
181
+
182
+ # 3. Validate it's a skill
183
+ unless entry.metadata && entry.metadata["type"] == "skill"
184
+ type = entry.metadata&.dig("type") || "none"
185
+ return validation_error("memory://#{file_path} is not a skill (type: #{type})")
186
+ end
187
+
188
+ # 4. Extract tool requirements
189
+ required_tools = entry.metadata["tools"]
190
+ permissions = entry.metadata["permissions"] || {}
191
+
192
+ # 5. Validate and swap tools (only if tools are specified)
193
+ if required_tools && !required_tools.empty?
194
+ begin
195
+ swap_tools(required_tools, permissions)
196
+ rescue ArgumentError => e
197
+ return validation_error(e.message)
198
+ end
199
+ end
200
+ # If no tools specified (nil or []), keep current tools (no swap)
201
+
202
+ # 6. Mark skill as loaded
203
+ @chat.mark_skill_loaded(file_path)
204
+
205
+ # 7. Return content with confirmation message
206
+ title = entry.title || "Untitled Skill"
207
+ result = "Loaded skill: #{title}\n\n"
208
+ result += format_with_line_numbers(entry.content)
209
+
210
+ # 8. Add system reminder if tools were swapped
211
+ if required_tools && !required_tools.empty?
212
+ result += "\n\n"
213
+ result += build_toolset_update_reminder(required_tools)
214
+ end
215
+
216
+ result
217
+ end
218
+
219
+ private
220
+
221
+ # Build system reminder for toolset updates
222
+ #
223
+ # @param new_tools [Array<String>] Tools that were added
224
+ # @return [String] System reminder message
225
+ def build_toolset_update_reminder(new_tools)
226
+ # Get current tool list from chat
227
+ # Handle both real Chat (hash) and MockChat (array)
228
+ tools_collection = @chat.tools
229
+ current_tools = if tools_collection.is_a?(Hash)
230
+ tools_collection.values.map(&:name).sort
231
+ else
232
+ tools_collection.map(&:name).sort
233
+ end
234
+
235
+ reminder = "<system-reminder>\n"
236
+ reminder += "Your available tools have been updated.\n\n"
237
+ reminder += "New tools loaded from skill:\n"
238
+ new_tools.each do |tool_name|
239
+ reminder += " - #{tool_name}\n"
240
+ end
241
+ reminder += "\nYour complete toolset is now:\n"
242
+ current_tools.each do |tool_name|
243
+ reminder += " - #{tool_name}\n"
244
+ end
245
+ reminder += "\nOnly use tools from this list. Do not attempt to use tools that are not listed here.\n"
246
+ reminder += "</system-reminder>"
247
+
248
+ reminder
249
+ end
250
+
251
+ # Swap agent tools to match skill requirements
252
+ #
253
+ # @param required_tools [Array<String>] Tools needed by the skill
254
+ # @param permissions [Hash] Tool permissions from skill metadata
255
+ # @return [void]
256
+ # @raise [ArgumentError] If validation fails
257
+ def swap_tools(required_tools, permissions)
258
+ # Future: Could validate tool availability against agent's configured tools
259
+ # For now, all tools in SwarmSDK are available (unless bypassed by permissions)
260
+
261
+ # Remove all mutable tools (keeps immutable tools)
262
+ @chat.remove_mutable_tools
263
+
264
+ # Add required tools from skill
265
+ required_tools.each do |tool_name|
266
+ tool_sym = tool_name.to_sym
267
+
268
+ # Get permissions for this tool (skill overrides agent permissions)
269
+ tool_permissions = permissions[tool_name] || permissions[tool_sym.to_s]
270
+
271
+ # Create tool instance
272
+ tool_instance = @tool_configurator.create_tool_instance(
273
+ tool_sym,
274
+ @agent_name,
275
+ @agent_definition.directory,
276
+ )
277
+
278
+ # Wrap with permissions (unless bypassed)
279
+ tool_instance = @tool_configurator.wrap_tool_with_permissions(
280
+ tool_instance,
281
+ tool_permissions,
282
+ @agent_definition,
283
+ )
284
+
285
+ # Add to chat
286
+ @chat.add_tool(tool_instance)
287
+ end
288
+ end
289
+
290
+ # Format validation error message
291
+ #
292
+ # @param message [String] Error message
293
+ # @return [String] Formatted error
294
+ def validation_error(message)
295
+ "<tool_use_error>InputValidationError: #{message}</tool_use_error>"
296
+ end
297
+
298
+ # Format content with line numbers (same format as Read tool)
299
+ #
300
+ # @param content [String] Content to format
301
+ # @return [String] Content with line numbers
302
+ def format_with_line_numbers(content)
303
+ lines = content.lines
304
+ output_lines = lines.each_with_index.map do |line, idx|
305
+ line_number = idx + 1
306
+ display_line = line.chomp
307
+ "#{line_number.to_s.rjust(6)}→#{display_line}"
308
+ end
309
+ output_lines.join("\n")
310
+ end
311
+ end
312
+ end
313
+ end