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,708 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'yaml'
5
+ require 'json'
6
+
7
+ module EnhanceSwarm
8
+ class SmartDefaults
9
+ include Singleton
10
+
11
+ DEFAULT_SETTINGS_FILE = '.enhance_swarm/smart_defaults.yml'
12
+ USER_PATTERNS_FILE = '.enhance_swarm/user_patterns.json'
13
+
14
+ def initialize
15
+ @settings = load_settings
16
+ @user_patterns = load_user_patterns
17
+ @project_context = analyze_project_context
18
+ ensure_settings_directory
19
+ end
20
+
21
+ # Detect optimal role for a given task
22
+ def suggest_role_for_task(task_description)
23
+ task_lower = task_description.downcase
24
+
25
+ # Check for explicit role keywords
26
+ role_keywords = {
27
+ 'backend' => %w[api server database model migration schema endpoint route controller service auth jwt],
28
+ 'frontend' => %w[ui ux component view page template css html javascript react vue angular design layout],
29
+ 'qa' => %w[test testing spec unit integration e2e selenium cypress jest rspec quality assurance bug],
30
+ 'ux' => %w[design user experience wireframe mockup prototype accessibility usability flow journey]
31
+ }
32
+
33
+ # Calculate keyword matches for each role
34
+ role_scores = role_keywords.transform_values do |keywords|
35
+ keywords.count { |keyword| task_lower.include?(keyword) }
36
+ end
37
+
38
+ # Add context from user patterns
39
+ if @user_patterns['role_preferences']
40
+ @user_patterns['role_preferences'].each do |role, weight|
41
+ role_scores[role] = (role_scores[role] || 0) + weight.to_f
42
+ end
43
+ end
44
+
45
+ # Return the role with highest score, or 'general' if tied
46
+ best_role = role_scores.max_by { |_, score| score }
47
+ best_role && best_role[1] > 0 ? best_role[0] : 'general'
48
+ end
49
+
50
+ # Suggest optimal configuration based on project type
51
+ def suggest_configuration
52
+ config = {}
53
+
54
+ # Detect project type and suggest stack
55
+ project_type = detect_project_type
56
+ config[:project_type] = project_type
57
+ config[:technology_stack] = suggest_technology_stack(project_type)
58
+
59
+ # Suggest commands based on project files
60
+ config[:commands] = suggest_commands(project_type)
61
+
62
+ # Suggest orchestration settings
63
+ config[:orchestration] = suggest_orchestration_settings
64
+
65
+ # Add MCP tool suggestions
66
+ config[:mcp_tools] = suggest_mcp_tools(project_type)
67
+
68
+ config
69
+ end
70
+
71
+ # Auto-retry with intelligent backoff
72
+ def auto_retry_with_backoff(operation, max_retries: 3, base_delay: 1)
73
+ attempt = 0
74
+ last_error = nil
75
+
76
+ loop do
77
+ attempt += 1
78
+
79
+ begin
80
+ result = yield
81
+
82
+ # Record successful pattern
83
+ record_success_pattern(operation, attempt)
84
+ return result
85
+
86
+ rescue StandardError => e
87
+ last_error = e
88
+
89
+ if attempt >= max_retries
90
+ record_failure_pattern(operation, attempt, e)
91
+ raise e
92
+ end
93
+
94
+ # Calculate exponential backoff with jitter
95
+ delay = base_delay * (2 ** (attempt - 1)) + rand(0.5)
96
+ delay = [delay, 30].min # Cap at 30 seconds
97
+
98
+ Logger.info("Retry #{attempt}/#{max_retries} for #{operation} in #{delay.round(1)}s: #{e.message}")
99
+ sleep(delay)
100
+ end
101
+ end
102
+ end
103
+
104
+ # Suggest next actions based on current state
105
+ def suggest_next_actions(current_context = {})
106
+ suggestions = []
107
+
108
+ # Check for common issues and suggest fixes
109
+ if stale_worktrees_detected?
110
+ suggestions << {
111
+ action: 'cleanup',
112
+ command: 'enhance-swarm cleanup --all',
113
+ reason: 'Stale worktrees detected',
114
+ priority: :medium
115
+ }
116
+ end
117
+
118
+ if pending_agent_messages?
119
+ suggestions << {
120
+ action: 'communicate',
121
+ command: 'enhance-swarm communicate --interactive',
122
+ reason: 'Pending agent messages need responses',
123
+ priority: :high
124
+ }
125
+ end
126
+
127
+ # Suggest based on project state
128
+ if tests_need_running?
129
+ suggestions << {
130
+ action: 'test',
131
+ command: determine_test_command,
132
+ reason: 'Code changes detected, tests should be run',
133
+ priority: :medium
134
+ }
135
+ end
136
+
137
+ # Suggest based on user patterns
138
+ time_based_suggestions.each { |suggestion| suggestions << suggestion }
139
+
140
+ suggestions.sort_by { |s| priority_weight(s[:priority]) }.reverse
141
+ end
142
+
143
+ # Auto-cleanup stale resources
144
+ def auto_cleanup_if_needed
145
+ cleanup_actions = []
146
+
147
+ # Check for stale worktrees (older than 1 day)
148
+ stale_worktrees = find_stale_worktrees
149
+ if stale_worktrees.count > 3
150
+ cleanup_actions << proc do
151
+ CleanupManager.cleanup_stale_worktrees
152
+ Logger.info("Auto-cleaned #{stale_worktrees.count} stale worktrees")
153
+ end
154
+ end
155
+
156
+ # Check for old communication files (older than 7 days)
157
+ old_comm_files = find_old_communication_files
158
+ if old_comm_files.count > 20
159
+ cleanup_actions << proc do
160
+ AgentCommunicator.instance.cleanup_old_messages(7)
161
+ Logger.info("Auto-cleaned #{old_comm_files.count} old communication files")
162
+ end
163
+ end
164
+
165
+ # Check for old notification history (older than 14 days)
166
+ if notification_history_size > 100
167
+ cleanup_actions << proc do
168
+ NotificationManager.instance.clear_history
169
+ Logger.info("Auto-cleaned notification history")
170
+ end
171
+ end
172
+
173
+ # Execute cleanup actions
174
+ cleanup_actions.each(&:call)
175
+
176
+ cleanup_actions.count
177
+ end
178
+
179
+ # Learn from user actions and update patterns
180
+ def learn_from_action(action_type, details = {})
181
+ @user_patterns['actions'] ||= {}
182
+ @user_patterns['actions'][action_type] ||= {
183
+ 'count' => 0,
184
+ 'last_used' => nil,
185
+ 'success_rate' => 1.0,
186
+ 'preferences' => {}
187
+ }
188
+
189
+ action_data = @user_patterns['actions'][action_type]
190
+ action_data['count'] += 1
191
+ action_data['last_used'] = Time.now.iso8601
192
+
193
+ # Update preferences based on details
194
+ details.each do |key, value|
195
+ action_data['preferences'][key.to_s] ||= {}
196
+ action_data['preferences'][key.to_s][value.to_s] ||= 0
197
+ action_data['preferences'][key.to_s][value.to_s] += 1
198
+ end
199
+
200
+ save_user_patterns
201
+ end
202
+
203
+ # Get context-aware command suggestions
204
+ def suggest_commands_for_context(context = {})
205
+ suggestions = []
206
+
207
+ # Based on current directory contents
208
+ if File.exist?('package.json')
209
+ suggestions << 'npm test' if context[:changed_files]&.any? { |f| f.end_with?('.js', '.ts') }
210
+ suggestions << 'npm run build' if context[:action] == 'deploy'
211
+ end
212
+
213
+ if File.exist?('Gemfile')
214
+ suggestions << 'bundle exec rspec' if context[:changed_files]&.any? { |f| f.end_with?('.rb') }
215
+ suggestions << 'bundle exec rubocop' if context[:action] == 'lint'
216
+ end
217
+
218
+ # Based on git status
219
+ if context[:git_status]
220
+ if context[:git_status][:modified_files] && context[:git_status][:modified_files] > 0
221
+ suggestions << 'enhance-swarm review'
222
+ end
223
+
224
+ if context[:git_status][:untracked_files] && context[:git_status][:untracked_files] > 0
225
+ suggestions << 'git add .'
226
+ end
227
+ end
228
+
229
+ # Based on user patterns
230
+ frequent_commands.each { |cmd| suggestions << cmd }
231
+
232
+ suggestions.uniq
233
+ end
234
+
235
+ # Auto-detect optimal concurrency settings
236
+ def suggest_concurrency_settings
237
+ # Base on system resources
238
+ cpu_cores = detect_cpu_cores
239
+ available_memory_gb = detect_available_memory
240
+
241
+ # Conservative defaults
242
+ max_agents = [cpu_cores / 2, 4].min
243
+ max_agents = [max_agents, 2].max # At least 2
244
+
245
+ # Adjust based on memory
246
+ if available_memory_gb < 4
247
+ max_agents = [max_agents, 2].min
248
+ elsif available_memory_gb > 16
249
+ max_agents = [max_agents + 2, 8].min
250
+ end
251
+
252
+ {
253
+ max_concurrent_agents: max_agents,
254
+ monitor_interval: max_agents > 4 ? 15 : 30,
255
+ timeout_multiplier: available_memory_gb < 8 ? 1.5 : 1.0
256
+ }
257
+ end
258
+
259
+ private
260
+
261
+ def load_settings
262
+ return default_settings unless File.exist?(DEFAULT_SETTINGS_FILE)
263
+
264
+ YAML.load_file(DEFAULT_SETTINGS_FILE)
265
+ rescue StandardError
266
+ default_settings
267
+ end
268
+
269
+ def load_user_patterns
270
+ return {} unless File.exist?(USER_PATTERNS_FILE)
271
+
272
+ JSON.parse(File.read(USER_PATTERNS_FILE))
273
+ rescue StandardError
274
+ {}
275
+ end
276
+
277
+ def save_user_patterns
278
+ ensure_settings_directory
279
+ File.write(USER_PATTERNS_FILE, JSON.pretty_generate(@user_patterns))
280
+ end
281
+
282
+ def ensure_settings_directory
283
+ dir = File.dirname(DEFAULT_SETTINGS_FILE)
284
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
285
+ end
286
+
287
+ def default_settings
288
+ {
289
+ 'auto_cleanup' => true,
290
+ 'auto_retry' => true,
291
+ 'smart_suggestions' => true,
292
+ 'learning_enabled' => true,
293
+ 'cleanup_threshold_days' => 7
294
+ }
295
+ end
296
+
297
+ def analyze_project_context
298
+ context = {}
299
+
300
+ # Git information
301
+ if Dir.exist?('.git')
302
+ context[:git] = {
303
+ branch: `git branch --show-current`.strip,
304
+ status: git_status_summary,
305
+ recent_commits: recent_commit_count
306
+ }
307
+ end
308
+
309
+ # Project files
310
+ context[:files] = {
311
+ package_json: File.exist?('package.json'),
312
+ gemfile: File.exist?('Gemfile'),
313
+ dockerfile: File.exist?('Dockerfile'),
314
+ config_files: Dir.glob('*.{yml,yaml,json,toml}').count
315
+ }
316
+
317
+ # Directory structure
318
+ context[:structure] = {
319
+ src_dirs: Dir.glob('{src,lib,app}').count,
320
+ test_dirs: Dir.glob('{test,spec,__tests__}').count,
321
+ has_nested_structure: Dir.glob('*/*').count > 10
322
+ }
323
+
324
+ context
325
+ end
326
+
327
+ def detect_project_type
328
+ return 'rails' if File.exist?('Gemfile') && File.exist?('config/application.rb')
329
+ return 'node' if File.exist?('package.json')
330
+ return 'python' if File.exist?('requirements.txt') || File.exist?('pyproject.toml')
331
+ return 'docker' if File.exist?('Dockerfile')
332
+ return 'static' if Dir.glob('*.html').any?
333
+
334
+ 'general'
335
+ end
336
+
337
+ def suggest_technology_stack(project_type)
338
+ case project_type
339
+ when 'rails'
340
+ ['Ruby on Rails', 'PostgreSQL', 'Redis', 'Sidekiq']
341
+ when 'node'
342
+ detect_node_stack
343
+ when 'python'
344
+ detect_python_stack
345
+ when 'docker'
346
+ ['Docker', 'Docker Compose']
347
+ else
348
+ []
349
+ end
350
+ end
351
+
352
+ def detect_node_stack
353
+ stack = ['Node.js']
354
+
355
+ if File.exist?('package.json')
356
+ package_json = JSON.parse(File.read('package.json'))
357
+ deps = package_json['dependencies'] || {}
358
+ dev_deps = package_json['devDependencies'] || {}
359
+ all_deps = deps.merge(dev_deps)
360
+
361
+ stack << 'React' if all_deps.key?('react')
362
+ stack << 'Vue.js' if all_deps.key?('vue')
363
+ stack << 'Angular' if all_deps.key?('@angular/core')
364
+ stack << 'Express' if all_deps.key?('express')
365
+ stack << 'TypeScript' if all_deps.key?('typescript')
366
+ stack << 'Webpack' if all_deps.key?('webpack')
367
+ stack << 'Jest' if all_deps.key?('jest')
368
+ end
369
+
370
+ stack
371
+ end
372
+
373
+ def detect_python_stack
374
+ stack = ['Python']
375
+
376
+ requirements_files = ['requirements.txt', 'pyproject.toml', 'Pipfile']
377
+ req_content = ''
378
+
379
+ requirements_files.each do |file|
380
+ if File.exist?(file)
381
+ req_content += File.read(file).downcase
382
+ end
383
+ end
384
+
385
+ stack << 'Django' if req_content.include?('django')
386
+ stack << 'Flask' if req_content.include?('flask')
387
+ stack << 'FastAPI' if req_content.include?('fastapi')
388
+ stack << 'SQLAlchemy' if req_content.include?('sqlalchemy')
389
+ stack << 'PostgreSQL' if req_content.include?('psycopg')
390
+ stack << 'Redis' if req_content.include?('redis')
391
+
392
+ stack
393
+ end
394
+
395
+ def suggest_commands(project_type)
396
+ commands = {}
397
+
398
+ case project_type
399
+ when 'rails'
400
+ commands = {
401
+ 'test' => 'bundle exec rspec',
402
+ 'lint' => 'bundle exec rubocop',
403
+ 'migrate' => 'bundle exec rails db:migrate',
404
+ 'console' => 'bundle exec rails console'
405
+ }
406
+ when 'node'
407
+ package_json = File.exist?('package.json') ? JSON.parse(File.read('package.json')) : {}
408
+ scripts = package_json['scripts'] || {}
409
+
410
+ commands['test'] = scripts['test'] || 'npm test'
411
+ commands['build'] = scripts['build'] || 'npm run build'
412
+ commands['start'] = scripts['start'] || 'npm start'
413
+ commands['lint'] = scripts['lint'] || 'npm run lint'
414
+ when 'python'
415
+ commands = {
416
+ 'test' => 'pytest',
417
+ 'lint' => 'flake8 .',
418
+ 'format' => 'black .',
419
+ 'install' => 'pip install -r requirements.txt'
420
+ }
421
+ end
422
+
423
+ commands
424
+ end
425
+
426
+ def suggest_orchestration_settings
427
+ concurrency = suggest_concurrency_settings
428
+
429
+ {
430
+ 'max_concurrent_agents' => concurrency[:max_concurrent_agents],
431
+ 'monitor_interval' => concurrency[:monitor_interval],
432
+ 'timeout_per_agent' => 300,
433
+ 'auto_cleanup' => true
434
+ }
435
+ end
436
+
437
+ def suggest_mcp_tools(project_type)
438
+ tools = {
439
+ 'desktop_commander' => false,
440
+ 'gemini_cli' => true
441
+ }
442
+
443
+ # Enable specific tools based on project type
444
+ case project_type
445
+ when 'rails', 'node', 'python'
446
+ tools['gemini_cli'] = true # Good for large codebases
447
+ when 'docker'
448
+ tools['desktop_commander'] = true # May need system operations
449
+ end
450
+
451
+ tools
452
+ end
453
+
454
+ def stale_worktrees_detected?
455
+ find_stale_worktrees.count > 2
456
+ end
457
+
458
+ def find_stale_worktrees
459
+ return [] unless Dir.exist?('.git')
460
+
461
+ begin
462
+ worktree_output = `git worktree list 2>/dev/null`
463
+ stale_worktrees = []
464
+
465
+ worktree_output.lines.each do |line|
466
+ next unless line.include?('swarm/')
467
+
468
+ parts = line.split
469
+ path = parts[0]
470
+
471
+ if Dir.exist?(path)
472
+ # Check if worktree is old (no activity in 24 hours)
473
+ last_modified = Dir.glob(File.join(path, '**/*')).map { |f| File.mtime(f) rescue Time.now }.max
474
+ if last_modified && (Time.now - last_modified) > 86400 # 24 hours
475
+ stale_worktrees << path
476
+ end
477
+ else
478
+ stale_worktrees << path # Broken worktree reference
479
+ end
480
+ end
481
+
482
+ stale_worktrees
483
+ rescue StandardError
484
+ []
485
+ end
486
+ end
487
+
488
+ def pending_agent_messages?
489
+ return false unless defined?(AgentCommunicator)
490
+
491
+ AgentCommunicator.instance.pending_messages.any?
492
+ end
493
+
494
+ def tests_need_running?
495
+ return false unless @project_context[:git]
496
+
497
+ # Check if there are unstaged changes in source files
498
+ status = `git status --porcelain`.lines
499
+ source_files_changed = status.any? do |line|
500
+ file = line[3..-1]&.strip
501
+ file&.match?(/\.(rb|js|ts|py|java|go|rs)$/)
502
+ end
503
+
504
+ source_files_changed
505
+ end
506
+
507
+ def determine_test_command
508
+ project_type = detect_project_type
509
+
510
+ case project_type
511
+ when 'rails' then 'bundle exec rspec'
512
+ when 'node' then 'npm test'
513
+ when 'python' then 'pytest'
514
+ else 'echo "No test command configured"'
515
+ end
516
+ end
517
+
518
+ def time_based_suggestions
519
+ suggestions = []
520
+ current_hour = Time.now.hour
521
+
522
+ # Morning suggestions (8-10 AM)
523
+ if current_hour.between?(8, 10)
524
+ suggestions << {
525
+ action: 'status',
526
+ command: 'enhance-swarm status',
527
+ reason: 'Morning status check',
528
+ priority: :low
529
+ }
530
+ end
531
+
532
+ # End of day suggestions (5-7 PM)
533
+ if current_hour.between?(17, 19)
534
+ suggestions << {
535
+ action: 'cleanup',
536
+ command: 'enhance-swarm cleanup --all',
537
+ reason: 'End of day cleanup',
538
+ priority: :low
539
+ }
540
+ end
541
+
542
+ suggestions
543
+ end
544
+
545
+ def priority_weight(priority)
546
+ case priority
547
+ when :critical then 4
548
+ when :high then 3
549
+ when :medium then 2
550
+ when :low then 1
551
+ else 0
552
+ end
553
+ end
554
+
555
+ def find_old_communication_files
556
+ comm_dir = '.enhance_swarm/communication'
557
+ return [] unless Dir.exist?(comm_dir)
558
+
559
+ cutoff = Time.now - (7 * 24 * 60 * 60) # 7 days ago
560
+
561
+ Dir.glob(File.join(comm_dir, '*.json')).select do |file|
562
+ File.mtime(file) < cutoff
563
+ end
564
+ end
565
+
566
+ def notification_history_size
567
+ return 0 unless defined?(NotificationManager)
568
+
569
+ NotificationManager.instance.recent_notifications(1000).count
570
+ end
571
+
572
+ def frequent_commands
573
+ return [] unless @user_patterns['actions']
574
+
575
+ @user_patterns['actions']
576
+ .select { |_, data| data['count'] > 5 }
577
+ .sort_by { |_, data| -data['count'] }
578
+ .first(5)
579
+ .map { |action, _| action }
580
+ end
581
+
582
+ def record_success_pattern(operation, attempt)
583
+ @user_patterns['retry_patterns'] ||= {}
584
+ @user_patterns['retry_patterns'][operation.to_s] ||= {
585
+ 'total_attempts' => 0,
586
+ 'success_attempts' => 0,
587
+ 'avg_attempts_to_success' => 1.0
588
+ }
589
+
590
+ pattern = @user_patterns['retry_patterns'][operation.to_s]
591
+ pattern['total_attempts'] += attempt
592
+ pattern['success_attempts'] += 1
593
+ pattern['avg_attempts_to_success'] = pattern['total_attempts'].to_f / pattern['success_attempts']
594
+
595
+ save_user_patterns
596
+ end
597
+
598
+ def record_failure_pattern(operation, attempt, error)
599
+ @user_patterns['failure_patterns'] ||= {}
600
+ @user_patterns['failure_patterns'][operation.to_s] ||= {
601
+ 'count' => 0,
602
+ 'last_error' => nil,
603
+ 'common_errors' => {}
604
+ }
605
+
606
+ pattern = @user_patterns['failure_patterns'][operation.to_s]
607
+ pattern['count'] += 1
608
+ pattern['last_error'] = error.message
609
+ pattern['common_errors'][error.class.name] ||= 0
610
+ pattern['common_errors'][error.class.name] += 1
611
+
612
+ save_user_patterns
613
+ end
614
+
615
+ def git_status_summary
616
+ return {} unless Dir.exist?('.git')
617
+
618
+ begin
619
+ status_output = `git status --porcelain`
620
+ {
621
+ modified_files: status_output.lines.count { |line| line.start_with?(' M', 'M ') },
622
+ untracked_files: status_output.lines.count { |line| line.start_with?('??') },
623
+ staged_files: status_output.lines.count { |line| line.start_with?('A ', 'M ') }
624
+ }
625
+ rescue StandardError
626
+ {}
627
+ end
628
+ end
629
+
630
+ def recent_commit_count
631
+ return 0 unless Dir.exist?('.git')
632
+
633
+ begin
634
+ `git log --oneline --since="1 week ago"`.lines.count
635
+ rescue StandardError
636
+ 0
637
+ end
638
+ end
639
+
640
+ def detect_cpu_cores
641
+ case RUBY_PLATFORM
642
+ when /linux/
643
+ `nproc`.to_i
644
+ when /darwin/
645
+ `sysctl -n hw.ncpu`.to_i
646
+ else
647
+ 4 # Fallback
648
+ end
649
+ rescue StandardError
650
+ 4
651
+ end
652
+
653
+ def detect_available_memory
654
+ case RUBY_PLATFORM
655
+ when /linux/
656
+ # Parse /proc/meminfo
657
+ meminfo = File.read('/proc/meminfo')
658
+ available_kb = meminfo[/MemAvailable:\s*(\d+)/, 1].to_i
659
+ available_kb / 1024.0 / 1024.0 # Convert to GB
660
+ when /darwin/
661
+ # Use vm_stat
662
+ vm_stat = `vm_stat`
663
+ page_size = 4096
664
+ pages_free = vm_stat[/Pages free:\s*(\d+)/, 1].to_i
665
+ (pages_free * page_size) / 1024.0 / 1024.0 / 1024.0 # Convert to GB
666
+ else
667
+ 8.0 # Fallback
668
+ end
669
+ rescue StandardError
670
+ 8.0
671
+ end
672
+
673
+ # Class methods for singleton access
674
+ class << self
675
+ def instance
676
+ @instance ||= new
677
+ end
678
+
679
+ def suggest_role_for_task(*args)
680
+ instance.suggest_role_for_task(*args)
681
+ end
682
+
683
+ def suggest_configuration
684
+ instance.suggest_configuration
685
+ end
686
+
687
+ def auto_retry_with_backoff(*args, &block)
688
+ instance.auto_retry_with_backoff(*args, &block)
689
+ end
690
+
691
+ def suggest_next_actions(*args)
692
+ instance.suggest_next_actions(*args)
693
+ end
694
+
695
+ def auto_cleanup_if_needed
696
+ instance.auto_cleanup_if_needed
697
+ end
698
+
699
+ def learn_from_action(*args)
700
+ instance.learn_from_action(*args)
701
+ end
702
+
703
+ def get_suggestions(context = {})
704
+ instance.suggest_next_actions(context)
705
+ end
706
+ end
707
+ end
708
+ end