enhance_swarm 1.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/.enhance_swarm/agent_scripts/frontend_agent.md +39 -0
- data/.enhance_swarm/user_patterns.json +37 -0
- data/CHANGELOG.md +184 -0
- data/LICENSE +21 -0
- data/PRODUCTION_TEST_LOG.md +502 -0
- data/README.md +905 -0
- data/Rakefile +28 -0
- data/USAGE_EXAMPLES.md +477 -0
- data/examples/enhance_workflow.md +346 -0
- data/examples/rails_project.md +253 -0
- data/exe/enhance-swarm +30 -0
- data/lib/enhance_swarm/additional_commands.rb +299 -0
- data/lib/enhance_swarm/agent_communicator.rb +460 -0
- data/lib/enhance_swarm/agent_reviewer.rb +283 -0
- data/lib/enhance_swarm/agent_spawner.rb +462 -0
- data/lib/enhance_swarm/cleanup_manager.rb +245 -0
- data/lib/enhance_swarm/cli.rb +1592 -0
- data/lib/enhance_swarm/command_executor.rb +78 -0
- data/lib/enhance_swarm/configuration.rb +324 -0
- data/lib/enhance_swarm/control_agent.rb +307 -0
- data/lib/enhance_swarm/dependency_validator.rb +195 -0
- data/lib/enhance_swarm/error_recovery.rb +785 -0
- data/lib/enhance_swarm/generator.rb +194 -0
- data/lib/enhance_swarm/interrupt_handler.rb +512 -0
- data/lib/enhance_swarm/logger.rb +106 -0
- data/lib/enhance_swarm/mcp_integration.rb +85 -0
- data/lib/enhance_swarm/monitor.rb +28 -0
- data/lib/enhance_swarm/notification_manager.rb +444 -0
- data/lib/enhance_swarm/orchestrator.rb +313 -0
- data/lib/enhance_swarm/output_streamer.rb +281 -0
- data/lib/enhance_swarm/process_monitor.rb +266 -0
- data/lib/enhance_swarm/progress_tracker.rb +215 -0
- data/lib/enhance_swarm/project_analyzer.rb +612 -0
- data/lib/enhance_swarm/resource_manager.rb +177 -0
- data/lib/enhance_swarm/retry_handler.rb +40 -0
- data/lib/enhance_swarm/session_manager.rb +247 -0
- data/lib/enhance_swarm/signal_handler.rb +95 -0
- data/lib/enhance_swarm/smart_defaults.rb +708 -0
- data/lib/enhance_swarm/task_integration.rb +150 -0
- data/lib/enhance_swarm/task_manager.rb +174 -0
- data/lib/enhance_swarm/version.rb +5 -0
- data/lib/enhance_swarm/visual_dashboard.rb +555 -0
- data/lib/enhance_swarm/web_ui.rb +211 -0
- data/lib/enhance_swarm.rb +69 -0
- data/setup.sh +86 -0
- data/sig/enhance_swarm.rbs +4 -0
- data/templates/claude/CLAUDE.md +160 -0
- data/templates/claude/MCP.md +117 -0
- data/templates/claude/PERSONAS.md +114 -0
- data/templates/claude/RULES.md +221 -0
- data/test_builtin_functionality.rb +121 -0
- data/test_core_components.rb +156 -0
- data/test_real_claude_integration.rb +285 -0
- data/test_security.rb +150 -0
- data/test_smart_defaults.rb +155 -0
- data/test_task_integration.rb +173 -0
- data/test_web_ui.rb +245 -0
- data/web/assets/css/main.css +645 -0
- data/web/assets/js/kanban.js +499 -0
- data/web/assets/js/main.js +525 -0
- data/web/templates/dashboard.html.erb +226 -0
- data/web/templates/kanban.html.erb +193 -0
- metadata +293 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'fileutils'
|
5
|
+
require_relative 'command_executor'
|
6
|
+
|
7
|
+
module EnhanceSwarm
|
8
|
+
class Generator
|
9
|
+
def initialize
|
10
|
+
@config = EnhanceSwarm.configuration
|
11
|
+
@template_dir = File.expand_path('../../templates', __dir__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def init_project
|
15
|
+
create_directories
|
16
|
+
create_config_file
|
17
|
+
generate_claude_files
|
18
|
+
generate_git_hooks
|
19
|
+
setup_task_management
|
20
|
+
|
21
|
+
puts '✅ Project initialized with EnhanceSwarm'
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_claude_files
|
25
|
+
claude_dir = File.join(EnhanceSwarm.root, '.claude')
|
26
|
+
FileUtils.mkdir_p(claude_dir)
|
27
|
+
|
28
|
+
# Generate main Claude files
|
29
|
+
%w[CLAUDE.md RULES.md MCP.md PERSONAS.md].each do |file|
|
30
|
+
generate_from_template(
|
31
|
+
"claude/#{file}",
|
32
|
+
File.join(claude_dir, file)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
puts '✅ Generated Claude configuration files in .claude/'
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_mcp_config
|
40
|
+
mcp_dir = File.join(EnhanceSwarm.root, '.mcp')
|
41
|
+
FileUtils.mkdir_p(mcp_dir)
|
42
|
+
|
43
|
+
# Generate MCP configuration
|
44
|
+
config = {
|
45
|
+
'tools' => {
|
46
|
+
'gemini' => {
|
47
|
+
'enabled' => @config.gemini_enabled,
|
48
|
+
'command' => 'gemini',
|
49
|
+
'description' => 'Large context analysis with Gemini CLI'
|
50
|
+
},
|
51
|
+
'desktop_commander' => {
|
52
|
+
'enabled' => @config.desktop_commander_enabled,
|
53
|
+
'description' => 'File operations outside project directory'
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
File.write(
|
59
|
+
File.join(mcp_dir, 'config.json'),
|
60
|
+
JSON.pretty_generate(config)
|
61
|
+
)
|
62
|
+
|
63
|
+
puts '✅ Generated MCP configuration'
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate_git_hooks
|
67
|
+
hooks_dir = File.join(EnhanceSwarm.root, '.git', 'hooks')
|
68
|
+
FileUtils.mkdir_p(hooks_dir)
|
69
|
+
|
70
|
+
# Pre-commit hook with sanitized command
|
71
|
+
safe_test_command = sanitize_shell_command(@config.test_command)
|
72
|
+
pre_commit = <<~HOOK
|
73
|
+
#!/bin/bash
|
74
|
+
# EnhanceSwarm pre-commit hook
|
75
|
+
|
76
|
+
# Run tests before committing
|
77
|
+
echo "Running tests..."
|
78
|
+
#{safe_test_command}
|
79
|
+
|
80
|
+
if [ $? -ne 0 ]; then
|
81
|
+
echo "❌ Tests failed! Please fix before committing."
|
82
|
+
exit 1
|
83
|
+
fi
|
84
|
+
|
85
|
+
echo "✅ All tests passed!"
|
86
|
+
HOOK
|
87
|
+
|
88
|
+
hook_path = File.join(hooks_dir, 'pre-commit')
|
89
|
+
File.write(hook_path, pre_commit)
|
90
|
+
File.chmod(0o755, hook_path)
|
91
|
+
|
92
|
+
puts '✅ Generated Git hooks'
|
93
|
+
end
|
94
|
+
|
95
|
+
def setup_task_management
|
96
|
+
# Check if swarm-tasks is available
|
97
|
+
|
98
|
+
CommandExecutor.execute('gem', 'list', 'swarm_tasks', '-i')
|
99
|
+
puts '✅ swarm_tasks gem detected'
|
100
|
+
rescue CommandExecutor::CommandError
|
101
|
+
# Create basic task directories
|
102
|
+
%w[backlog active completed].each do |state|
|
103
|
+
FileUtils.mkdir_p(File.join(EnhanceSwarm.root, 'tasks', state))
|
104
|
+
end
|
105
|
+
|
106
|
+
puts '⚠️ swarm_tasks gem not found. Created basic task directories.'
|
107
|
+
puts ' Install with: gem install swarm_tasks'
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def create_directories
|
113
|
+
dirs = [
|
114
|
+
'.claude',
|
115
|
+
'.claude/agents',
|
116
|
+
'.claude/automation',
|
117
|
+
'.mcp',
|
118
|
+
'tasks/backlog',
|
119
|
+
'tasks/active',
|
120
|
+
'tasks/completed'
|
121
|
+
]
|
122
|
+
|
123
|
+
dirs.each do |dir|
|
124
|
+
FileUtils.mkdir_p(File.join(EnhanceSwarm.root, dir))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_config_file
|
129
|
+
config_path = File.join(EnhanceSwarm.root, '.enhance_swarm.yml')
|
130
|
+
|
131
|
+
return if File.exist?(config_path)
|
132
|
+
|
133
|
+
@config.save!
|
134
|
+
puts '✅ Created .enhance_swarm.yml configuration file'
|
135
|
+
end
|
136
|
+
|
137
|
+
def generate_from_template(template_name, output_path)
|
138
|
+
template_path = File.join(@template_dir, template_name)
|
139
|
+
|
140
|
+
unless File.exist?(template_path)
|
141
|
+
puts "⚠️ Template not found: #{template_name}"
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
145
|
+
# Read template
|
146
|
+
template_content = File.read(template_path)
|
147
|
+
erb = ERB.new(template_content)
|
148
|
+
|
149
|
+
# Render with configuration
|
150
|
+
rendered = erb.result(binding)
|
151
|
+
|
152
|
+
# Write output
|
153
|
+
File.write(output_path, rendered)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Helper methods for ERB templates
|
157
|
+
def project_name
|
158
|
+
@config.project_name
|
159
|
+
end
|
160
|
+
|
161
|
+
def project_description
|
162
|
+
@config.project_description
|
163
|
+
end
|
164
|
+
|
165
|
+
def technology_stack
|
166
|
+
@config.technology_stack
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_command
|
170
|
+
@config.test_command
|
171
|
+
end
|
172
|
+
|
173
|
+
def task_command
|
174
|
+
@config.task_command
|
175
|
+
end
|
176
|
+
|
177
|
+
def task_move_command
|
178
|
+
@config.task_move_command
|
179
|
+
end
|
180
|
+
|
181
|
+
def code_standards
|
182
|
+
@config.code_standards.join("\n")
|
183
|
+
end
|
184
|
+
|
185
|
+
def important_notes
|
186
|
+
@config.important_notes.join("\n")
|
187
|
+
end
|
188
|
+
|
189
|
+
def sanitize_shell_command(command)
|
190
|
+
# Remove dangerous shell metacharacters while preserving basic functionality
|
191
|
+
command.to_s.gsub(/[;&|`$\\]/, '').strip
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,512 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module EnhanceSwarm
|
6
|
+
class InterruptHandler
|
7
|
+
STUCK_THRESHOLD = 600 # 10 minutes
|
8
|
+
MEMORY_THRESHOLD = 1000 # 1GB in MB
|
9
|
+
RESPONSE_TIMEOUT = 30 # 30 seconds for user response
|
10
|
+
|
11
|
+
def initialize(notification_manager = nil)
|
12
|
+
@notification_manager = notification_manager || NotificationManager.instance
|
13
|
+
@interrupts_enabled = true
|
14
|
+
@monitoring_active = false
|
15
|
+
@user_responses = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_monitoring(agents)
|
19
|
+
return if @monitoring_active
|
20
|
+
|
21
|
+
@monitoring_active = true
|
22
|
+
@monitoring_thread = Thread.new do
|
23
|
+
monitor_for_interrupts(agents)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop_monitoring
|
28
|
+
@monitoring_active = false
|
29
|
+
@monitoring_thread&.kill
|
30
|
+
@monitoring_thread = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_agent_stuck(agent)
|
34
|
+
return unless @interrupts_enabled
|
35
|
+
|
36
|
+
agent_id = agent[:id]
|
37
|
+
role = agent[:role]
|
38
|
+
last_activity = agent[:last_activity]
|
39
|
+
current_task = agent[:current_task]
|
40
|
+
|
41
|
+
# Calculate how long stuck
|
42
|
+
time_stuck = Time.now - last_activity
|
43
|
+
|
44
|
+
# Only interrupt if stuck for significant time
|
45
|
+
return unless time_stuck > STUCK_THRESHOLD
|
46
|
+
|
47
|
+
# Show stuck notification
|
48
|
+
@notification_manager.agent_stuck(agent_id, role, last_activity, current_task)
|
49
|
+
|
50
|
+
# Prompt user for action with timeout
|
51
|
+
response = prompt_user_with_timeout(
|
52
|
+
"Agent '#{role}' stuck for #{format_duration(time_stuck)}. Restart? [y/N]",
|
53
|
+
timeout: RESPONSE_TIMEOUT,
|
54
|
+
default: 'n'
|
55
|
+
)
|
56
|
+
|
57
|
+
case response.downcase
|
58
|
+
when 'y', 'yes'
|
59
|
+
restart_agent(agent)
|
60
|
+
when 'k', 'kill'
|
61
|
+
kill_agent(agent)
|
62
|
+
when 'd', 'debug'
|
63
|
+
debug_agent(agent)
|
64
|
+
else
|
65
|
+
puts "Continuing to monitor agent '#{role}'...".colorize(:yellow)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_agent_excessive_memory(agent)
|
70
|
+
return unless @interrupts_enabled
|
71
|
+
|
72
|
+
memory_mb = agent[:memory_mb]
|
73
|
+
return unless memory_mb > MEMORY_THRESHOLD
|
74
|
+
|
75
|
+
agent_id = agent[:id]
|
76
|
+
role = agent[:role]
|
77
|
+
|
78
|
+
@notification_manager.intervention_needed(
|
79
|
+
"Agent '#{role}' using #{memory_mb}MB memory",
|
80
|
+
agent_id,
|
81
|
+
[
|
82
|
+
"enhance-swarm restart #{agent_id}",
|
83
|
+
"enhance-swarm kill #{agent_id}",
|
84
|
+
"Continue monitoring"
|
85
|
+
]
|
86
|
+
)
|
87
|
+
|
88
|
+
response = prompt_user_with_timeout(
|
89
|
+
"Agent '#{role}' using #{memory_mb}MB. Action? [r]estart/[k]ill/[c]ontinue",
|
90
|
+
timeout: RESPONSE_TIMEOUT,
|
91
|
+
default: 'c'
|
92
|
+
)
|
93
|
+
|
94
|
+
case response.downcase
|
95
|
+
when 'r', 'restart'
|
96
|
+
restart_agent(agent)
|
97
|
+
when 'k', 'kill'
|
98
|
+
kill_agent(agent)
|
99
|
+
else
|
100
|
+
puts "Continuing to monitor agent '#{role}'...".colorize(:yellow)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_coordination_conflict(agents, conflict_type, details = {})
|
105
|
+
return unless @interrupts_enabled
|
106
|
+
|
107
|
+
case conflict_type
|
108
|
+
when :file_conflict
|
109
|
+
handle_file_conflict(agents, details)
|
110
|
+
when :dependency_deadlock
|
111
|
+
handle_dependency_deadlock(agents, details)
|
112
|
+
when :resource_contention
|
113
|
+
handle_resource_contention(agents, details)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_critical_error(agent, error, context = {})
|
118
|
+
return unless @interrupts_enabled
|
119
|
+
|
120
|
+
agent_id = agent[:id]
|
121
|
+
role = agent[:role]
|
122
|
+
|
123
|
+
# Analyze error for suggested fixes
|
124
|
+
suggestions = analyze_error_for_suggestions(error, context)
|
125
|
+
|
126
|
+
@notification_manager.agent_failed(agent_id, role, error.message, suggestions)
|
127
|
+
|
128
|
+
if suggestions.any?
|
129
|
+
puts "\nError analysis complete. Choose an action:".colorize(:red)
|
130
|
+
suggestions.each_with_index do |suggestion, index|
|
131
|
+
puts " #{index + 1}. #{suggestion}".colorize(:yellow)
|
132
|
+
end
|
133
|
+
puts " c. Enter custom command".colorize(:blue)
|
134
|
+
|
135
|
+
response = prompt_user_with_timeout(
|
136
|
+
"Choose [1-#{suggestions.length}] or [c]ustom:",
|
137
|
+
timeout: RESPONSE_TIMEOUT,
|
138
|
+
default: '1'
|
139
|
+
)
|
140
|
+
|
141
|
+
execute_error_recovery(agent, response, suggestions)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Enable/disable interrupts
|
146
|
+
def enable_interrupts!
|
147
|
+
@interrupts_enabled = true
|
148
|
+
puts "✅ Interrupts enabled - will prompt for stuck/failed agents".colorize(:green)
|
149
|
+
end
|
150
|
+
|
151
|
+
def disable_interrupts!
|
152
|
+
@interrupts_enabled = false
|
153
|
+
puts "🔇 Interrupts disabled - agents will run without intervention".colorize(:yellow)
|
154
|
+
end
|
155
|
+
|
156
|
+
def interrupts_enabled?
|
157
|
+
@interrupts_enabled
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def monitor_for_interrupts(agents)
|
163
|
+
while @monitoring_active
|
164
|
+
agents.each do |agent|
|
165
|
+
next unless agent_needs_attention?(agent)
|
166
|
+
|
167
|
+
# Check for stuck agents
|
168
|
+
if agent_stuck?(agent)
|
169
|
+
handle_agent_stuck(agent)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Check for memory issues
|
173
|
+
if agent_excessive_memory?(agent)
|
174
|
+
handle_agent_excessive_memory(agent)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Check for process issues
|
178
|
+
unless process_healthy?(agent)
|
179
|
+
handle_process_issue(agent)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
sleep(30) # Check every 30 seconds
|
184
|
+
end
|
185
|
+
rescue StandardError => e
|
186
|
+
Logger.error("Interrupt monitoring error: #{e.message}")
|
187
|
+
end
|
188
|
+
|
189
|
+
def agent_needs_attention?(agent)
|
190
|
+
agent_stuck?(agent) ||
|
191
|
+
agent_excessive_memory?(agent) ||
|
192
|
+
!process_healthy?(agent)
|
193
|
+
end
|
194
|
+
|
195
|
+
def agent_stuck?(agent)
|
196
|
+
return false unless agent[:last_activity]
|
197
|
+
|
198
|
+
Time.now - agent[:last_activity] > STUCK_THRESHOLD
|
199
|
+
end
|
200
|
+
|
201
|
+
def agent_excessive_memory?(agent)
|
202
|
+
return false unless agent[:memory_mb]
|
203
|
+
|
204
|
+
agent[:memory_mb] > MEMORY_THRESHOLD
|
205
|
+
end
|
206
|
+
|
207
|
+
def process_healthy?(agent)
|
208
|
+
return true unless agent[:pid]
|
209
|
+
|
210
|
+
begin
|
211
|
+
Process.kill(0, agent[:pid])
|
212
|
+
true
|
213
|
+
rescue Errno::ESRCH
|
214
|
+
false
|
215
|
+
rescue Errno::EPERM
|
216
|
+
true # Process exists but we can't signal it
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def prompt_user_with_timeout(prompt, timeout: 30, default: nil)
|
221
|
+
puts prompt.colorize(:blue)
|
222
|
+
|
223
|
+
if default
|
224
|
+
puts "(Auto-selecting '#{default}' in #{timeout}s if no response)".colorize(:light_black)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Set up timeout
|
228
|
+
response = nil
|
229
|
+
input_thread = Thread.new do
|
230
|
+
response = $stdin.gets&.chomp
|
231
|
+
end
|
232
|
+
|
233
|
+
# Wait for input or timeout
|
234
|
+
unless input_thread.join(timeout)
|
235
|
+
input_thread.kill
|
236
|
+
puts "\nTimeout reached, using default: '#{default}'".colorize(:yellow)
|
237
|
+
response = default
|
238
|
+
end
|
239
|
+
|
240
|
+
response || default || ''
|
241
|
+
end
|
242
|
+
|
243
|
+
def restart_agent(agent)
|
244
|
+
agent_id = agent[:id]
|
245
|
+
role = agent[:role]
|
246
|
+
|
247
|
+
puts "🔄 Restarting agent '#{role}'...".colorize(:yellow)
|
248
|
+
|
249
|
+
begin
|
250
|
+
# Kill existing process
|
251
|
+
if agent[:pid]
|
252
|
+
Process.kill('TERM', agent[:pid])
|
253
|
+
sleep(2)
|
254
|
+
Process.kill('KILL', agent[:pid]) rescue nil
|
255
|
+
end
|
256
|
+
|
257
|
+
# Clean up resources
|
258
|
+
cleanup_agent_resources(agent)
|
259
|
+
|
260
|
+
# Restart with same task
|
261
|
+
new_pid = spawn_agent_replacement(agent)
|
262
|
+
|
263
|
+
if new_pid
|
264
|
+
puts "✅ Agent '#{role}' restarted with PID #{new_pid}".colorize(:green)
|
265
|
+
@notification_manager.notify(:agent_completed, "Agent '#{role}' restarted successfully")
|
266
|
+
else
|
267
|
+
puts "❌ Failed to restart agent '#{role}'".colorize(:red)
|
268
|
+
end
|
269
|
+
|
270
|
+
rescue StandardError => e
|
271
|
+
puts "❌ Error restarting agent: #{e.message}".colorize(:red)
|
272
|
+
Logger.error("Failed to restart agent #{agent_id}: #{e.message}")
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def kill_agent(agent)
|
277
|
+
agent_id = agent[:id]
|
278
|
+
role = agent[:role]
|
279
|
+
|
280
|
+
puts "🛑 Killing agent '#{role}'...".colorize(:red)
|
281
|
+
|
282
|
+
begin
|
283
|
+
if agent[:pid]
|
284
|
+
Process.kill('KILL', agent[:pid])
|
285
|
+
puts "✅ Agent '#{role}' terminated".colorize(:green)
|
286
|
+
end
|
287
|
+
|
288
|
+
cleanup_agent_resources(agent)
|
289
|
+
@notification_manager.notify(:agent_completed, "Agent '#{role}' terminated by user")
|
290
|
+
|
291
|
+
rescue StandardError => e
|
292
|
+
puts "❌ Error killing agent: #{e.message}".colorize(:red)
|
293
|
+
Logger.error("Failed to kill agent #{agent_id}: #{e.message}")
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def debug_agent(agent)
|
298
|
+
agent_id = agent[:id]
|
299
|
+
role = agent[:role]
|
300
|
+
|
301
|
+
puts "🔍 Debugging agent '#{role}'...".colorize(:blue)
|
302
|
+
|
303
|
+
# Show agent details
|
304
|
+
puts "\nAgent Details:".colorize(:blue)
|
305
|
+
puts " ID: #{agent_id}"
|
306
|
+
puts " Role: #{role}"
|
307
|
+
puts " PID: #{agent[:pid]}"
|
308
|
+
puts " Last Activity: #{agent[:last_activity]}"
|
309
|
+
puts " Current Task: #{agent[:current_task] || 'Unknown'}"
|
310
|
+
puts " Memory: #{agent[:memory_mb]}MB" if agent[:memory_mb]
|
311
|
+
|
312
|
+
# Show recent logs
|
313
|
+
show_agent_logs(agent)
|
314
|
+
|
315
|
+
# Show worktree status
|
316
|
+
show_agent_worktree_status(agent)
|
317
|
+
end
|
318
|
+
|
319
|
+
def handle_file_conflict(agents, details)
|
320
|
+
conflicted_file = details[:file]
|
321
|
+
conflicting_agents = details[:agents] || agents.select { |a| a[:status] == 'active' }
|
322
|
+
|
323
|
+
@notification_manager.intervention_needed(
|
324
|
+
"File conflict detected: #{conflicted_file}",
|
325
|
+
nil,
|
326
|
+
[
|
327
|
+
"Pause conflicting agents",
|
328
|
+
"Merge changes manually",
|
329
|
+
"Restart with coordination"
|
330
|
+
]
|
331
|
+
)
|
332
|
+
|
333
|
+
response = prompt_user_with_timeout(
|
334
|
+
"File conflict in #{conflicted_file}. Action? [p]ause/[m]erge/[r]estart",
|
335
|
+
timeout: RESPONSE_TIMEOUT,
|
336
|
+
default: 'p'
|
337
|
+
)
|
338
|
+
|
339
|
+
case response.downcase
|
340
|
+
when 'p', 'pause'
|
341
|
+
pause_conflicting_agents(conflicting_agents)
|
342
|
+
when 'm', 'merge'
|
343
|
+
initiate_manual_merge(conflicted_file, conflicting_agents)
|
344
|
+
when 'r', 'restart'
|
345
|
+
restart_with_coordination(conflicting_agents)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def handle_dependency_deadlock(agents, details)
|
350
|
+
@notification_manager.intervention_needed(
|
351
|
+
"Dependency deadlock detected between agents",
|
352
|
+
nil,
|
353
|
+
["Restart with updated dependencies", "Manual intervention"]
|
354
|
+
)
|
355
|
+
|
356
|
+
# Implementation for dependency deadlock resolution
|
357
|
+
puts "🔄 Resolving dependency deadlock...".colorize(:yellow)
|
358
|
+
end
|
359
|
+
|
360
|
+
def handle_resource_contention(agents, details)
|
361
|
+
resource = details[:resource]
|
362
|
+
|
363
|
+
@notification_manager.intervention_needed(
|
364
|
+
"Resource contention for #{resource}",
|
365
|
+
nil,
|
366
|
+
["Serialize access", "Increase resources"]
|
367
|
+
)
|
368
|
+
end
|
369
|
+
|
370
|
+
def analyze_error_for_suggestions(error, context)
|
371
|
+
suggestions = []
|
372
|
+
error_message = error.message.downcase
|
373
|
+
|
374
|
+
case error_message
|
375
|
+
when /timeout/
|
376
|
+
suggestions << "enhance-swarm restart #{context[:agent_id]} --timeout=300"
|
377
|
+
suggestions << "Check network connectivity"
|
378
|
+
when /permission denied/
|
379
|
+
suggestions << "Check file permissions"
|
380
|
+
suggestions << "Run with appropriate privileges"
|
381
|
+
when /no such file/
|
382
|
+
suggestions << "Verify file paths and dependencies"
|
383
|
+
suggestions << "Regenerate missing files"
|
384
|
+
when /memory|out of space/
|
385
|
+
suggestions << "enhance-swarm cleanup --all"
|
386
|
+
suggestions << "Increase available memory"
|
387
|
+
when /git/
|
388
|
+
suggestions << "Fix git repository state"
|
389
|
+
suggestions << "Reset to clean state"
|
390
|
+
else
|
391
|
+
suggestions << "enhance-swarm restart #{context[:agent_id]}"
|
392
|
+
suggestions << "Check logs for more details"
|
393
|
+
end
|
394
|
+
|
395
|
+
suggestions
|
396
|
+
end
|
397
|
+
|
398
|
+
def execute_error_recovery(agent, response, suggestions)
|
399
|
+
case response
|
400
|
+
when /^\d+$/
|
401
|
+
index = response.to_i - 1
|
402
|
+
if index >= 0 && index < suggestions.length
|
403
|
+
suggestion = suggestions[index]
|
404
|
+
puts "Executing: #{suggestion}".colorize(:blue)
|
405
|
+
|
406
|
+
if suggestion.start_with?('enhance-swarm')
|
407
|
+
execute_enhance_swarm_command(suggestion)
|
408
|
+
else
|
409
|
+
puts "Manual action required: #{suggestion}".colorize(:yellow)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
when 'c', 'custom'
|
413
|
+
custom_command = prompt_user_with_timeout("Enter custom command:", timeout: 60)
|
414
|
+
if custom_command && !custom_command.empty?
|
415
|
+
execute_custom_recovery_command(custom_command, agent)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def execute_enhance_swarm_command(command)
|
421
|
+
# Parse and execute enhance-swarm commands
|
422
|
+
parts = command.split(' ')
|
423
|
+
if parts.first == 'enhance-swarm'
|
424
|
+
puts "This would execute: #{command}".colorize(:blue)
|
425
|
+
# In real implementation, this would call the appropriate CLI method
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def execute_custom_recovery_command(command, agent)
|
430
|
+
puts "Executing custom recovery: #{command}".colorize(:blue)
|
431
|
+
|
432
|
+
begin
|
433
|
+
CommandExecutor.execute('bash', '-c', command)
|
434
|
+
puts "✅ Custom recovery command completed".colorize(:green)
|
435
|
+
rescue StandardError => e
|
436
|
+
puts "❌ Custom recovery failed: #{e.message}".colorize(:red)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def format_duration(seconds)
|
441
|
+
if seconds < 60
|
442
|
+
"#{seconds.round}s"
|
443
|
+
elsif seconds < 3600
|
444
|
+
"#{(seconds / 60).round}m"
|
445
|
+
else
|
446
|
+
"#{(seconds / 3600).round(1)}h"
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
def cleanup_agent_resources(agent)
|
451
|
+
# Clean up worktree, temp files, etc.
|
452
|
+
if agent[:worktree_path] && File.exist?(agent[:worktree_path])
|
453
|
+
puts "Cleaning up worktree: #{agent[:worktree_path]}".colorize(:yellow)
|
454
|
+
# Cleanup implementation
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def spawn_agent_replacement(agent)
|
459
|
+
# Spawn a new agent with the same configuration
|
460
|
+
# This would integrate with the Orchestrator
|
461
|
+
puts "Spawning replacement agent...".colorize(:blue)
|
462
|
+
# Return new PID or false if failed
|
463
|
+
nil
|
464
|
+
end
|
465
|
+
|
466
|
+
def show_agent_logs(agent)
|
467
|
+
puts "\nRecent Activity:".colorize(:blue)
|
468
|
+
# Show recent log entries for this agent
|
469
|
+
puts " (Log viewing not implemented in this demo)"
|
470
|
+
end
|
471
|
+
|
472
|
+
def show_agent_worktree_status(agent)
|
473
|
+
puts "\nWorktree Status:".colorize(:blue)
|
474
|
+
if agent[:worktree_path]
|
475
|
+
puts " Path: #{agent[:worktree_path]}"
|
476
|
+
# Show git status, recent commits, etc.
|
477
|
+
else
|
478
|
+
puts " No worktree assigned"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
def pause_conflicting_agents(agents)
|
483
|
+
puts "Pausing conflicting agents...".colorize(:yellow)
|
484
|
+
agents.each do |agent|
|
485
|
+
# Send pause signal to agents
|
486
|
+
puts " Paused: #{agent[:role]}"
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def initiate_manual_merge(file, agents)
|
491
|
+
puts "Initiating manual merge for #{file}...".colorize(:blue)
|
492
|
+
puts "Please resolve conflicts and run: enhance-swarm resume"
|
493
|
+
end
|
494
|
+
|
495
|
+
def restart_with_coordination(agents)
|
496
|
+
puts "Restarting agents with improved coordination...".colorize(:blue)
|
497
|
+
agents.each do |agent|
|
498
|
+
restart_agent(agent)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def handle_process_issue(agent)
|
503
|
+
puts "Process issue detected for agent #{agent[:role]}".colorize(:red)
|
504
|
+
@notification_manager.agent_failed(
|
505
|
+
agent[:id],
|
506
|
+
agent[:role],
|
507
|
+
"Process terminated unexpectedly",
|
508
|
+
["enhance-swarm restart #{agent[:id]}", "Check system resources"]
|
509
|
+
)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|