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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.enhance_swarm/agent_scripts/frontend_agent.md +39 -0
  3. data/.enhance_swarm/user_patterns.json +37 -0
  4. data/CHANGELOG.md +184 -0
  5. data/LICENSE +21 -0
  6. data/PRODUCTION_TEST_LOG.md +502 -0
  7. data/README.md +905 -0
  8. data/Rakefile +28 -0
  9. data/USAGE_EXAMPLES.md +477 -0
  10. data/examples/enhance_workflow.md +346 -0
  11. data/examples/rails_project.md +253 -0
  12. data/exe/enhance-swarm +30 -0
  13. data/lib/enhance_swarm/additional_commands.rb +299 -0
  14. data/lib/enhance_swarm/agent_communicator.rb +460 -0
  15. data/lib/enhance_swarm/agent_reviewer.rb +283 -0
  16. data/lib/enhance_swarm/agent_spawner.rb +462 -0
  17. data/lib/enhance_swarm/cleanup_manager.rb +245 -0
  18. data/lib/enhance_swarm/cli.rb +1592 -0
  19. data/lib/enhance_swarm/command_executor.rb +78 -0
  20. data/lib/enhance_swarm/configuration.rb +324 -0
  21. data/lib/enhance_swarm/control_agent.rb +307 -0
  22. data/lib/enhance_swarm/dependency_validator.rb +195 -0
  23. data/lib/enhance_swarm/error_recovery.rb +785 -0
  24. data/lib/enhance_swarm/generator.rb +194 -0
  25. data/lib/enhance_swarm/interrupt_handler.rb +512 -0
  26. data/lib/enhance_swarm/logger.rb +106 -0
  27. data/lib/enhance_swarm/mcp_integration.rb +85 -0
  28. data/lib/enhance_swarm/monitor.rb +28 -0
  29. data/lib/enhance_swarm/notification_manager.rb +444 -0
  30. data/lib/enhance_swarm/orchestrator.rb +313 -0
  31. data/lib/enhance_swarm/output_streamer.rb +281 -0
  32. data/lib/enhance_swarm/process_monitor.rb +266 -0
  33. data/lib/enhance_swarm/progress_tracker.rb +215 -0
  34. data/lib/enhance_swarm/project_analyzer.rb +612 -0
  35. data/lib/enhance_swarm/resource_manager.rb +177 -0
  36. data/lib/enhance_swarm/retry_handler.rb +40 -0
  37. data/lib/enhance_swarm/session_manager.rb +247 -0
  38. data/lib/enhance_swarm/signal_handler.rb +95 -0
  39. data/lib/enhance_swarm/smart_defaults.rb +708 -0
  40. data/lib/enhance_swarm/task_integration.rb +150 -0
  41. data/lib/enhance_swarm/task_manager.rb +174 -0
  42. data/lib/enhance_swarm/version.rb +5 -0
  43. data/lib/enhance_swarm/visual_dashboard.rb +555 -0
  44. data/lib/enhance_swarm/web_ui.rb +211 -0
  45. data/lib/enhance_swarm.rb +69 -0
  46. data/setup.sh +86 -0
  47. data/sig/enhance_swarm.rbs +4 -0
  48. data/templates/claude/CLAUDE.md +160 -0
  49. data/templates/claude/MCP.md +117 -0
  50. data/templates/claude/PERSONAS.md +114 -0
  51. data/templates/claude/RULES.md +221 -0
  52. data/test_builtin_functionality.rb +121 -0
  53. data/test_core_components.rb +156 -0
  54. data/test_real_claude_integration.rb +285 -0
  55. data/test_security.rb +150 -0
  56. data/test_smart_defaults.rb +155 -0
  57. data/test_task_integration.rb +173 -0
  58. data/test_web_ui.rb +245 -0
  59. data/web/assets/css/main.css +645 -0
  60. data/web/assets/js/kanban.js +499 -0
  61. data/web/assets/js/main.js +525 -0
  62. data/web/templates/dashboard.html.erb +226 -0
  63. data/web/templates/kanban.html.erb +193 -0
  64. metadata +293 -0
@@ -0,0 +1,555 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'colorize'
5
+ require 'json'
6
+
7
+ module EnhanceSwarm
8
+ class VisualDashboard
9
+ include Singleton
10
+
11
+ def initialize
12
+ @agents = {}
13
+ @coordination_status = {}
14
+ @dashboard_active = false
15
+ @refresh_rate = 2 # seconds
16
+ @last_update = Time.now
17
+ @terminal_size = get_terminal_size
18
+ end
19
+
20
+ # Start the visual dashboard
21
+ def start_dashboard(agents = [])
22
+ @dashboard_active = true
23
+ @agents = agents.each_with_object({}) { |agent, hash| hash[agent[:id]] = agent }
24
+
25
+ puts "šŸ–„ļø Starting Visual Agent Dashboard...".colorize(:green)
26
+ puts "Press 'q' to quit, 'r' to refresh, 'p' to pause".colorize(:light_black)
27
+
28
+ setup_terminal
29
+ display_loop
30
+ end
31
+
32
+ def stop_dashboard
33
+ @dashboard_active = false
34
+ restore_terminal
35
+ puts "\nšŸ–„ļø Dashboard stopped".colorize(:yellow)
36
+ end
37
+
38
+ # Update agent status
39
+ def update_agent(agent_id, updates)
40
+ return unless @agents[agent_id]
41
+
42
+ @agents[agent_id].merge!(updates)
43
+ @last_update = Time.now
44
+ end
45
+
46
+ # Update coordination status
47
+ def update_coordination(status)
48
+ @coordination_status = status
49
+ @last_update = Time.now
50
+ end
51
+
52
+ # Add new agent to dashboard
53
+ def add_agent(agent)
54
+ @agents[agent[:id]] = agent
55
+ @last_update = Time.now
56
+ end
57
+
58
+ # Remove agent from dashboard
59
+ def remove_agent(agent_id)
60
+ @agents.delete(agent_id)
61
+ @last_update = Time.now
62
+ end
63
+
64
+ # Display a static snapshot of agent status
65
+ def display_snapshot(agents = [])
66
+ @agents = agents.each_with_object({}) { |agent, hash| hash[agent[:id]] = agent }
67
+
68
+ puts "šŸ“ø EnhanceSwarm Dashboard Snapshot".colorize(:cyan)
69
+ puts "─" * 50
70
+ puts "Timestamp: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
71
+ puts
72
+
73
+ if @agents.empty?
74
+ puts "No agents currently running".colorize(:light_black)
75
+ return
76
+ end
77
+
78
+ puts "šŸ¤– Agent Status:".colorize(:blue)
79
+ @agents.each do |id, agent|
80
+ role = agent[:role] || 'unknown'
81
+ status = format_agent_status(agent)
82
+ progress = agent[:progress_percentage] || agent[:progress] || 0
83
+ duration = format_duration(agent)
84
+
85
+ puts " #{role.ljust(10)} │ #{status} │ #{progress}% │ #{duration}"
86
+ end
87
+
88
+ puts
89
+ puts "System Resources:".colorize(:blue)
90
+ memory_info = get_memory_info
91
+ puts " Memory: #{memory_info[:used_gb]}GB/#{memory_info[:total_gb]}GB (#{memory_info[:used_percent]}%)"
92
+ puts " Active Processes: #{@agents.count { |_, agent| agent[:pid] }}"
93
+
94
+ puts "\nšŸ“Š Summary:".colorize(:green)
95
+ puts " Total Agents: #{@agents.count}"
96
+ puts " Active: #{@agents.count { |_, agent| agent[:status] == 'active' || agent[:status] == 'running' }}"
97
+ puts " Completed: #{@agents.count { |_, agent| agent[:status] == 'completed' }}"
98
+ puts " Failed: #{@agents.count { |_, agent| agent[:status] == 'failed' }}"
99
+ end
100
+
101
+ private
102
+
103
+ def setup_terminal
104
+ # Hide cursor and enable raw mode for input
105
+ print "\e[?25l" # Hide cursor
106
+ print "\e[2J" # Clear screen
107
+ print "\e[H" # Move to top-left
108
+ end
109
+
110
+ def restore_terminal
111
+ print "\e[?25h" # Show cursor
112
+ print "\e[0m" # Reset colors
113
+ end
114
+
115
+ def display_loop
116
+ while @dashboard_active
117
+ render_dashboard
118
+
119
+ # Check for user input (non-blocking)
120
+ if input_available?
121
+ key = $stdin.getc
122
+ handle_input(key)
123
+ end
124
+
125
+ sleep(@refresh_rate)
126
+ end
127
+ rescue Interrupt
128
+ @dashboard_active = false
129
+ ensure
130
+ restore_terminal
131
+ end
132
+
133
+ def render_dashboard
134
+ clear_screen
135
+
136
+ # Header
137
+ render_header
138
+
139
+ # Coordination overview
140
+ render_coordination_overview
141
+
142
+ # Agent grid
143
+ render_agent_grid
144
+
145
+ # Status bar
146
+ render_status_bar
147
+
148
+ # Controls
149
+ render_controls
150
+ end
151
+
152
+ def clear_screen
153
+ print "\e[2J\e[H"
154
+ end
155
+
156
+ def render_header
157
+ time_str = Time.now.strftime('%H:%M:%S')
158
+ agent_count = @agents.count
159
+ active_count = @agents.count { |_, agent| agent[:status] == 'active' }
160
+
161
+ puts "ā”Œā”€ā”€ā”€ šŸ–„ļø EnhanceSwarm Visual Dashboard ─────────────────────────────────┐".colorize(:cyan)
162
+ puts "│ #{time_str} │ Agents: #{agent_count} │ Active: #{active_count} │ Updated: #{time_ago(@last_update)} ago │".colorize(:white)
163
+ puts "ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜".colorize(:cyan)
164
+ puts
165
+ end
166
+
167
+ def render_coordination_overview
168
+ puts "šŸ“Š Coordination Status".colorize(:blue)
169
+ puts "─" * 40
170
+
171
+ if @coordination_status.any?
172
+ phase = @coordination_status[:phase] || 'Unknown'
173
+ progress = @coordination_status[:progress] || 0
174
+ active_agents = @coordination_status[:active_agents] || []
175
+ completed_agents = @coordination_status[:completed_agents] || []
176
+
177
+ puts "Phase: #{phase}".colorize(:yellow)
178
+ puts "Progress: #{render_progress_bar(progress, 30)} #{progress}%"
179
+ puts "Active: #{active_agents.join(', ')}" if active_agents.any?
180
+ puts "Completed: #{completed_agents.join(', ')}" if completed_agents.any?
181
+ else
182
+ puts "No active coordination".colorize(:light_black)
183
+ end
184
+
185
+ puts
186
+ end
187
+
188
+ def render_agent_grid
189
+ puts "šŸ¤– Agent Status Grid".colorize(:blue)
190
+ puts "─" * 60
191
+
192
+ if @agents.empty?
193
+ puts "No agents to display".colorize(:light_black)
194
+ return
195
+ end
196
+
197
+ # Calculate grid layout
198
+ terminal_width = @terminal_size[:width] || 80
199
+ agent_width = 18
200
+ cols = [terminal_width / agent_width, 1].max
201
+
202
+ @agents.values.each_slice(cols) do |agent_row|
203
+ render_agent_row(agent_row)
204
+ puts
205
+ end
206
+ end
207
+
208
+ def render_agent_row(agents)
209
+ # Top border
210
+ agents.each { print "ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” " }
211
+ puts
212
+
213
+ # Agent ID and role
214
+ agents.each do |agent|
215
+ role = (agent[:role] || 'unknown')[0..7].ljust(8)
216
+ print "│ #{role} │ "
217
+ end
218
+ puts
219
+
220
+ # Status line
221
+ agents.each do |agent|
222
+ status = format_agent_status(agent)
223
+ print "│ #{status.ljust(14)} │ "
224
+ end
225
+ puts
226
+
227
+ # Progress line
228
+ agents.each do |agent|
229
+ progress = render_agent_progress(agent)
230
+ print "│ #{progress} │ "
231
+ end
232
+ puts
233
+
234
+ # Duration line
235
+ agents.each do |agent|
236
+ duration = format_duration(agent)
237
+ print "│ #{duration.ljust(14)} │ "
238
+ end
239
+ puts
240
+
241
+ # Bottom border
242
+ agents.each { print "ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ " }
243
+ puts
244
+ end
245
+
246
+ def format_agent_status(agent)
247
+ status = agent[:status] || 'unknown'
248
+
249
+ case status
250
+ when 'active'
251
+ "🟢 Active".colorize(:green)
252
+ when 'completed'
253
+ "āœ… Done".colorize(:green)
254
+ when 'failed'
255
+ "āŒ Failed".colorize(:red)
256
+ when 'stuck'
257
+ "āš ļø Stuck".colorize(:yellow)
258
+ when 'starting'
259
+ "šŸ”„ Starting".colorize(:blue)
260
+ else
261
+ "⚪ #{status.capitalize}".colorize(:light_black)
262
+ end
263
+ end
264
+
265
+ def render_agent_progress(agent)
266
+ if agent[:progress_percentage]
267
+ percentage = agent[:progress_percentage].to_i
268
+ bar = render_mini_progress_bar(percentage, 12)
269
+ "#{bar} #{percentage}%"
270
+ elsif agent[:current_task]
271
+ task = agent[:current_task][0..11]
272
+ task.ljust(14)
273
+ else
274
+ " "
275
+ end
276
+ end
277
+
278
+ def format_duration(agent)
279
+ if agent[:start_time]
280
+ duration = Time.now - Time.parse(agent[:start_time])
281
+ format_time_duration(duration)
282
+ else
283
+ ""
284
+ end
285
+ end
286
+
287
+ def render_status_bar
288
+ puts "šŸ“ˆ System Resources".colorize(:blue)
289
+ puts "─" * 30
290
+
291
+ # Memory usage
292
+ memory_info = get_memory_info
293
+ puts "Memory: #{render_progress_bar(memory_info[:used_percent], 20)} #{memory_info[:used_gb]}GB/#{memory_info[:total_gb]}GB"
294
+
295
+ # Active processes
296
+ process_count = @agents.count { |_, agent| agent[:pid] }
297
+ puts "Processes: #{process_count} active"
298
+
299
+ # Communication queue
300
+ if defined?(AgentCommunicator)
301
+ pending_messages = AgentCommunicator.instance.pending_messages.count
302
+ puts "Messages: #{pending_messages} pending"
303
+ end
304
+
305
+ puts
306
+ end
307
+
308
+ def render_controls
309
+ puts "šŸŽ® Controls".colorize(:blue)
310
+ puts "─" * 15
311
+ puts "[q] Quit [r] Refresh [p] Pause [c] Clear [h] Help"
312
+ end
313
+
314
+ def render_progress_bar(percentage, width)
315
+ filled = (percentage * width / 100.0).round
316
+ empty = width - filled
317
+
318
+ bar = "ā–ˆ" * filled + "ā–‘" * empty
319
+ case percentage
320
+ when 0..30
321
+ bar.colorize(:red)
322
+ when 31..70
323
+ bar.colorize(:yellow)
324
+ else
325
+ bar.colorize(:green)
326
+ end
327
+ end
328
+
329
+ def render_mini_progress_bar(percentage, width)
330
+ filled = (percentage * width / 100.0).round
331
+ empty = width - filled
332
+
333
+ "ā–ˆ" * filled + "ā–‘" * empty
334
+ end
335
+
336
+ def handle_input(key)
337
+ case key.downcase
338
+ when 'q'
339
+ @dashboard_active = false
340
+ when 'r'
341
+ # Force refresh
342
+ @last_update = Time.now
343
+ when 'p'
344
+ pause_dashboard
345
+ when 'c'
346
+ clear_screen
347
+ when 'h'
348
+ show_help
349
+ when 's'
350
+ save_dashboard_snapshot
351
+ when 'd'
352
+ show_detailed_view
353
+ end
354
+ end
355
+
356
+ def pause_dashboard
357
+ puts "\nāøļø Dashboard paused. Press any key to continue...".colorize(:yellow)
358
+ $stdin.getc
359
+ end
360
+
361
+ def show_help
362
+ clear_screen
363
+ puts "šŸ–„ļø EnhanceSwarm Dashboard Help".colorize(:cyan)
364
+ puts "─" * 40
365
+ puts
366
+ puts "Controls:".colorize(:blue)
367
+ puts " q - Quit dashboard"
368
+ puts " r - Force refresh"
369
+ puts " p - Pause/resume"
370
+ puts " c - Clear screen"
371
+ puts " s - Save snapshot"
372
+ puts " d - Detailed view"
373
+ puts " h - Show this help"
374
+ puts
375
+ puts "Agent Status Icons:".colorize(:blue)
376
+ puts " 🟢 Active - Agent is working"
377
+ puts " āœ… Done - Agent completed successfully"
378
+ puts " āŒ Failed - Agent encountered an error"
379
+ puts " āš ļø Stuck - Agent appears stuck"
380
+ puts " šŸ”„ Starting - Agent is initializing"
381
+ puts
382
+ puts "Press any key to return...".colorize(:light_black)
383
+ $stdin.getc
384
+ end
385
+
386
+ def save_dashboard_snapshot
387
+ timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
388
+ filename = ".enhance_swarm/dashboard_snapshot_#{timestamp}.json"
389
+
390
+ snapshot = {
391
+ timestamp: Time.now.iso8601,
392
+ agents: @agents,
393
+ coordination: @coordination_status,
394
+ system_info: get_system_snapshot
395
+ }
396
+
397
+ FileUtils.mkdir_p(File.dirname(filename))
398
+ File.write(filename, JSON.pretty_generate(snapshot))
399
+
400
+ flash_message("šŸ’¾ Snapshot saved: #{filename}")
401
+ end
402
+
403
+ def show_detailed_view
404
+ clear_screen
405
+ puts "šŸ“‹ Detailed Agent View".colorize(:cyan)
406
+ puts "─" * 50
407
+
408
+ @agents.each do |id, agent|
409
+ puts "\nšŸ¤– #{agent[:role] || 'Unknown'} (#{id})".colorize(:blue)
410
+ puts " Status: #{format_agent_status(agent)}"
411
+ puts " PID: #{agent[:pid] || 'N/A'}"
412
+ puts " Task: #{agent[:current_task] || 'N/A'}"
413
+ puts " Progress: #{agent[:progress_percentage] || 0}%"
414
+ puts " Duration: #{format_duration(agent)}"
415
+ puts " Memory: #{agent[:memory_mb] || 'N/A'}MB" if agent[:memory_mb]
416
+ puts " Output: #{agent[:output_path] || 'N/A'}" if agent[:output_path]
417
+ end
418
+
419
+ puts "\nPress any key to return...".colorize(:light_black)
420
+ $stdin.getc
421
+ end
422
+
423
+ def flash_message(message)
424
+ # Save current position and show message
425
+ print "\e[s" # Save cursor position
426
+ height = @terminal_size[:height] || 24
427
+ print "\e[#{height - 2};1H" # Move to bottom
428
+ print message.colorize(:green)
429
+ sleep(2)
430
+ print "\e[u" # Restore cursor position
431
+ end
432
+
433
+ def input_available?
434
+ # Check if we're in an interactive terminal first
435
+ return false unless $stdin.tty?
436
+
437
+ # Non-blocking input check
438
+ ready = IO.select([$stdin], nil, nil, 0)
439
+ ready && ready[0].include?($stdin)
440
+ rescue StandardError
441
+ false
442
+ end
443
+
444
+ def get_terminal_size
445
+ begin
446
+ # Check if we're in an interactive terminal
447
+ return { width: 80, height: 24 } unless $stdin.tty?
448
+
449
+ stty_output = `stty size 2>/dev/null`.strip
450
+ return { width: 80, height: 24 } if stty_output.empty?
451
+
452
+ rows, cols = stty_output.split.map(&:to_i)
453
+ return { width: 80, height: 24 } if rows == 0 || cols == 0
454
+
455
+ { width: cols, height: rows }
456
+ rescue StandardError
457
+ { width: 80, height: 24 }
458
+ end
459
+ end
460
+
461
+ def get_memory_info
462
+ begin
463
+ if RUBY_PLATFORM.include?('darwin') # macOS
464
+ vm_stat = `vm_stat`
465
+ page_size = 4096
466
+
467
+ pages_free = vm_stat[/Pages free:\s+(\d+)/, 1].to_i
468
+ pages_wired = vm_stat[/Pages wired down:\s+(\d+)/, 1].to_i
469
+ pages_active = vm_stat[/Pages active:\s+(\d+)/, 1].to_i
470
+ pages_inactive = vm_stat[/Pages inactive:\s+(\d+)/, 1].to_i
471
+
472
+ total_pages = pages_free + pages_wired + pages_active + pages_inactive
473
+ used_pages = total_pages - pages_free
474
+
475
+ total_gb = (total_pages * page_size / 1024.0 / 1024.0 / 1024.0).round(1)
476
+ used_gb = (used_pages * page_size / 1024.0 / 1024.0 / 1024.0).round(1)
477
+ used_percent = ((used_pages.to_f / total_pages) * 100).round
478
+
479
+ { total_gb: total_gb, used_gb: used_gb, used_percent: used_percent }
480
+ else
481
+ # Default fallback
482
+ { total_gb: 8.0, used_gb: 4.0, used_percent: 50 }
483
+ end
484
+ rescue
485
+ { total_gb: 8.0, used_gb: 4.0, used_percent: 50 }
486
+ end
487
+ end
488
+
489
+ def get_system_snapshot
490
+ {
491
+ memory: get_memory_info,
492
+ terminal_size: @terminal_size,
493
+ ruby_version: RUBY_VERSION,
494
+ platform: RUBY_PLATFORM,
495
+ timestamp: Time.now.iso8601
496
+ }
497
+ end
498
+
499
+ def time_ago(time)
500
+ seconds = Time.now - time
501
+
502
+ if seconds < 60
503
+ "#{seconds.round}s"
504
+ elsif seconds < 3600
505
+ "#{(seconds / 60).round}m"
506
+ else
507
+ "#{(seconds / 3600).round}h"
508
+ end
509
+ end
510
+
511
+ def format_time_duration(seconds)
512
+ if seconds < 60
513
+ "#{seconds.round}s"
514
+ elsif seconds < 3600
515
+ minutes = seconds / 60
516
+ "#{minutes.round}m"
517
+ else
518
+ hours = seconds / 3600
519
+ minutes = (seconds % 3600) / 60
520
+ "#{hours.round}h#{minutes.round}m"
521
+ end
522
+ end
523
+
524
+ # Class methods for singleton access
525
+ class << self
526
+ def instance
527
+ @instance ||= new
528
+ end
529
+
530
+ def start_dashboard(*args)
531
+ instance.start_dashboard(*args)
532
+ end
533
+
534
+ def stop_dashboard
535
+ instance.stop_dashboard
536
+ end
537
+
538
+ def update_agent(*args)
539
+ instance.update_agent(*args)
540
+ end
541
+
542
+ def update_coordination(*args)
543
+ instance.update_coordination(*args)
544
+ end
545
+
546
+ def add_agent(*args)
547
+ instance.add_agent(*args)
548
+ end
549
+
550
+ def remove_agent(*args)
551
+ instance.remove_agent(*args)
552
+ end
553
+ end
554
+ end
555
+ end