claude_swarm 1.0.10 → 1.0.11

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +3 -0
  3. data/CLAUDE.md +0 -1
  4. data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
  5. data/docs/v2/CHANGELOG.swarm_cli.md +12 -0
  6. data/docs/v2/CHANGELOG.swarm_memory.md +139 -0
  7. data/docs/v2/CHANGELOG.swarm_sdk.md +249 -1
  8. data/docs/v2/README.md +15 -5
  9. data/docs/v2/guides/complete-tutorial.md +93 -7
  10. data/docs/v2/guides/getting-started.md +3 -1
  11. data/docs/v2/guides/memory-adapters.md +41 -0
  12. data/docs/v2/guides/{migrating-to-2.3.md → migrating-to-2.x.md} +213 -8
  13. data/docs/v2/guides/plugins.md +52 -5
  14. data/docs/v2/guides/rails-integration.md +6 -0
  15. data/docs/v2/guides/swarm-memory.md +2 -13
  16. data/docs/v2/reference/cli.md +0 -1
  17. data/docs/v2/reference/configuration_reference.md +300 -0
  18. data/docs/v2/reference/event_payload_structures.md +26 -4
  19. data/docs/v2/reference/ruby-dsl.md +457 -4
  20. data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
  21. data/docs/v2/reference/yaml.md +2 -2
  22. data/lib/claude_swarm/mcp_generator.rb +1 -1
  23. data/lib/claude_swarm/orchestrator.rb +8 -1
  24. data/lib/claude_swarm/version.rb +1 -1
  25. data/lib/swarm_cli/version.rb +1 -1
  26. data/lib/swarm_memory/core/semantic_index.rb +10 -2
  27. data/lib/swarm_memory/core/storage.rb +7 -2
  28. data/lib/swarm_memory/dsl/memory_config.rb +37 -0
  29. data/lib/swarm_memory/integration/sdk_plugin.rb +120 -27
  30. data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
  31. data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
  32. data/lib/swarm_memory/tools/load_skill.rb +0 -1
  33. data/lib/swarm_memory/tools/memory_edit.rb +2 -1
  34. data/lib/swarm_memory/tools/memory_read.rb +1 -1
  35. data/lib/swarm_memory/version.rb +1 -1
  36. data/lib/swarm_memory.rb +7 -5
  37. data/lib/swarm_sdk/agent/chat.rb +1 -1
  38. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +4 -0
  39. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +1 -1
  40. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +38 -4
  41. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +2 -2
  42. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +3 -5
  43. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +48 -0
  44. data/lib/swarm_sdk/agent/context.rb +1 -2
  45. data/lib/swarm_sdk/agent/definition.rb +3 -3
  46. data/lib/swarm_sdk/agent/system_prompt_builder.rb +1 -1
  47. data/lib/swarm_sdk/agent_registry.rb +146 -0
  48. data/lib/swarm_sdk/builders/base_builder.rb +91 -12
  49. data/lib/swarm_sdk/config.rb +302 -0
  50. data/lib/swarm_sdk/configuration/parser.rb +22 -2
  51. data/lib/swarm_sdk/configuration.rb +13 -4
  52. data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
  53. data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
  54. data/lib/swarm_sdk/hooks/adapter.rb +3 -3
  55. data/lib/swarm_sdk/hooks/shell_executor.rb +4 -3
  56. data/lib/swarm_sdk/models.json +4333 -1
  57. data/lib/swarm_sdk/models.rb +43 -2
  58. data/lib/swarm_sdk/plugin.rb +2 -2
  59. data/lib/swarm_sdk/result.rb +52 -0
  60. data/lib/swarm_sdk/swarm/agent_initializer.rb +1 -1
  61. data/lib/swarm_sdk/swarm/hook_triggers.rb +1 -0
  62. data/lib/swarm_sdk/swarm/logging_callbacks.rb +1 -0
  63. data/lib/swarm_sdk/swarm/tool_configurator.rb +18 -4
  64. data/lib/swarm_sdk/swarm.rb +76 -13
  65. data/lib/swarm_sdk/tools/bash.rb +7 -9
  66. data/lib/swarm_sdk/tools/glob.rb +5 -5
  67. data/lib/swarm_sdk/tools/read.rb +8 -8
  68. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +4 -3
  69. data/lib/swarm_sdk/tools/web_fetch.rb +20 -18
  70. data/lib/swarm_sdk/version.rb +1 -1
  71. data/lib/swarm_sdk/workflow/builder.rb +49 -0
  72. data/lib/swarm_sdk/workflow/node_builder.rb +4 -2
  73. data/lib/swarm_sdk/workflow/transformer_executor.rb +4 -3
  74. data/lib/swarm_sdk.rb +261 -105
  75. data/swarm_cli.gemspec +1 -1
  76. data/swarm_memory.gemspec +8 -3
  77. data/swarm_sdk.gemspec +4 -4
  78. data/team_full.yml +104 -300
  79. metadata +9 -5
  80. data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
  81. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -32,6 +32,132 @@ result = swarm.execute("Task prompt")
32
32
 
33
33
  ## Top-Level Methods
34
34
 
35
+ ### SwarmSDK.agent
36
+
37
+ Register an agent globally for reuse across multiple swarms.
38
+
39
+ **Signature:**
40
+ ```ruby
41
+ SwarmSDK.agent(name) {|builder| ... } → void
42
+ ```
43
+
44
+ **Parameters:**
45
+ - `name` (Symbol, required): Unique agent name
46
+ - `block` (required): Agent configuration block (uses [Agent Builder DSL](#agent-builder-dsl))
47
+
48
+ **Description:**
49
+ Registers an agent definition in a global registry, allowing it to be referenced by name in any swarm or workflow without re-defining it. This promotes code reuse and separation of concerns.
50
+
51
+ **Duplicate Registration:**
52
+ Raises `ArgumentError` if an agent with the same name is already registered. Use `SwarmSDK.clear_agent_registry!` to reset.
53
+
54
+ **Example - Define agents in separate files:**
55
+ ```ruby
56
+ # agents/backend.rb
57
+ SwarmSDK.agent :backend do
58
+ model "claude-sonnet-4"
59
+ description "Backend developer"
60
+ system_prompt "You build APIs and databases"
61
+ tools :Read, :Edit, :Bash
62
+ # Note: Don't set delegates_to here - it's swarm-specific!
63
+ end
64
+
65
+ # agents/database.rb
66
+ SwarmSDK.agent :database do
67
+ model "claude-sonnet-4"
68
+ description "Database specialist"
69
+ system_prompt "You design schemas and write migrations"
70
+ tools :Read, :Edit
71
+ end
72
+ ```
73
+
74
+ **Example - Reference in swarms:**
75
+ ```ruby
76
+ # Load agent definitions
77
+ require_relative "agents/backend"
78
+ require_relative "agents/database"
79
+
80
+ # Reference by name and configure delegation (swarm-specific)
81
+ swarm = SwarmSDK.build do
82
+ name "Dev Team"
83
+ lead :backend
84
+
85
+ agent :backend do
86
+ delegates_to :database # Delegation is swarm-specific
87
+ end
88
+ agent :database # Pulls from registry as-is
89
+ end
90
+ ```
91
+
92
+ **Example - Override registry settings:**
93
+ ```ruby
94
+ swarm = SwarmSDK.build do
95
+ name "Extended Team"
96
+ lead :backend
97
+
98
+ # Registry config applied first, then override block
99
+ agent :backend do
100
+ delegates_to :database, :cache # Set delegation for this swarm
101
+ tools :CustomTool # Adds to registry tools
102
+ timeout 300 # Overrides registry timeout
103
+ end
104
+ end
105
+ ```
106
+
107
+ **Workflow Auto-Resolution:**
108
+ Agents referenced in workflow nodes are automatically resolved from the registry if not defined at workflow level:
109
+
110
+ ```ruby
111
+ SwarmSDK.workflow do
112
+ name "Pipeline"
113
+ start_node :build
114
+
115
+ # No need to define :backend here - auto-resolved from registry!
116
+ node :build do
117
+ agent(:backend) # Resolved from global registry
118
+ end
119
+
120
+ node :test do
121
+ agent(:backend).delegates_to(:database) # Both resolved from registry
122
+ end
123
+ end
124
+ ```
125
+
126
+ **Use Cases:**
127
+ - **Code reuse**: Define agents once, use in multiple swarms
128
+ - **Separation of concerns**: Agent definitions in dedicated files
129
+ - **Testing**: Clear registry between tests with `SwarmSDK.clear_agent_registry!`
130
+ - **Organization**: Large projects with many agents
131
+
132
+ ---
133
+
134
+ ### SwarmSDK.clear_agent_registry!
135
+
136
+ Clear all registered agents from the global registry.
137
+
138
+ **Signature:**
139
+ ```ruby
140
+ SwarmSDK.clear_agent_registry! → void
141
+ ```
142
+
143
+ **Description:**
144
+ Removes all agents from the global registry. Primarily used for testing to ensure isolation between test cases.
145
+
146
+ **Example:**
147
+ ```ruby
148
+ # In test setup
149
+ def setup
150
+ SwarmSDK.clear_agent_registry!
151
+ end
152
+
153
+ # Or in RSpec
154
+ before(:each) do
155
+ SwarmSDK.clear_agent_registry!
156
+ end
157
+ ```
158
+
159
+ ---
160
+
35
161
  ### SwarmSDK.build
36
162
 
37
163
  Build a simple multi-agent swarm.
@@ -161,6 +287,8 @@ SwarmSDK.configure {|config| ... } → void
161
287
  - `webfetch_max_tokens` (Integer): Maximum tokens for WebFetch LLM responses (default: 4096)
162
288
  - `allow_filesystem_tools` (Boolean): Enable/disable filesystem tools globally (default: true)
163
289
 
290
+ **See [Configuration Reference](configuration_reference.md) for the complete list of 45+ configuration options including API keys, timeouts, limits, and more.**
291
+
164
292
  **Description:**
165
293
  Global configuration that applies to all swarms.
166
294
 
@@ -198,13 +326,220 @@ SwarmSDK.configure do |config|
198
326
  end
199
327
 
200
328
  # Can also set directly
201
- SwarmSDK.settings.allow_filesystem_tools = false
329
+ SwarmSDK.config.allow_filesystem_tools = false
202
330
 
203
331
  # Or via environment variable
204
332
  ENV['SWARM_SDK_ALLOW_FILESYSTEM_TOOLS'] = 'false'
205
333
 
206
334
  # Reset to defaults (disables WebFetch LLM processing, enables filesystem tools)
207
- SwarmSDK.reset_settings!
335
+ SwarmSDK.reset_config!
336
+ ```
337
+
338
+ ---
339
+
340
+ ### Custom Tool Registration
341
+
342
+ Simple API for registering custom tools without creating full plugins.
343
+
344
+ #### SwarmSDK.register_tool
345
+
346
+ Register a custom RubyLLM::Tool for use in swarms.
347
+
348
+ **Signatures:**
349
+ ```ruby
350
+ SwarmSDK.register_tool(tool_class) → Symbol
351
+ SwarmSDK.register_tool(name, tool_class) → Symbol
352
+ ```
353
+
354
+ **Parameters:**
355
+ - `tool_class` (Class): RubyLLM::Tool subclass to register
356
+ - `name` (Symbol, optional): Explicit tool name (if not provided, name is inferred)
357
+
358
+ **Returns:**
359
+ - `Symbol`: The registered tool name
360
+
361
+ **Description:**
362
+ Registers a custom tool globally so it can be used in any swarm. Tools are registered by name and can be referenced in agent configurations.
363
+
364
+ **Tool Lookup Order:**
365
+ 1. Plugin tools (from PluginRegistry)
366
+ 2. Custom tools (from CustomToolRegistry)
367
+ 3. Built-in tools (from Tools::Registry)
368
+
369
+ **Name Inference:**
370
+ If no name is provided, the tool name is inferred from the class name by:
371
+ 1. Taking the last component (e.g., `MyApp::Tools::WeatherTool` → `WeatherTool`)
372
+ 2. Removing the `Tool` suffix if present (e.g., `WeatherTool` → `Weather`)
373
+
374
+ **Context Support:**
375
+ Tools can declare `creation_requirements` to receive agent context:
376
+ - `:agent_name` - Agent identifier (Symbol)
377
+ - `:directory` - Agent's working directory (String)
378
+
379
+ **Validation:**
380
+ - Tool class must inherit from `RubyLLM::Tool`
381
+ - Cannot override built-in tools (Read, Write, Edit, etc.)
382
+ - Cannot override plugin tools (MemoryWrite, etc.)
383
+ - Cannot register the same name twice
384
+
385
+ **When to Use:**
386
+ - Simple, stateless tools
387
+ - No persistent storage needed
388
+ - No lifecycle hooks required
389
+ - Quick setup without plugin infrastructure
390
+
391
+ **When to Use Plugins Instead:**
392
+ - Need per-agent storage
393
+ - Need lifecycle hooks (on_agent_initialized, on_user_message, etc.)
394
+ - Want to contribute to system prompts
395
+ - Building suite of related tools
396
+
397
+ **Example - Simple Tool:**
398
+ ```ruby
399
+ class WeatherTool < RubyLLM::Tool
400
+ description "Get weather for a city"
401
+ param :city, type: "string", required: true
402
+
403
+ def execute(city:)
404
+ # Call weather API
405
+ "Weather in #{city}: Sunny, 72°F"
406
+ end
407
+ end
408
+
409
+ # Register with inferred name (:Weather)
410
+ SwarmSDK.register_tool(WeatherTool)
411
+
412
+ # Use in swarm
413
+ SwarmSDK.build do
414
+ agent :assistant do
415
+ model "claude-sonnet-4"
416
+ tools :Weather, :Read # Mix custom and built-in tools
417
+ end
418
+ end
419
+ ```
420
+
421
+ **Example - Context-Aware Tool:**
422
+ ```ruby
423
+ class AgentLogTool < RubyLLM::Tool
424
+ # Declare required context
425
+ def self.creation_requirements
426
+ [:agent_name, :directory]
427
+ end
428
+
429
+ description "Log a message for this agent"
430
+ param :message, type: "string", required: true
431
+
432
+ def initialize(agent_name:, directory:)
433
+ super()
434
+ @agent_name = agent_name
435
+ @log_file = File.join(directory, ".agent.log")
436
+ end
437
+
438
+ def execute(message:)
439
+ File.append(@log_file, "[#{@agent_name}] #{message}\n")
440
+ "Logged message to #{@log_file}"
441
+ end
442
+ end
443
+
444
+ SwarmSDK.register_tool(:AgentLog, AgentLogTool)
445
+ ```
446
+
447
+ **Example - Explicit Name:**
448
+ ```ruby
449
+ # Register with custom name
450
+ SwarmSDK.register_tool(:GetWeather, WeatherTool)
451
+
452
+ SwarmSDK.build do
453
+ agent :assistant do
454
+ tools :GetWeather # Use the custom name
455
+ end
456
+ end
457
+ ```
458
+
459
+ #### SwarmSDK.custom_tool_registered?
460
+
461
+ Check if a custom tool is registered.
462
+
463
+ **Signature:**
464
+ ```ruby
465
+ SwarmSDK.custom_tool_registered?(name) → Boolean
466
+ ```
467
+
468
+ **Parameters:**
469
+ - `name` (Symbol | String): Tool name to check
470
+
471
+ **Returns:**
472
+ - `true` if tool is registered, `false` otherwise
473
+
474
+ **Example:**
475
+ ```ruby
476
+ SwarmSDK.register_tool(WeatherTool)
477
+ SwarmSDK.custom_tool_registered?(:Weather) # => true
478
+ SwarmSDK.custom_tool_registered?("Weather") # => true
479
+ SwarmSDK.custom_tool_registered?(:Unknown) # => false
480
+ ```
481
+
482
+ #### SwarmSDK.custom_tools
483
+
484
+ List all registered custom tool names.
485
+
486
+ **Signature:**
487
+ ```ruby
488
+ SwarmSDK.custom_tools → Array<Symbol>
489
+ ```
490
+
491
+ **Returns:**
492
+ - Array of registered tool names (as symbols)
493
+
494
+ **Example:**
495
+ ```ruby
496
+ SwarmSDK.register_tool(WeatherTool)
497
+ SwarmSDK.register_tool(:Stock, StockPriceTool)
498
+
499
+ SwarmSDK.custom_tools # => [:Weather, :Stock]
500
+ ```
501
+
502
+ #### SwarmSDK.unregister_tool
503
+
504
+ Remove a registered custom tool.
505
+
506
+ **Signature:**
507
+ ```ruby
508
+ SwarmSDK.unregister_tool(name) → Class | nil
509
+ ```
510
+
511
+ **Parameters:**
512
+ - `name` (Symbol | String): Tool name to unregister
513
+
514
+ **Returns:**
515
+ - The unregistered tool class, or `nil` if not found
516
+
517
+ **Example:**
518
+ ```ruby
519
+ removed = SwarmSDK.unregister_tool(:Weather)
520
+ removed # => WeatherTool
521
+ ```
522
+
523
+ #### SwarmSDK.clear_custom_tools!
524
+
525
+ Clear all registered custom tools.
526
+
527
+ **Signature:**
528
+ ```ruby
529
+ SwarmSDK.clear_custom_tools! → void
530
+ ```
531
+
532
+ **Description:**
533
+ Removes all custom tool registrations. Primarily useful for testing to ensure clean state between test runs.
534
+
535
+ **Example:**
536
+ ```ruby
537
+ SwarmSDK.register_tool(WeatherTool)
538
+ SwarmSDK.register_tool(StockPriceTool)
539
+
540
+ SwarmSDK.clear_custom_tools!
541
+
542
+ SwarmSDK.custom_tools # => []
208
543
  ```
209
544
 
210
545
  ---
@@ -980,7 +1315,7 @@ tools(*tool_names, include_default: true) → void
980
1315
  - `ScratchpadWrite`, `ScratchpadRead`, `ScratchpadList`
981
1316
 
982
1317
  **Memory tools** (added if agent has `memory` configured):
983
- - `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryMultiEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
1318
+ - `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
984
1319
 
985
1320
  **Additional tools:**
986
1321
  - `Write`, `Edit`, `MultiEdit`, `Bash`
@@ -1129,7 +1464,7 @@ memory(&block) → void
1129
1464
  - `directory(string)` - Directory where memory.json will be stored (required)
1130
1465
 
1131
1466
  **Description:**
1132
- Enables persistent memory for the agent. When configured, the agent automatically gets all 7 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryMultiEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended to help the agent use memory effectively.
1467
+ Enables persistent memory for the agent. When configured, the agent automatically gets all 6 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended to help the agent use memory effectively.
1133
1468
 
1134
1469
  Memory is per-agent (isolated) and persistent (survives across sessions).
1135
1470
 
@@ -2231,6 +2566,124 @@ result.to_json → String
2231
2566
  ```
2232
2567
  Convert to JSON string.
2233
2568
 
2569
+ **per_agent_usage**
2570
+ ```ruby
2571
+ result.per_agent_usage → Hash<Symbol, Hash>
2572
+ ```
2573
+ Returns per-agent usage breakdown extracted from execution logs.
2574
+
2575
+ Each agent entry contains:
2576
+ - `input_tokens` - Input tokens for this agent
2577
+ - `output_tokens` - Output tokens for this agent
2578
+ - `total_tokens` - Total tokens (input + output)
2579
+ - `cached_tokens` - Cached tokens used
2580
+ - `context_limit` - Context window limit
2581
+ - `usage_percentage` - Percentage of context window used
2582
+ - `tokens_remaining` - Tokens remaining in context window
2583
+ - `input_cost` - Input cost in dollars
2584
+ - `output_cost` - Output cost in dollars
2585
+ - `total_cost` - Total cost in dollars
2586
+
2587
+ **Example:**
2588
+ ```ruby
2589
+ result = swarm.execute("Build authentication")
2590
+ usage = result.per_agent_usage
2591
+
2592
+ usage[:backend]
2593
+ # => {
2594
+ # input_tokens: 15000,
2595
+ # output_tokens: 5000,
2596
+ # total_tokens: 20000,
2597
+ # cached_tokens: 2000,
2598
+ # context_limit: 200000,
2599
+ # usage_percentage: 10.0,
2600
+ # tokens_remaining: 180000,
2601
+ # input_cost: 0.045,
2602
+ # output_cost: 0.075,
2603
+ # total_cost: 0.12
2604
+ # }
2605
+ ```
2606
+
2607
+ ---
2608
+
2609
+ ## Swarm Methods
2610
+
2611
+ ### context_breakdown
2612
+
2613
+ Get real-time context usage breakdown for all agents.
2614
+
2615
+ **Signature:**
2616
+ ```ruby
2617
+ swarm.context_breakdown → Hash<Symbol, Hash>
2618
+ ```
2619
+
2620
+ **Returns:**
2621
+ Hash with agent names as keys and context usage info as values.
2622
+
2623
+ **Description:**
2624
+ Returns live context metrics for all agents in the swarm, including both primary agents and delegation instances. Useful for monitoring token consumption and costs during execution.
2625
+
2626
+ Each agent entry contains:
2627
+ - `input_tokens` - Cumulative input tokens
2628
+ - `output_tokens` - Cumulative output tokens
2629
+ - `total_tokens` - Total tokens (input + output)
2630
+ - `cached_tokens` - Cached tokens used
2631
+ - `cache_creation_tokens` - Tokens written to cache
2632
+ - `effective_input_tokens` - Actual input tokens charged (input - cached)
2633
+ - `context_limit` - Context window limit for the model
2634
+ - `usage_percentage` - Percentage of context window used
2635
+ - `tokens_remaining` - Tokens remaining in context window
2636
+ - `input_cost` - Cumulative input cost in dollars
2637
+ - `output_cost` - Cumulative output cost in dollars
2638
+ - `total_cost` - Cumulative total cost in dollars
2639
+
2640
+ **Example:**
2641
+ ```ruby
2642
+ swarm = SwarmSDK.build do
2643
+ name "Dev Team"
2644
+ lead :backend
2645
+
2646
+ agent :backend do
2647
+ model "claude-sonnet-4"
2648
+ delegates_to :tester
2649
+ end
2650
+
2651
+ agent :tester do
2652
+ model "gpt-4o-mini"
2653
+ end
2654
+ end
2655
+
2656
+ # During or after execution
2657
+ breakdown = swarm.context_breakdown
2658
+
2659
+ # Primary agent
2660
+ breakdown[:backend]
2661
+ # => {
2662
+ # input_tokens: 15000,
2663
+ # output_tokens: 5000,
2664
+ # total_tokens: 20000,
2665
+ # cached_tokens: 2000,
2666
+ # cache_creation_tokens: 1000,
2667
+ # effective_input_tokens: 13000,
2668
+ # context_limit: 200000,
2669
+ # usage_percentage: 10.0,
2670
+ # tokens_remaining: 180000,
2671
+ # input_cost: 0.045,
2672
+ # output_cost: 0.075,
2673
+ # total_cost: 0.12
2674
+ # }
2675
+
2676
+ # Delegation instance
2677
+ breakdown[:"tester@backend"]
2678
+ # => { ... same structure ... }
2679
+ ```
2680
+
2681
+ **Use Cases:**
2682
+ - Monitor token consumption during long-running tasks
2683
+ - Track costs per agent in real-time
2684
+ - Identify context-heavy agents approaching limits
2685
+ - Debug context window issues
2686
+
2234
2687
  ---
2235
2688
 
2236
2689
  ## Context Management
@@ -855,28 +855,7 @@ end
855
855
 
856
856
  ---
857
857
 
858
- ### 4. MemoryMultiEdit
859
-
860
- **File:** `lib/swarm_memory/tools/memory_multi_edit.rb`
861
-
862
- Apply multiple edits sequentially to one entry.
863
-
864
- **Input (JSON array):**
865
- ```json
866
- [
867
- {"old_string": "status: pending", "new_string": "status: resolved"},
868
- {"old_string": "confidence: medium", "new_string": "confidence: high"}
869
- ]
870
- ```
871
-
872
- **Behavior:**
873
- - Edits applied **sequentially** (each sees previous results)
874
- - **All-or-nothing** - if any edit fails, NO changes saved
875
- - Shows which edits succeeded before failure
876
-
877
- ---
878
-
879
- ### 5. MemoryGlob
858
+ ### 4. MemoryGlob
880
859
 
881
860
  **File:** `lib/swarm_memory/tools/memory_glob.rb`
882
861
 
@@ -915,7 +894,7 @@ MAX_RESULTS = 500 # Truncates with system reminder if exceeded
915
894
 
916
895
  ---
917
896
 
918
- ### 6. MemoryGrep
897
+ ### 5. MemoryGrep
919
898
 
920
899
  **File:** `lib/swarm_memory/tools/memory_grep.rb`
921
900
 
@@ -949,7 +928,7 @@ path: "skill/debug.md" # Just that file
949
928
 
950
929
  ---
951
930
 
952
- ### 7. MemoryDelete
931
+ ### 6. MemoryDelete
953
932
 
954
933
  **File:** `lib/swarm_memory/tools/memory_delete.rb`
955
934
 
@@ -964,7 +943,7 @@ MemoryDelete(file_path: "experience/temp-experiment.md")
964
943
 
965
944
  ---
966
945
 
967
- ### 8. MemoryDefrag
946
+ ### 7. MemoryDefrag
968
947
 
969
948
  **File:** `lib/swarm_memory/tools/memory_defrag.rb`
970
949
 
@@ -1035,7 +1014,7 @@ MemoryDefrag(action: "full", dry_run: true) # Preview all
1035
1014
 
1036
1015
  ---
1037
1016
 
1038
- ### 9. LoadSkill
1017
+ ### 8. LoadSkill
1039
1018
 
1040
1019
  **File:** `lib/swarm_memory/tools/load_skill.rb`
1041
1020
 
@@ -1079,7 +1058,7 @@ sequenceDiagram
1079
1058
  **Immutable Tools:**
1080
1059
  ```ruby
1081
1060
  # These tools NEVER removed during skill loading:
1082
- - MemoryWrite, MemoryRead, MemoryEdit, MemoryMultiEdit
1061
+ - MemoryWrite, MemoryRead, MemoryEdit
1083
1062
  - MemoryDelete, MemoryGlob, MemoryGrep, MemoryDefrag
1084
1063
  - LoadSkill (self)
1085
1064
  ```
@@ -1243,7 +1222,7 @@ class SDKPlugin < SwarmSDK::Plugin
1243
1222
  # Return memory prompt based on mode
1244
1223
  end
1245
1224
 
1246
- def storage_enabled?(agent_definition)
1225
+ def memory_configured?(agent_definition)
1247
1226
  agent_definition.memory_enabled?
1248
1227
  end
1249
1228
  end
@@ -2025,7 +2004,6 @@ lib/swarm_memory/
2025
2004
  │ ├── memory_write.rb
2026
2005
  │ ├── memory_read.rb
2027
2006
  │ ├── memory_edit.rb
2028
- │ ├── memory_multi_edit.rb
2029
2007
  │ ├── memory_delete.rb
2030
2008
  │ ├── memory_glob.rb
2031
2009
  │ ├── memory_grep.rb
@@ -649,7 +649,7 @@ agents:
649
649
  - `ScratchpadWrite`, `ScratchpadRead`, `ScratchpadList`
650
650
 
651
651
  **Memory tools** (added if agent has `memory` configured):
652
- - `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryMultiEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
652
+ - `MemoryWrite`, `MemoryRead`, `MemoryEdit`, `MemoryGlob`, `MemoryGrep`, `MemoryDelete`
653
653
 
654
654
  **Additional tools:**
655
655
  - `Write`, `Edit`, `MultiEdit`, `Bash`
@@ -761,7 +761,7 @@ Plugin storage (like SwarmMemory) is always shared by base agent name:
761
761
  **Default:** `null` (memory disabled)
762
762
  **Description:** Configure persistent memory storage for this agent.
763
763
 
764
- When configured, the agent automatically gets all 7 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryMultiEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended.
764
+ When configured, the agent automatically gets all 6 memory tools (MemoryWrite, MemoryRead, MemoryEdit, MemoryGlob, MemoryGrep, MemoryDelete) and a memory system prompt is appended.
765
765
 
766
766
  Memory is per-agent (isolated) and persistent (survives across sessions).
767
767
 
@@ -123,7 +123,7 @@ module ClaudeSwarm
123
123
  # Add optional arguments
124
124
  # Handle prompt_file by reading the file contents
125
125
  if instance[:prompt_file]
126
- prompt_file_path = File.join(@config.root_directory, instance[:prompt_file])
126
+ prompt_file_path = File.join(@config.base_dir, instance[:prompt_file])
127
127
  if File.exist?(prompt_file_path)
128
128
  prompt_content = File.read(prompt_file_path)
129
129
  args.push("--prompt", prompt_content)
@@ -568,7 +568,14 @@ module ClaudeSwarm
568
568
  end
569
569
 
570
570
  # Always add instance prompt if it exists
571
- if instance[:prompt]
571
+ if instance[:prompt_file]
572
+ prompt_file_path = File.join(@config.base_dir, instance[:prompt_file])
573
+ if File.exist?(prompt_file_path)
574
+ prompt_content = File.read(prompt_file_path)
575
+ parts << "--append-system-prompt"
576
+ parts << prompt_content
577
+ end
578
+ elsif instance[:prompt]
572
579
  parts << "--append-system-prompt"
573
580
  parts << instance[:prompt]
574
581
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "1.0.10"
4
+ VERSION = "1.0.11"
5
5
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwarmCLI
4
- VERSION = "2.1.4"
4
+ VERSION = "2.1.7"
5
5
  end
@@ -181,6 +181,9 @@ module SwarmMemory
181
181
 
182
182
  # Calculate hybrid scores combining semantic similarity and keyword matching
183
183
  #
184
+ # When keyword_score is 0 (no tag matches), falls back to pure semantic scoring
185
+ # to avoid penalizing results that have excellent semantic matches but no tag overlap.
186
+ #
184
187
  # @param results [Array<Hash>] Results with semantic :similarity scores
185
188
  # @param query_keywords [Array<String>] Keywords from query
186
189
  # @return [Array<Hash>] Results with updated :similarity (hybrid score) and debug info
@@ -189,8 +192,13 @@ module SwarmMemory
189
192
  semantic_score = result[:similarity]
190
193
  keyword_score = calculate_keyword_score(result, query_keywords)
191
194
 
192
- # Hybrid score: weighted combination
193
- hybrid_score = (@semantic_weight * semantic_score) + (@keyword_weight * keyword_score)
195
+ # Fallback to pure semantic when no keyword matches
196
+ # This prevents penalizing results with excellent semantic matches but no tag overlap
197
+ hybrid_score = if keyword_score.zero?
198
+ semantic_score
199
+ else
200
+ (@semantic_weight * semantic_score) + (@keyword_weight * keyword_score)
201
+ end
194
202
 
195
203
  # Update result with hybrid score and debug info
196
204
  result.merge(
@@ -18,7 +18,9 @@ module SwarmMemory
18
18
  #
19
19
  # @param adapter [Adapters::Base] Storage adapter
20
20
  # @param embedder [Embeddings::Embedder, nil] Optional embedder for semantic search
21
- def initialize(adapter:, embedder: nil)
21
+ # @param semantic_weight [Float, nil] Weight for semantic similarity in hybrid search (0.0-1.0)
22
+ # @param keyword_weight [Float, nil] Weight for keyword matching in hybrid search (0.0-1.0)
23
+ def initialize(adapter:, embedder: nil, semantic_weight: nil, keyword_weight: nil)
22
24
  raise ArgumentError, "adapter is required" unless adapter.is_a?(Adapters::Base)
23
25
 
24
26
  @adapter = adapter
@@ -26,7 +28,10 @@ module SwarmMemory
26
28
 
27
29
  # Create semantic index if embedder is provided
28
30
  @semantic_index = if embedder
29
- SemanticIndex.new(adapter: adapter, embedder: embedder)
31
+ index_options = { adapter: adapter, embedder: embedder }
32
+ index_options[:semantic_weight] = semantic_weight if semantic_weight
33
+ index_options[:keyword_weight] = keyword_weight if keyword_weight
34
+ SemanticIndex.new(**index_options)
30
35
  end
31
36
  end
32
37