swarm_memory 2.0.0
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 +7 -0
- data/LICENSE +21 -0
- data/lib/claude_swarm/base_executor.rb +133 -0
- data/lib/claude_swarm/claude_code_executor.rb +349 -0
- data/lib/claude_swarm/claude_mcp_server.rb +77 -0
- data/lib/claude_swarm/cli.rb +712 -0
- data/lib/claude_swarm/commands/ps.rb +216 -0
- data/lib/claude_swarm/commands/show.rb +139 -0
- data/lib/claude_swarm/configuration.rb +363 -0
- data/lib/claude_swarm/hooks/session_start_hook.rb +42 -0
- data/lib/claude_swarm/json_handler.rb +91 -0
- data/lib/claude_swarm/mcp_generator.rb +248 -0
- data/lib/claude_swarm/openai/chat_completion.rb +264 -0
- data/lib/claude_swarm/openai/executor.rb +254 -0
- data/lib/claude_swarm/openai/responses.rb +338 -0
- data/lib/claude_swarm/orchestrator.rb +879 -0
- data/lib/claude_swarm/process_tracker.rb +78 -0
- data/lib/claude_swarm/session_cost_calculator.rb +209 -0
- data/lib/claude_swarm/session_path.rb +42 -0
- data/lib/claude_swarm/settings_generator.rb +77 -0
- data/lib/claude_swarm/system_utils.rb +46 -0
- data/lib/claude_swarm/templates/generation_prompt.md.erb +230 -0
- data/lib/claude_swarm/tools/reset_session_tool.rb +24 -0
- data/lib/claude_swarm/tools/session_info_tool.rb +24 -0
- data/lib/claude_swarm/tools/task_tool.rb +63 -0
- data/lib/claude_swarm/version.rb +5 -0
- data/lib/claude_swarm/worktree_manager.rb +475 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +69 -0
- 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 +45 -0
- data/lib/swarm_memory/adapters/base.rb +140 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +789 -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 +286 -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 +139 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +201 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +76 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +40 -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 +145 -0
- data/lib/swarm_memory/tools/memory_grep.rb +209 -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 +215 -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 +1144 -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 +416 -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 +167 -0
- metadata +313 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
class ProcessTracker
|
|
5
|
+
PIDS_DIR = "pids"
|
|
6
|
+
|
|
7
|
+
def initialize(session_path)
|
|
8
|
+
@session_path = session_path
|
|
9
|
+
@pids_dir = File.join(@session_path, PIDS_DIR)
|
|
10
|
+
ensure_pids_directory
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def track_pid(pid, name)
|
|
14
|
+
pid_file = File.join(@pids_dir, pid.to_s)
|
|
15
|
+
File.write(pid_file, name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cleanup_all
|
|
19
|
+
return unless Dir.exist?(@pids_dir)
|
|
20
|
+
|
|
21
|
+
# Get all PID files
|
|
22
|
+
pid_files = Dir.glob(File.join(@pids_dir, "*"))
|
|
23
|
+
|
|
24
|
+
pid_files.each do |pid_file|
|
|
25
|
+
pid = File.basename(pid_file).to_i
|
|
26
|
+
name = begin
|
|
27
|
+
File.read(pid_file).strip
|
|
28
|
+
rescue StandardError
|
|
29
|
+
"unknown"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
# Check if process is still running
|
|
34
|
+
Process.kill(0, pid)
|
|
35
|
+
# If we get here, process is running, so kill it
|
|
36
|
+
Process.kill("TERM", pid)
|
|
37
|
+
puts "✓ Terminated MCP server: #{name} (PID: #{pid})"
|
|
38
|
+
|
|
39
|
+
# Give it a moment to terminate gracefully
|
|
40
|
+
sleep(0.1)
|
|
41
|
+
|
|
42
|
+
# Force kill if still running
|
|
43
|
+
begin
|
|
44
|
+
Process.kill(0, pid)
|
|
45
|
+
Process.kill("KILL", pid)
|
|
46
|
+
puts " → Force killed #{name} (PID: #{pid})"
|
|
47
|
+
rescue Errno::ESRCH
|
|
48
|
+
# Process is gone, which is what we want
|
|
49
|
+
end
|
|
50
|
+
rescue Errno::ESRCH
|
|
51
|
+
# Process not found, already terminated
|
|
52
|
+
puts " → MCP server #{name} (PID: #{pid}) already terminated"
|
|
53
|
+
rescue Errno::EPERM
|
|
54
|
+
# Permission denied
|
|
55
|
+
puts " ⚠️ No permission to terminate #{name} (PID: #{pid})"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Clean up the pids directory
|
|
60
|
+
FileUtils.rm_rf(@pids_dir)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class << self
|
|
64
|
+
def cleanup_session(session_path)
|
|
65
|
+
return unless Dir.exist?(File.join(session_path, PIDS_DIR))
|
|
66
|
+
|
|
67
|
+
tracker = new(session_path)
|
|
68
|
+
tracker.cleanup_all
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def ensure_pids_directory
|
|
75
|
+
FileUtils.mkdir_p(@pids_dir)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
module SessionCostCalculator
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
# Model pricing in dollars per million tokens
|
|
8
|
+
MODEL_PRICING = {
|
|
9
|
+
opus: {
|
|
10
|
+
input: 15.0,
|
|
11
|
+
output: 75.0,
|
|
12
|
+
cache_write: 18.75,
|
|
13
|
+
cache_read: 1.50,
|
|
14
|
+
},
|
|
15
|
+
sonnet: {
|
|
16
|
+
input: 3.0,
|
|
17
|
+
output: 15.0,
|
|
18
|
+
cache_write: 3.75,
|
|
19
|
+
cache_read: 0.30,
|
|
20
|
+
},
|
|
21
|
+
haiku: {
|
|
22
|
+
input: 0.80,
|
|
23
|
+
output: 4.0,
|
|
24
|
+
cache_write: 1.0,
|
|
25
|
+
cache_read: 0.08,
|
|
26
|
+
},
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Determine model type from model name
|
|
30
|
+
def model_type_from_name(model_name)
|
|
31
|
+
return unless model_name
|
|
32
|
+
|
|
33
|
+
model_name_lower = model_name.downcase
|
|
34
|
+
if model_name_lower.include?("opus")
|
|
35
|
+
:opus
|
|
36
|
+
elsif model_name_lower.include?("sonnet")
|
|
37
|
+
:sonnet
|
|
38
|
+
elsif model_name_lower.include?("haiku")
|
|
39
|
+
:haiku
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Calculate cost from token usage
|
|
44
|
+
def calculate_token_cost(usage, model_name)
|
|
45
|
+
model_type = model_type_from_name(model_name)
|
|
46
|
+
return 0.0 unless model_type && usage
|
|
47
|
+
|
|
48
|
+
pricing = MODEL_PRICING[model_type]
|
|
49
|
+
return 0.0 unless pricing
|
|
50
|
+
|
|
51
|
+
cost = 0.0
|
|
52
|
+
|
|
53
|
+
# Regular input tokens
|
|
54
|
+
if usage["input_tokens"]
|
|
55
|
+
cost += (usage["input_tokens"] / 1_000_000.0) * pricing[:input]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Output tokens
|
|
59
|
+
if usage["output_tokens"]
|
|
60
|
+
cost += (usage["output_tokens"] / 1_000_000.0) * pricing[:output]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Cache creation tokens (write)
|
|
64
|
+
if usage["cache_creation_input_tokens"]
|
|
65
|
+
cost += (usage["cache_creation_input_tokens"] / 1_000_000.0) * pricing[:cache_write]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Cache read tokens
|
|
69
|
+
if usage["cache_read_input_tokens"]
|
|
70
|
+
cost += (usage["cache_read_input_tokens"] / 1_000_000.0) * pricing[:cache_read]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
cost
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Calculate total cost from session log file
|
|
77
|
+
# Returns a hash with:
|
|
78
|
+
# - total_cost: Total cost in USD (sum of cost_usd for instances, token costs for main)
|
|
79
|
+
# - instances_with_cost: Set of instance names that have cost data
|
|
80
|
+
def calculate_total_cost(session_log_path)
|
|
81
|
+
return { total_cost: 0.0, instances_with_cost: Set.new } unless File.exist?(session_log_path)
|
|
82
|
+
|
|
83
|
+
# Track costs per instance - simple sum of cost_usd
|
|
84
|
+
instance_costs = {}
|
|
85
|
+
instances_with_cost = Set.new
|
|
86
|
+
main_instance_cost = 0.0
|
|
87
|
+
|
|
88
|
+
File.foreach(session_log_path) do |line|
|
|
89
|
+
data = JsonHandler.parse(line)
|
|
90
|
+
next if data == line # Skip unparseable lines
|
|
91
|
+
|
|
92
|
+
instance_name = data["instance"]
|
|
93
|
+
instance_id = data["instance_id"]
|
|
94
|
+
|
|
95
|
+
# Handle main instance token-based costs
|
|
96
|
+
if instance_id == "main" && data.dig("event", "type") == "assistant"
|
|
97
|
+
usage = data.dig("event", "message", "usage")
|
|
98
|
+
model = data.dig("event", "message", "model")
|
|
99
|
+
if usage && model
|
|
100
|
+
token_cost = calculate_token_cost(usage, model)
|
|
101
|
+
main_instance_cost += token_cost
|
|
102
|
+
instances_with_cost << instance_name if token_cost > 0
|
|
103
|
+
end
|
|
104
|
+
# Handle other instances with cost_usd (non-cumulative)
|
|
105
|
+
elsif instance_id != "main" && data.dig("event", "type") == "result"
|
|
106
|
+
# Use cost_usd (non-cumulative) instead of total_cost_usd (cumulative)
|
|
107
|
+
if (cost = data.dig("event", "cost_usd"))
|
|
108
|
+
instances_with_cost << instance_name
|
|
109
|
+
instance_costs[instance_name] ||= 0.0
|
|
110
|
+
instance_costs[instance_name] += cost
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Calculate total: sum of all instance costs + main instance token costs
|
|
116
|
+
other_instances_cost = instance_costs.values.sum
|
|
117
|
+
total_cost = other_instances_cost + main_instance_cost
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
total_cost: total_cost,
|
|
121
|
+
instances_with_cost: instances_with_cost,
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Calculate simple total cost (for backward compatibility)
|
|
126
|
+
def calculate_simple_total(session_log_path)
|
|
127
|
+
calculate_total_cost(session_log_path)[:total_cost]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Parse instance hierarchy with costs from session log
|
|
131
|
+
# Returns a hash of instances with their cost data and relationships
|
|
132
|
+
def parse_instance_hierarchy(session_log_path)
|
|
133
|
+
instances = {}
|
|
134
|
+
# Track main instance token costs
|
|
135
|
+
main_instance_costs = {}
|
|
136
|
+
|
|
137
|
+
return instances unless File.exist?(session_log_path)
|
|
138
|
+
|
|
139
|
+
File.foreach(session_log_path) do |line|
|
|
140
|
+
data = JsonHandler.parse(line)
|
|
141
|
+
next if data == line # Skip unparseable lines
|
|
142
|
+
|
|
143
|
+
instance_name = data["instance"]
|
|
144
|
+
instance_id = data["instance_id"]
|
|
145
|
+
calling_instance = data["calling_instance"]
|
|
146
|
+
|
|
147
|
+
# Initialize instance data
|
|
148
|
+
instances[instance_name] ||= {
|
|
149
|
+
name: instance_name,
|
|
150
|
+
id: instance_id,
|
|
151
|
+
cost: 0.0,
|
|
152
|
+
calls: 0,
|
|
153
|
+
called_by: Set.new,
|
|
154
|
+
calls_to: Set.new,
|
|
155
|
+
has_cost_data: false,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Track relationships
|
|
159
|
+
if calling_instance && calling_instance != instance_name
|
|
160
|
+
instances[instance_name][:called_by] << calling_instance
|
|
161
|
+
|
|
162
|
+
instances[calling_instance] ||= {
|
|
163
|
+
name: calling_instance,
|
|
164
|
+
id: data["calling_instance_id"],
|
|
165
|
+
cost: 0.0,
|
|
166
|
+
calls: 0,
|
|
167
|
+
called_by: Set.new,
|
|
168
|
+
calls_to: Set.new,
|
|
169
|
+
has_cost_data: false,
|
|
170
|
+
}
|
|
171
|
+
instances[calling_instance][:calls_to] << instance_name
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Handle main instance token-based costs
|
|
175
|
+
if instance_id == "main" && data.dig("event", "type") == "assistant"
|
|
176
|
+
usage = data.dig("event", "message", "usage")
|
|
177
|
+
model = data.dig("event", "message", "model")
|
|
178
|
+
if usage && model
|
|
179
|
+
token_cost = calculate_token_cost(usage, model)
|
|
180
|
+
if token_cost > 0
|
|
181
|
+
main_instance_costs[instance_name] ||= 0.0
|
|
182
|
+
main_instance_costs[instance_name] += token_cost
|
|
183
|
+
instances[instance_name][:has_cost_data] = true
|
|
184
|
+
instances[instance_name][:calls] += 1
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
# Track costs and calls for non-main instances using cost_usd
|
|
188
|
+
elsif data.dig("event", "type") == "result" && instance_id != "main"
|
|
189
|
+
instances[instance_name][:calls] += 1
|
|
190
|
+
# Use cost_usd (non-cumulative) instead of total_cost_usd
|
|
191
|
+
if (cost = data.dig("event", "cost_usd"))
|
|
192
|
+
instances[instance_name][:cost] += cost
|
|
193
|
+
instances[instance_name][:has_cost_data] = true
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Set main instance costs (replace, don't add)
|
|
199
|
+
main_instance_costs.each do |name, cost|
|
|
200
|
+
if instances[name]
|
|
201
|
+
# For main instances, use ONLY token costs, not cumulative costs
|
|
202
|
+
instances[name][:cost] = cost
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
instances
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
module SessionPath
|
|
5
|
+
class << self
|
|
6
|
+
# Convert a directory path to a safe folder name using + as separator
|
|
7
|
+
def project_folder_name(working_dir = Dir.pwd)
|
|
8
|
+
# Don't expand path if it's already expanded (avoids double expansion on Windows)
|
|
9
|
+
path = working_dir.start_with?("/") || working_dir.match?(/^[A-Za-z]:/) ? working_dir : File.expand_path(working_dir)
|
|
10
|
+
|
|
11
|
+
# Handle Windows drive letters (C:\ → C)
|
|
12
|
+
path = path.gsub(/^([A-Za-z]):/, '\1')
|
|
13
|
+
|
|
14
|
+
# Remove leading slash/backslash
|
|
15
|
+
path = path.sub(%r{^[/\\]}, "")
|
|
16
|
+
|
|
17
|
+
# Replace all path separators with +
|
|
18
|
+
path.gsub(%r{[/\\]}, "+")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Generate a full session path for a given directory and session ID
|
|
22
|
+
def generate(working_dir: Dir.pwd, session_id: SecureRandom.uuid)
|
|
23
|
+
project_name = project_folder_name(working_dir)
|
|
24
|
+
ClaudeSwarm.joined_sessions_dir(project_name, session_id)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Ensure the session directory exists
|
|
28
|
+
def ensure_directory(session_path)
|
|
29
|
+
FileUtils.mkdir_p(session_path)
|
|
30
|
+
|
|
31
|
+
# Add .gitignore to swarm home if it doesn't exist
|
|
32
|
+
gitignore_path = ClaudeSwarm.joined_home_dir(".gitignore")
|
|
33
|
+
File.write(gitignore_path, "*\n") unless File.exist?(gitignore_path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get the session path from environment (required)
|
|
37
|
+
def from_env
|
|
38
|
+
ENV["CLAUDE_SWARM_SESSION_PATH"] or raise "CLAUDE_SWARM_SESSION_PATH not set"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
class SettingsGenerator
|
|
5
|
+
def initialize(configuration)
|
|
6
|
+
@config = configuration
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def generate_all
|
|
10
|
+
ensure_session_directory
|
|
11
|
+
|
|
12
|
+
@config.instances.each do |name, instance|
|
|
13
|
+
generate_settings(name, instance)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def settings_path(instance_name)
|
|
18
|
+
File.join(session_path, "#{instance_name}_settings.json")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def session_path
|
|
24
|
+
@session_path ||= SessionPath.from_env
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ensure_session_directory
|
|
28
|
+
# Session directory is already created by orchestrator
|
|
29
|
+
# Just ensure it exists
|
|
30
|
+
SessionPath.ensure_directory(session_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_settings(name, instance)
|
|
34
|
+
settings = {}
|
|
35
|
+
|
|
36
|
+
# Add hooks if configured
|
|
37
|
+
if instance[:hooks] && !instance[:hooks].empty?
|
|
38
|
+
settings["hooks"] = instance[:hooks]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Add SessionStart hook for main instance to capture transcript path
|
|
42
|
+
if name == @config.main_instance
|
|
43
|
+
session_start_hook = build_session_start_hook
|
|
44
|
+
|
|
45
|
+
# Initialize hooks if not present
|
|
46
|
+
settings["hooks"] ||= {}
|
|
47
|
+
settings["hooks"]["SessionStart"] ||= []
|
|
48
|
+
|
|
49
|
+
# Add our hook to the SessionStart hooks
|
|
50
|
+
settings["hooks"]["SessionStart"] << session_start_hook
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Only write settings file if there are settings to write
|
|
54
|
+
return if settings.empty?
|
|
55
|
+
|
|
56
|
+
# Write settings file
|
|
57
|
+
JsonHandler.write_file!(settings_path(name), settings)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_session_start_hook
|
|
61
|
+
hook_script_path = File.expand_path("hooks/session_start_hook.rb", __dir__)
|
|
62
|
+
# Pass session path as an argument since ENV may not be inherited
|
|
63
|
+
session_path_arg = session_path
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
"matcher" => "startup",
|
|
67
|
+
"hooks" => [
|
|
68
|
+
{
|
|
69
|
+
"type" => "command",
|
|
70
|
+
"command" => "ruby #{hook_script_path} '#{session_path_arg}'",
|
|
71
|
+
"timeout" => 5,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
module SystemUtils
|
|
5
|
+
def system!(*args)
|
|
6
|
+
system(*args)
|
|
7
|
+
handle_command_failure(last_status, args)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def system_with_pid!(*args)
|
|
11
|
+
# Spawn the process - by default, inherits the parent's I/O
|
|
12
|
+
pid = Process.spawn(*args)
|
|
13
|
+
|
|
14
|
+
# Yield the PID to the block if given
|
|
15
|
+
yield(pid) if block_given?
|
|
16
|
+
|
|
17
|
+
# Wait for the process to complete
|
|
18
|
+
_, status = Process.wait2(pid)
|
|
19
|
+
|
|
20
|
+
# Check the exit status
|
|
21
|
+
handle_command_failure(status, args)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def last_status
|
|
25
|
+
$CHILD_STATUS
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def handle_command_failure(status, args) # rubocop:disable Naming/PredicateMethod
|
|
31
|
+
return true if status&.success?
|
|
32
|
+
|
|
33
|
+
exit_status = status&.exitstatus || 1
|
|
34
|
+
command_str = args.size == 1 ? args.first : args.join(" ")
|
|
35
|
+
|
|
36
|
+
if exit_status == 143 # timeout command exit status = 128 + 15 (SIGTERM)
|
|
37
|
+
warn("⏱️ Command timeout: #{command_str}")
|
|
38
|
+
else
|
|
39
|
+
warn("❌ Command failed with exit status: #{exit_status}")
|
|
40
|
+
raise Error, "Command failed with exit status #{exit_status}: #{command_str}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
You are a Claude Swarm configuration generator assistant. Your role is to help the user create a well-structured swarm YAML file through an interactive conversation.
|
|
2
|
+
|
|
3
|
+
## Claude Swarm Overview
|
|
4
|
+
Claude Swarm is a Ruby gem that orchestrates multiple Claude Code instances as a collaborative AI development team. It enables running AI agents with specialized roles, tools, and directory contexts, communicating via MCP (Model Context Protocol).
|
|
5
|
+
|
|
6
|
+
Key capabilities:
|
|
7
|
+
- Define multiple AI instances with different roles and specializations
|
|
8
|
+
- Set up connections between instances for collaboration
|
|
9
|
+
- Restrict tools based on each instance's responsibilities
|
|
10
|
+
- Run instances in different directories or Git worktrees
|
|
11
|
+
- Support for custom system prompts per instance
|
|
12
|
+
- Choose appropriate models (opus for complex tasks, sonnet for simpler ones)
|
|
13
|
+
- Support for OpenAI instances
|
|
14
|
+
|
|
15
|
+
## Your Task
|
|
16
|
+
1. Start by asking about the user's project structure and development needs
|
|
17
|
+
2. Understand what kind of team they need (roles, specializations)
|
|
18
|
+
3. Suggest an appropriate swarm topology based on their needs
|
|
19
|
+
4. Help them refine and customize the configuration
|
|
20
|
+
5. Generate the final claude-swarm.yml content
|
|
21
|
+
6. When the configuration is complete, save it to: <%= output_file || "a descriptive filename based on the swarm's function" %>
|
|
22
|
+
|
|
23
|
+
## File Naming Convention
|
|
24
|
+
<% if output_file %>
|
|
25
|
+
The user has specified the output file: <%= output_file %>
|
|
26
|
+
<% else %>
|
|
27
|
+
Since no output file was specified, name the file based on the swarm's function. Examples:
|
|
28
|
+
- web-dev-swarm.yml for full-stack web development teams
|
|
29
|
+
- data-pipeline-swarm.yml for data processing teams
|
|
30
|
+
- microservices-swarm.yml for microservice architectures
|
|
31
|
+
- mobile-app-swarm.yml for mobile development teams
|
|
32
|
+
- ml-research-swarm.yml for machine learning teams
|
|
33
|
+
- devops-swarm.yml for infrastructure and deployment teams
|
|
34
|
+
Use descriptive names that clearly indicate the swarm's purpose.
|
|
35
|
+
<% end %>
|
|
36
|
+
|
|
37
|
+
## Configuration Structure
|
|
38
|
+
```yaml
|
|
39
|
+
version: 1
|
|
40
|
+
swarm:
|
|
41
|
+
name: "Descriptive Swarm Name"
|
|
42
|
+
main: main_instance_name
|
|
43
|
+
instances:
|
|
44
|
+
instance_name:
|
|
45
|
+
description: "Clear description of role and responsibilities"
|
|
46
|
+
directory: ./path/to/directory
|
|
47
|
+
model: opus
|
|
48
|
+
allowed_tools: [Read, Edit, Write, Bash]
|
|
49
|
+
connections: [other_instance_names] # Optional
|
|
50
|
+
prompt: |
|
|
51
|
+
Custom system prompt for specialization
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Best Practices to Follow
|
|
56
|
+
- Use descriptive, role-based instance names (e.g., frontend_dev, api_architect)
|
|
57
|
+
- Write prompts using multi-line strings to make them more readable.
|
|
58
|
+
- Write clear descriptions explaining each instance's responsibilities
|
|
59
|
+
- Choose opus model for complex architectural or algorithmic tasks and routine development.
|
|
60
|
+
- Choose sonnet model for simpler tasks
|
|
61
|
+
- Set up logical connections (e.g., lead → team members, architect → implementers), but do not create circular dependencies.
|
|
62
|
+
- Always add this to the end of every prompt: `For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.`
|
|
63
|
+
- Select tools based on each instance's actual needs:
|
|
64
|
+
- Read: For code review and analysis roles
|
|
65
|
+
- Edit: For active development roles
|
|
66
|
+
- Write: For creating new files
|
|
67
|
+
- Bash: For running commands, tests, builds
|
|
68
|
+
- MultiEdit: For editing multiple files at once
|
|
69
|
+
- WebFetch: For fetching information from the web
|
|
70
|
+
- WebSearch: For searching the web
|
|
71
|
+
- Use custom prompts to specialize each instance's expertise
|
|
72
|
+
- Organize directories to match project structure
|
|
73
|
+
|
|
74
|
+
## Interactive Questions to Ask
|
|
75
|
+
- What type of project are you working on?
|
|
76
|
+
- What are the main technologies/frameworks you're using?
|
|
77
|
+
- What development tasks do you need help with?
|
|
78
|
+
- Do you need specialized roles (testing, DevOps, documentation)?
|
|
79
|
+
- Are there specific areas that need focused attention?
|
|
80
|
+
- Do you have multiple repositories or services to coordinate?
|
|
81
|
+
|
|
82
|
+
<full_readme>
|
|
83
|
+
<%= readme_content %>
|
|
84
|
+
</full_readme>
|
|
85
|
+
|
|
86
|
+
<prompt_best_practices>
|
|
87
|
+
# Claude 4 prompt engineering best practices
|
|
88
|
+
|
|
89
|
+
This guide provides specific prompt engineering techniques for Claude 4 models (Opus 4 and Sonnet 4) to help you achieve optimal results in your applications. These models have been trained for more precise instruction following than previous generations of Claude models.
|
|
90
|
+
|
|
91
|
+
## General principles
|
|
92
|
+
|
|
93
|
+
### Be explicit with your instructions
|
|
94
|
+
|
|
95
|
+
Claude 4 models respond well to clear, explicit instructions. Being specific about your desired output can help enhance results. Customers who desire the "above and beyond" behavior from previous Claude models might need to more explicitly request these behaviors with Claude 4.
|
|
96
|
+
|
|
97
|
+
**Less effective:**
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
Create an analytics dashboard
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**More effective:**
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
Create an analytics dashboard. Include as many relevant features and interactions as possible. Go beyond the basics to create a fully-featured implementation.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Add context to improve performance
|
|
110
|
+
|
|
111
|
+
Providing context or motivation behind your instructions, such as explaining to Claude why such behavior is important, can help Claude 4 better understand your goals and deliver more targeted responses.
|
|
112
|
+
|
|
113
|
+
**Less effective:**
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
NEVER use ellipses
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**More effective:**
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
Your response will be read aloud by a text-to-speech engine, so never use ellipses since the text-to-speech engine will not know how to pronounce them.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Claude is smart enough to generalize from the explanation.
|
|
126
|
+
|
|
127
|
+
### Be vigilant with examples & details
|
|
128
|
+
|
|
129
|
+
Claude 4 models pay attention to details and examples as part of instruction following. Ensure that your examples align with the behaviors you want to encourage and minimize behaviors you want to avoid.
|
|
130
|
+
|
|
131
|
+
## Guidance for specific situations
|
|
132
|
+
|
|
133
|
+
### Control the format of responses
|
|
134
|
+
|
|
135
|
+
There are a few ways that we have found to be particularly effective in seering output formatting in Claude 4 models:
|
|
136
|
+
|
|
137
|
+
1. **Tell Claude what to do instead of what not to do**
|
|
138
|
+
|
|
139
|
+
* Instead of: "Do not use markdown in your response"
|
|
140
|
+
* Try: "Your response should be composed of smoothly flowing prose paragraphs."
|
|
141
|
+
|
|
142
|
+
2. **Use XML format indicators**
|
|
143
|
+
|
|
144
|
+
* Try: "Write the prose sections of your response in <smoothly_flowing_prose_paragraphs> tags."
|
|
145
|
+
|
|
146
|
+
3. **Match your prompt style to the desired output**
|
|
147
|
+
|
|
148
|
+
The formatting style used in your prompt may influence Claude's response style. If you are still experiencing steerability issues with output formatting, we recommend as best as you can matching your prompt style to your desired output style. For exmaple, removing markdown from your prompt can reduce the volume of markdown in the output.
|
|
149
|
+
|
|
150
|
+
### Leverage thinking & interleaved thinking capabilities
|
|
151
|
+
|
|
152
|
+
Claude 4 offers thinking capabilities that can be especially helpful for tasks involving reflection after tool use or complex multi-step reasoning. You can guide its initial or interleaved thinking for better results.
|
|
153
|
+
|
|
154
|
+
```text Example prompt
|
|
155
|
+
After receiving tool results, carefully reflect on their quality and determine optimal next steps before proceeding. Use your thinking to plan and iterate based on this new information, and then take the best next action.
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Optimize parallel tool calling
|
|
159
|
+
|
|
160
|
+
Claude 4 models excel at parallel tool execution. They have a high success rate in using parallel tool calling without any prompting to do so, but some minor prompting can boost this behavior to ~100% parallel tool use success rate. We have found this prompt to be most effective:
|
|
161
|
+
|
|
162
|
+
```text Sample prompt for agents
|
|
163
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Reduce file creation in agentic coding
|
|
167
|
+
|
|
168
|
+
Claude 4 models may sometimes create new files for testing and iteration purposes, particularly when working with code. This approach allows Claude to use files, especially python scripts, as a 'temporary scratchpad' before saving its final output. Using temporary files can improve outcomes particularly for agentic coding use cases.
|
|
169
|
+
|
|
170
|
+
If you'd prefer to minimize net new file creation, you can instruct Claude to clean up after itself:
|
|
171
|
+
|
|
172
|
+
```text Sample prompt
|
|
173
|
+
If you create any temporary new files, scripts, or helper files for iteration, clean up these files by removing them at the end of the task.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Enhance visual and frontend code generation
|
|
177
|
+
|
|
178
|
+
For frontend code generation, you can steer Claude 4 models to create complex, detailed, and interactive designs by providing explicit encouragement:
|
|
179
|
+
|
|
180
|
+
```text Sample prompt
|
|
181
|
+
Don't hold back. Give it your all.
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
You can also improve Claude's frontend performance in specific areas by providing additional modifiers and details on what to focus on:
|
|
185
|
+
|
|
186
|
+
* "Include as many relevant features and interactions as possible"
|
|
187
|
+
* "Add thoughtful details like hover states, transitions, and micro-interactions"
|
|
188
|
+
* "Create an impressive demonstration showcasing web development capabilities"
|
|
189
|
+
* "Apply design principles: hierarchy, contrast, balance, and movement"
|
|
190
|
+
|
|
191
|
+
### Avoid focusing on passing tests and hard-coding
|
|
192
|
+
|
|
193
|
+
Frontier language models can sometimes focus too heavily on making tests pass at the expense of more general solutions. To prevent this behavior and ensure robust, generalizable solutions:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
Please write a high quality, general purpose solution. Implement a solution that works correctly for all valid inputs, not just the test cases. Do not hard-code values or create solutions that only work for specific test inputs. Instead, implement the actual logic that solves the problem generally.
|
|
197
|
+
|
|
198
|
+
Focus on understanding the problem requirements and implementing the correct algorithm. Tests are there to verify correctness, not to define the solution. Provide a principled implementation that follows best practices and software design principles.
|
|
199
|
+
|
|
200
|
+
If the task is unreasonable or infeasible, or if any of the tests are incorrect, please tell me. The solution should be robust, maintainable, and extendable.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
1. **Be specific about desired behavior**: Consider describing exactly what you'd like to see in the output.
|
|
204
|
+
|
|
205
|
+
2. **Frame your instructions with modifiers**: Adding modifiers that encourage Claude to increase the quality and detail of its output can help better shape Claude's performance. For example, instead of "Create an analytics dashboard", use "Create an analytics dashboard. Include as many relevant features and interactions as possible. Go beyond the basics to create a fully-featured implementation."
|
|
206
|
+
|
|
207
|
+
3. **Request specific features explicitly**: Animations and interactive elements should be requested explicitly when desired.
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Be clear, direct, and detailed
|
|
211
|
+
|
|
212
|
+
When interacting with Claude, think of it as a brilliant but very new employee (with amnesia) who needs explicit instructions. Like any new employee, Claude does not have context on your norms, styles, guidelines, or preferred ways of working.
|
|
213
|
+
The more precisely you explain what you want, the better Claude's response will be.
|
|
214
|
+
|
|
215
|
+
**The golden rule of clear prompting:** Show your prompt to a colleague, ideally someone who has minimal context on the task, and ask them to follow the instructions. If they're confused, Claude will likely be too.
|
|
216
|
+
|
|
217
|
+
## How to be clear, contextual, and specific
|
|
218
|
+
|
|
219
|
+
* **Give Claude contextual information:** Just like you might be able to better perform on a task if you knew more context, Claude will perform better if it has more contextual information. Some examples of contextual information:
|
|
220
|
+
* What the task results will be used for
|
|
221
|
+
* What audience the output is meant for
|
|
222
|
+
* What workflow the task is a part of, and where this task belongs in that workflow
|
|
223
|
+
* The end goal of the task, or what a successful task completion looks like
|
|
224
|
+
* **Be specific about what you want Claude to do:** For example, if you want Claude to output only code and nothing else, say so.
|
|
225
|
+
* **Provide instructions as sequential steps:** Use numbered lists or bullet points to better ensure that Claude carries out the task the exact way you want it to.
|
|
226
|
+
|
|
227
|
+
</prompt_best_practices>
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
IMPORTANT: Do not generate swarms with circular dependencies. For example, instance A connections to instance B, and instance B connections to instance A.
|