claude_swarm 1.0.0 ā 1.0.2
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 +21 -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 -3
- 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 +247 -4
- data/EXAMPLES.md +0 -164
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Manual Test 12: Complete Integration
|
|
5
|
+
#
|
|
6
|
+
# Tests: ALL features combined in one swarm
|
|
7
|
+
#
|
|
8
|
+
# Run: bundle exec ruby -Ilib lib/swarm_sdk/examples/dsl/12_complete_integration.rb
|
|
9
|
+
|
|
10
|
+
require "swarm_sdk"
|
|
11
|
+
require_relative "../../../swarm_sdk/swarm_builder"
|
|
12
|
+
require_relative "../../../swarm_sdk/agent_builder"
|
|
13
|
+
require_relative "../../../swarm_sdk/all_agents_builder"
|
|
14
|
+
require_relative "../../../swarm_sdk/permissions_builder"
|
|
15
|
+
|
|
16
|
+
ENV["OPENAI_API_KEY"] = "test-key"
|
|
17
|
+
|
|
18
|
+
# Track execution
|
|
19
|
+
@swarm_started = false
|
|
20
|
+
@swarm_stopped = false
|
|
21
|
+
|
|
22
|
+
swarm = SwarmSDK.build do
|
|
23
|
+
name("Complete Integration Test")
|
|
24
|
+
lead(:full_featured_agent)
|
|
25
|
+
|
|
26
|
+
# Swarm-level hooks
|
|
27
|
+
hook(:swarm_start) do |_ctx|
|
|
28
|
+
@swarm_started = true
|
|
29
|
+
puts "š Swarm starting..."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
hook(:swarm_stop) do |_ctx|
|
|
33
|
+
@swarm_stopped = true
|
|
34
|
+
puts "ā
Swarm complete!"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# All-agents configuration
|
|
38
|
+
all_agents do
|
|
39
|
+
tools(:Read, :Write)
|
|
40
|
+
|
|
41
|
+
permissions do
|
|
42
|
+
tool(:Write).allow_paths("tmp/**/*")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
hook(:pre_tool_use, matcher: "Write") do |_ctx|
|
|
46
|
+
puts "š Validating write operation..."
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Full-featured agent with everything
|
|
51
|
+
agent(:full_featured_agent) do
|
|
52
|
+
# Core config
|
|
53
|
+
model("gpt-5-nano")
|
|
54
|
+
provider("openai")
|
|
55
|
+
api_version("v1/responses")
|
|
56
|
+
context_window(200_000)
|
|
57
|
+
system_prompt("You are a fully-featured test agent.")
|
|
58
|
+
description("Agent with all features enabled")
|
|
59
|
+
|
|
60
|
+
# Capabilities
|
|
61
|
+
tools(:Bash, :TodoWrite)
|
|
62
|
+
delegates_to(:helper)
|
|
63
|
+
directory(".")
|
|
64
|
+
|
|
65
|
+
# MCP server (example using filesystem-mcp)
|
|
66
|
+
mcp_server(
|
|
67
|
+
:filesystem,
|
|
68
|
+
type: :stdio,
|
|
69
|
+
command: "npx",
|
|
70
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
71
|
+
env: {},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# LLM parameters (gpt-5-nano doesn't support temperature, using empty hash for demo)
|
|
75
|
+
parameters({})
|
|
76
|
+
|
|
77
|
+
# Advanced flags
|
|
78
|
+
bypass_permissions(false)
|
|
79
|
+
skip_base_prompt(false)
|
|
80
|
+
assume_model_exists(true)
|
|
81
|
+
timeout(120)
|
|
82
|
+
|
|
83
|
+
# Agent-specific permissions
|
|
84
|
+
permissions do
|
|
85
|
+
tool(:Bash).allow_commands("^echo", "^ls", "^pwd$")
|
|
86
|
+
tool(:Bash).deny_commands("^rm", "^shutdown")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Agent-specific hook
|
|
90
|
+
hook(:pre_tool_use, matcher: "Bash") do |ctx|
|
|
91
|
+
puts "š”ļø Validating Bash command: #{ctx.tool_call.parameters[:command]}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Helper agent
|
|
96
|
+
agent(:helper) do
|
|
97
|
+
model("gpt-5-nano")
|
|
98
|
+
provider("openai")
|
|
99
|
+
system_prompt("You are a helper. Answer concisely.")
|
|
100
|
+
description("Helper agent")
|
|
101
|
+
tools(:Read)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
puts "=" * 70
|
|
106
|
+
puts "COMPLETE INTEGRATION TEST"
|
|
107
|
+
puts "=" * 70
|
|
108
|
+
puts ""
|
|
109
|
+
puts "ā
Swarm built with ALL features:"
|
|
110
|
+
puts " ā Swarm config (name, lead)"
|
|
111
|
+
puts " ā Agent core params (model, provider, base_url, api_version, context_window)"
|
|
112
|
+
puts " ā Agent identity (system_prompt, description)"
|
|
113
|
+
puts " ā Capabilities (tools, delegates_to, directory)"
|
|
114
|
+
puts " ā MCP servers (filesystem via stdio)"
|
|
115
|
+
puts " ā LLM params (parameters, timeout)"
|
|
116
|
+
puts " ā Advanced flags (disable_default_tools, bypass_permissions, skip_base_prompt, assume_model_exists)"
|
|
117
|
+
puts " ā Permissions (all_agents and agent-level)"
|
|
118
|
+
puts " ā Hooks (swarm-level, agent-level, all_agents)"
|
|
119
|
+
puts " ā Delegation"
|
|
120
|
+
puts ""
|
|
121
|
+
|
|
122
|
+
puts "Running integration test..."
|
|
123
|
+
result = swarm.execute("Say 'integration test'")
|
|
124
|
+
|
|
125
|
+
puts ""
|
|
126
|
+
if result.success?
|
|
127
|
+
puts "Response: #{result.content}"
|
|
128
|
+
puts "Success: #{result.success?}"
|
|
129
|
+
puts ""
|
|
130
|
+
puts "š COMPLETE INTEGRATION SUCCESSFUL!"
|
|
131
|
+
puts "All DSL features work correctly together!"
|
|
132
|
+
else
|
|
133
|
+
puts "ā Error: #{result.error&.message}"
|
|
134
|
+
puts "This might be due to MCP server connection - that's okay for syntax testing"
|
|
135
|
+
puts ""
|
|
136
|
+
puts "ā
DSL syntax is correct even if MCP connection failed"
|
|
137
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
swarm:
|
|
4
|
+
name: "File Tools Demo Swarm"
|
|
5
|
+
# all_agents:
|
|
6
|
+
# permissions:
|
|
7
|
+
# Write:
|
|
8
|
+
# allowed_paths: ["tmp/LOL/**/*"]
|
|
9
|
+
# Read:
|
|
10
|
+
# denied_paths: ["lib/**/*"]
|
|
11
|
+
lead: coordinator
|
|
12
|
+
|
|
13
|
+
agents:
|
|
14
|
+
# Coordinator agent - orchestrates file operations and delegates to worker
|
|
15
|
+
coordinator:
|
|
16
|
+
description: "Coordinator that analyzes files and delegates file operations to worker"
|
|
17
|
+
model: gpt-5-nano
|
|
18
|
+
reasoning: "low"
|
|
19
|
+
provider: openai
|
|
20
|
+
# api_version: "v1/responses"
|
|
21
|
+
|
|
22
|
+
# Coordinator has read-only tools plus ability to delegate
|
|
23
|
+
# TodoWrite helps track progress on complex multi-step tasks
|
|
24
|
+
tools:
|
|
25
|
+
- Read
|
|
26
|
+
- Grep
|
|
27
|
+
- Glob
|
|
28
|
+
- TodoWrite
|
|
29
|
+
# - Write
|
|
30
|
+
|
|
31
|
+
# Can delegate work to the worker agent
|
|
32
|
+
delegates_to:
|
|
33
|
+
- worker
|
|
34
|
+
|
|
35
|
+
directory: .
|
|
36
|
+
|
|
37
|
+
system_prompt: |
|
|
38
|
+
You are a file analysis coordinator. Your role is to:
|
|
39
|
+
|
|
40
|
+
1. Analyze codebases using Read, Grep, and Glob tools
|
|
41
|
+
2. Search for patterns and understand file structures
|
|
42
|
+
3. Delegate file modification tasks to the 'worker' agent
|
|
43
|
+
4. Execute bash commands for gathering information (like git status, ls, etc.)
|
|
44
|
+
5. Use TodoWrite to track progress on complex multi-step tasks (3+ steps)
|
|
45
|
+
|
|
46
|
+
You have READ-ONLY access to files. You cannot write or edit files directly.
|
|
47
|
+
When you need to create or modify files, delegate to the 'worker' agent.
|
|
48
|
+
|
|
49
|
+
Always provide clear, specific instructions when delegating tasks.
|
|
50
|
+
Use TodoWrite proactively to organize your work and show progress.
|
|
51
|
+
|
|
52
|
+
# Worker agent - performs file write/edit operations in tmp/ directory only
|
|
53
|
+
worker:
|
|
54
|
+
description: "Worker that performs file write and edit operations in tmp/ directory"
|
|
55
|
+
model: claude-sonnet-4-5
|
|
56
|
+
provider: openai
|
|
57
|
+
# api_version: "v1/responses"
|
|
58
|
+
|
|
59
|
+
# Worker has all tools including Write and Edit
|
|
60
|
+
# TodoWrite helps track progress, MultiEdit enables multiple replacements
|
|
61
|
+
tools:
|
|
62
|
+
- Read
|
|
63
|
+
# - Write
|
|
64
|
+
# - Edit
|
|
65
|
+
# - MultiEdit
|
|
66
|
+
- Grep
|
|
67
|
+
- Glob
|
|
68
|
+
- TodoWrite
|
|
69
|
+
|
|
70
|
+
# Worker doesn't delegate - it's a leaf node
|
|
71
|
+
delegates_to: []
|
|
72
|
+
|
|
73
|
+
directory: .
|
|
74
|
+
|
|
75
|
+
system_prompt: |
|
|
76
|
+
You are a file operations worker. Your role is to:
|
|
77
|
+
|
|
78
|
+
1. Create new files using the Write tool
|
|
79
|
+
2. Modify existing files using the Edit tool or MultiEdit tool (for multiple changes)
|
|
80
|
+
3. Read files to verify operations
|
|
81
|
+
4. Search and analyze files as needed
|
|
82
|
+
5. Use TodoWrite to track progress on complex multi-step tasks (3+ steps)
|
|
83
|
+
|
|
84
|
+
CRITICAL SAFETY RULE:
|
|
85
|
+
You can ONLY perform Write and Edit operations in the tmp/ directory.
|
|
86
|
+
|
|
87
|
+
- ā
ALLOWED: /tmp/test.txt, ./tmp/data.json, /absolute/path/to/tmp/file.rb
|
|
88
|
+
- ā FORBIDDEN: /home/user/file.txt, ./src/main.rb, /etc/config
|
|
89
|
+
|
|
90
|
+
Before performing any Write or Edit operation:
|
|
91
|
+
1. Verify the file path contains 'tmp/' or '/tmp/'
|
|
92
|
+
2. If the path is NOT in tmp/, REFUSE the operation and explain why
|
|
93
|
+
3. Always use absolute paths when possible for clarity
|
|
94
|
+
|
|
95
|
+
You may freely use Read, Grep, Glob, and Bash tools on any files.
|
|
96
|
+
This restriction applies ONLY to Write and Edit operations.
|
|
97
|
+
|
|
98
|
+
Use MultiEdit when you need to make multiple changes to a single file efficiently.
|
|
99
|
+
Use TodoWrite proactively to organize your work and show progress.
|
|
100
|
+
|
|
101
|
+
When you complete a task, provide a clear summary of what you did.
|
|
102
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Basic Hooks Example - Beginner Level
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the fundamentals of SwarmSDK's hook system:
|
|
7
|
+
# - What hooks are and when they fire
|
|
8
|
+
# - Simple validation hooks
|
|
9
|
+
# - Logging hooks
|
|
10
|
+
# - Blocking operations with hooks
|
|
11
|
+
#
|
|
12
|
+
# Run: bundle exec ruby -Ilib lib/swarm_sdk/examples/hooks/01_basic_hooks.rb
|
|
13
|
+
|
|
14
|
+
require "swarm_sdk"
|
|
15
|
+
|
|
16
|
+
puts "=" * 80
|
|
17
|
+
puts "BASIC HOOKS EXAMPLE"
|
|
18
|
+
puts "=" * 80
|
|
19
|
+
puts ""
|
|
20
|
+
|
|
21
|
+
# Create a simple swarm with hooks
|
|
22
|
+
swarm = SwarmSDK.build do
|
|
23
|
+
name("Basic Hooks Demo")
|
|
24
|
+
lead(:assistant)
|
|
25
|
+
|
|
26
|
+
# Swarm-level hook: fires before any tool is used
|
|
27
|
+
hook(:pre_tool_use) do |context|
|
|
28
|
+
puts "š§ About to use tool: #{context.tool_name}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Swarm-level hook: fires after any tool completes
|
|
32
|
+
hook(:post_tool_use) do |context|
|
|
33
|
+
if context.tool_result.success?
|
|
34
|
+
puts "ā
Tool #{context.tool_name} succeeded"
|
|
35
|
+
else
|
|
36
|
+
puts "ā Tool #{context.tool_name} failed"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
agent(:assistant) do
|
|
41
|
+
description("A simple assistant with validation hooks")
|
|
42
|
+
model("gpt-4o")
|
|
43
|
+
system_prompt(<<~PROMPT)
|
|
44
|
+
You are a helpful assistant. You can read and write files.
|
|
45
|
+
When asked to create files, use the Write tool.
|
|
46
|
+
PROMPT
|
|
47
|
+
|
|
48
|
+
tools(:Write)
|
|
49
|
+
|
|
50
|
+
# Agent-specific hook: validate Write operations
|
|
51
|
+
hook(:pre_tool_use, matcher: "Write") do |context|
|
|
52
|
+
file_path = context.tool_call.parameters[:file_path]
|
|
53
|
+
content = context.tool_call.parameters[:content]
|
|
54
|
+
|
|
55
|
+
puts "š Validating write operation..."
|
|
56
|
+
puts " File: #{file_path}"
|
|
57
|
+
puts " Content size: #{content&.length || 0} bytes"
|
|
58
|
+
|
|
59
|
+
# Block empty content
|
|
60
|
+
if content.nil? || content.strip.empty?
|
|
61
|
+
SwarmSDK::Hooks::Result.halt("Cannot write empty content")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Block certain file extensions
|
|
65
|
+
if file_path&.end_with?(".key", ".pem", ".secret")
|
|
66
|
+
SwarmSDK::Hooks::Result.halt("Cannot write sensitive file types")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
puts " ā Validation passed"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Example 1: Successful operation
|
|
75
|
+
puts "\n--- Example 1: Valid Write Operation ---"
|
|
76
|
+
begin
|
|
77
|
+
result = swarm.execute("Create a file called test.txt with 'Hello World'")
|
|
78
|
+
puts "\nResult: #{result.success? ? "SUCCESS" : "FAILED"}"
|
|
79
|
+
puts "Response: #{result.content[0..100]}..."
|
|
80
|
+
rescue => e
|
|
81
|
+
puts "\nError: #{e.message}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Example 2: Blocked - empty content
|
|
85
|
+
puts "\n\n--- Example 2: Blocked - Empty Content ---"
|
|
86
|
+
begin
|
|
87
|
+
result = swarm.execute("Create a file called empty.txt with no content")
|
|
88
|
+
puts "\nResult: #{result.success? ? "SUCCESS" : "FAILED"}"
|
|
89
|
+
puts "Response: #{result.content[0..200]}..."
|
|
90
|
+
rescue => e
|
|
91
|
+
puts "\nError: #{e.message}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Example 3: Blocked - sensitive file
|
|
95
|
+
puts "\n\n--- Example 3: Blocked - Sensitive File Type ---"
|
|
96
|
+
begin
|
|
97
|
+
result = swarm.execute("Create a file called secret.key with 'API_KEY=123'")
|
|
98
|
+
puts "\nResult: #{result.success? ? "SUCCESS" : "FAILED"}"
|
|
99
|
+
puts "Response: #{result.content[0..200]}..."
|
|
100
|
+
rescue => e
|
|
101
|
+
puts "\nError: #{e.message}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
puts "\n" + "=" * 80
|
|
105
|
+
puts "KEY TAKEAWAYS"
|
|
106
|
+
puts "=" * 80
|
|
107
|
+
puts <<~SUMMARY
|
|
108
|
+
|
|
109
|
+
1. **pre_tool_use hooks** run before tools execute
|
|
110
|
+
- Perfect for validation and security checks
|
|
111
|
+
- Can halt execution with Hooks::Result.halt()
|
|
112
|
+
|
|
113
|
+
2. **post_tool_use hooks** run after tools complete
|
|
114
|
+
- Access results via context.tool_result
|
|
115
|
+
- Check success/failure status
|
|
116
|
+
|
|
117
|
+
3. **Matchers** target specific tools
|
|
118
|
+
- matcher: "Write" - only Write tool
|
|
119
|
+
- matcher: "Write|Edit" - Write OR Edit
|
|
120
|
+
- No matcher = all tools
|
|
121
|
+
|
|
122
|
+
4. **Hook levels**:
|
|
123
|
+
- Swarm-level (all agents): hook :event_name
|
|
124
|
+
- Agent-specific: Inside agent block
|
|
125
|
+
|
|
126
|
+
5. **Halting execution**:
|
|
127
|
+
- SwarmSDK::Hooks::Result.halt("reason")
|
|
128
|
+
- Returns error message to agent
|
|
129
|
+
- Agent sees tool failure, not code exception
|
|
130
|
+
|
|
131
|
+
SUMMARY
|
|
132
|
+
|
|
133
|
+
puts "=" * 80
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Usage Tracking with Hooks - Intermediate Level
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates usage tracking with the NEW agent_step architecture:
|
|
7
|
+
# - Access token usage and costs in agent_step hooks
|
|
8
|
+
# - Track costs per agent response
|
|
9
|
+
# - Implement budget limits
|
|
10
|
+
# - Monitor context window usage
|
|
11
|
+
# - Generate cost reports
|
|
12
|
+
#
|
|
13
|
+
# NEW ARCHITECTURE: Usage is in agent_step and agent_stop, NOT post_tool_use!
|
|
14
|
+
#
|
|
15
|
+
# Run: bundle exec ruby -Ilib lib/swarm_sdk/examples/hooks/02_usage_tracking.rb
|
|
16
|
+
|
|
17
|
+
require "swarm_sdk"
|
|
18
|
+
|
|
19
|
+
puts "=" * 80
|
|
20
|
+
puts "USAGE TRACKING WITH HOOKS"
|
|
21
|
+
puts "=" * 80
|
|
22
|
+
puts ""
|
|
23
|
+
|
|
24
|
+
# Track costs across all operations
|
|
25
|
+
@total_cost = 0.0
|
|
26
|
+
@step_costs = []
|
|
27
|
+
@step_count = 0
|
|
28
|
+
|
|
29
|
+
# Budget limit (in dollars)
|
|
30
|
+
BUDGET_LIMIT = 0.10 # 10 cents
|
|
31
|
+
|
|
32
|
+
swarm = SwarmSDK.build do
|
|
33
|
+
name("Cost Tracking Demo")
|
|
34
|
+
lead(:analyst)
|
|
35
|
+
|
|
36
|
+
# NEW ARCHITECTURE: Usage tracking in agent_step!
|
|
37
|
+
# agent_step fires when the LLM responds with tool calls
|
|
38
|
+
hook(:agent_step) do |context|
|
|
39
|
+
# Access usage metadata (NEW LOCATION!)
|
|
40
|
+
usage = context.metadata[:usage]
|
|
41
|
+
|
|
42
|
+
if usage
|
|
43
|
+
@step_count += 1
|
|
44
|
+
cost = usage[:total_cost]
|
|
45
|
+
input_tokens = usage[:input_tokens]
|
|
46
|
+
output_tokens = usage[:output_tokens]
|
|
47
|
+
|
|
48
|
+
# Track costs
|
|
49
|
+
@total_cost += cost
|
|
50
|
+
@step_costs << {
|
|
51
|
+
step: @step_count,
|
|
52
|
+
cost: cost,
|
|
53
|
+
tokens: usage[:total_tokens],
|
|
54
|
+
tool_calls: context.metadata[:tool_calls]&.size || 0,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Log usage information
|
|
58
|
+
puts "\nš° Agent Step #{@step_count}:"
|
|
59
|
+
puts " Tokens: #{input_tokens} in + #{output_tokens} out = #{usage[:total_tokens]} total"
|
|
60
|
+
puts " Cost: $#{format("%.6f", cost)}"
|
|
61
|
+
puts " Running total: $#{format("%.6f", @total_cost)}"
|
|
62
|
+
puts " Tool calls: #{@step_costs.last[:tool_calls]}"
|
|
63
|
+
|
|
64
|
+
# Check context usage
|
|
65
|
+
if usage[:tokens_used_percentage]
|
|
66
|
+
puts " Context: #{usage[:tokens_used_percentage]} (#{usage[:tokens_remaining]} tokens remaining)"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Warn if approaching budget limit
|
|
70
|
+
if @total_cost > BUDGET_LIMIT * 0.8
|
|
71
|
+
puts " ā ļø WARNING: Approaching budget limit (#{(@total_cost / BUDGET_LIMIT * 100).round}%)"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# HALT if budget exceeded
|
|
75
|
+
if @total_cost > BUDGET_LIMIT
|
|
76
|
+
puts " š BUDGET EXCEEDED!"
|
|
77
|
+
SwarmSDK::Hooks::Result.halt("Budget limit of $#{BUDGET_LIMIT} exceeded")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Monitor context warnings
|
|
83
|
+
hook(:context_warning) do |context|
|
|
84
|
+
puts "\nā ļø Context Warning:"
|
|
85
|
+
puts " Threshold: #{context.metadata[:threshold]}%"
|
|
86
|
+
puts " Current usage: #{context.metadata[:percentage].round(1)}%"
|
|
87
|
+
puts " Tokens remaining: #{context.metadata[:tokens_remaining]}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# agent_stop fires when the LLM gives final response (no more tool calls)
|
|
91
|
+
hook(:agent_stop) do |context|
|
|
92
|
+
usage = context.metadata[:usage]
|
|
93
|
+
|
|
94
|
+
puts "\nš Agent Completed:"
|
|
95
|
+
puts " Model: #{context.metadata[:model]}"
|
|
96
|
+
puts " Finish reason: #{context.metadata[:finish_reason]}"
|
|
97
|
+
|
|
98
|
+
if usage
|
|
99
|
+
@step_count += 1
|
|
100
|
+
cost = usage[:total_cost]
|
|
101
|
+
@total_cost += cost
|
|
102
|
+
|
|
103
|
+
puts " Final step tokens: #{usage[:total_tokens]}"
|
|
104
|
+
puts " Final step cost: $#{format("%.6f", cost)}"
|
|
105
|
+
puts " Total cost: $#{format("%.6f", @total_cost)}"
|
|
106
|
+
puts " Context usage: #{usage[:tokens_used_percentage]}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
agent(:analyst) do
|
|
111
|
+
description("Data analyst that processes files")
|
|
112
|
+
model("gpt-4o-mini") # Cheaper model for demo
|
|
113
|
+
system_prompt(<<~PROMPT)
|
|
114
|
+
You are a data analyst. Analyze files and generate reports.
|
|
115
|
+
Use the available tools to read files and create summaries.
|
|
116
|
+
PROMPT
|
|
117
|
+
|
|
118
|
+
tools(:Write)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
puts "Budget limit: $#{BUDGET_LIMIT}"
|
|
123
|
+
puts ""
|
|
124
|
+
|
|
125
|
+
# Execute a task
|
|
126
|
+
puts "\n--- Running Task ---"
|
|
127
|
+
begin
|
|
128
|
+
result = swarm.execute(<<~TASK)
|
|
129
|
+
Create a short analysis report about Ruby programming.
|
|
130
|
+
Write it to analysis.txt.
|
|
131
|
+
|
|
132
|
+
Include:
|
|
133
|
+
1. Brief overview of Ruby
|
|
134
|
+
2. Key features
|
|
135
|
+
3. Common use cases
|
|
136
|
+
TASK
|
|
137
|
+
|
|
138
|
+
puts "\n--- Task Complete ---"
|
|
139
|
+
puts "Success: #{result.success?}"
|
|
140
|
+
puts "Total cost: $#{format("%.6f", @total_cost)}"
|
|
141
|
+
puts "Total steps: #{@step_count}"
|
|
142
|
+
|
|
143
|
+
# Generate cost breakdown
|
|
144
|
+
puts "\n--- Cost Breakdown by Step ---"
|
|
145
|
+
@step_costs.each do |step|
|
|
146
|
+
puts "Step #{step[:step]}:"
|
|
147
|
+
puts " Cost: $#{format("%.6f", step[:cost])}"
|
|
148
|
+
puts " Tokens: #{step[:tokens]}"
|
|
149
|
+
puts " Tool calls: #{step[:tool_calls]}"
|
|
150
|
+
end
|
|
151
|
+
rescue => e
|
|
152
|
+
puts "\nError: #{e.message}"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
puts "\n" + "=" * 80
|
|
156
|
+
puts "KEY FEATURES DEMONSTRATED"
|
|
157
|
+
puts "=" * 80
|
|
158
|
+
puts <<~SUMMARY
|
|
159
|
+
|
|
160
|
+
1. **NEW ARCHITECTURE: Usage in agent_step hook**
|
|
161
|
+
- agent_step fires when LLM responds with tool calls
|
|
162
|
+
- agent_stop fires when LLM gives final response
|
|
163
|
+
- Usage is NO LONGER in post_tool_use!
|
|
164
|
+
|
|
165
|
+
2. **Usage Data Structure**
|
|
166
|
+
- context.metadata[:usage] contains:
|
|
167
|
+
* input_tokens, output_tokens, total_tokens
|
|
168
|
+
* input_cost, output_cost, total_cost
|
|
169
|
+
* cumulative_input_tokens, cumulative_output_tokens
|
|
170
|
+
* context_limit, tokens_used_percentage, tokens_remaining
|
|
171
|
+
|
|
172
|
+
3. **Cost Tracking**
|
|
173
|
+
- Track total costs across all agent steps
|
|
174
|
+
- Break down costs by step
|
|
175
|
+
- Each LLM response is one step
|
|
176
|
+
|
|
177
|
+
4. **Budget Limits**
|
|
178
|
+
- Implement spending limits
|
|
179
|
+
- Warn when approaching limit
|
|
180
|
+
- Halt execution when exceeded
|
|
181
|
+
|
|
182
|
+
5. **Context Monitoring**
|
|
183
|
+
- Monitor token usage as percentage
|
|
184
|
+
- Track remaining context window
|
|
185
|
+
- Receive warnings at thresholds (80%, 90%, etc.)
|
|
186
|
+
|
|
187
|
+
**KEY DIFFERENCE FROM OLD ARCHITECTURE:**
|
|
188
|
+
- OLD: Usage in post_tool_use (one per tool)
|
|
189
|
+
- NEW: Usage in agent_step (one per LLM response)
|
|
190
|
+
- An agent_step may include multiple tool calls
|
|
191
|
+
- Usage reflects the cost of the LLM response that generated those tool calls
|
|
192
|
+
|
|
193
|
+
**Example Flow:**
|
|
194
|
+
1. User prompt ā LLM responds with tool calls ā agent_step hook (with usage)
|
|
195
|
+
2. Tools execute ā post_tool_use hooks (NO usage)
|
|
196
|
+
3. Tool results sent to LLM ā LLM responds with more tool calls ā agent_step (with usage)
|
|
197
|
+
4. LLM gives final answer ā agent_stop hook (with usage)
|
|
198
|
+
|
|
199
|
+
SUMMARY
|
|
200
|
+
|
|
201
|
+
puts "=" * 80
|