aidp 0.32.0 → 0.33.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/analyze/feature_analyzer.rb +322 -320
  3. data/lib/aidp/auto_update/coordinator.rb +97 -7
  4. data/lib/aidp/auto_update.rb +0 -12
  5. data/lib/aidp/cli/devcontainer_commands.rb +0 -5
  6. data/lib/aidp/cli.rb +2 -1
  7. data/lib/aidp/comment_consolidator.rb +78 -0
  8. data/lib/aidp/concurrency.rb +0 -3
  9. data/lib/aidp/config.rb +0 -1
  10. data/lib/aidp/config_paths.rb +71 -0
  11. data/lib/aidp/execute/work_loop_runner.rb +324 -15
  12. data/lib/aidp/harness/ai_filter_factory.rb +285 -0
  13. data/lib/aidp/harness/config_schema.rb +97 -1
  14. data/lib/aidp/harness/config_validator.rb +1 -1
  15. data/lib/aidp/harness/configuration.rb +61 -5
  16. data/lib/aidp/harness/filter_definition.rb +212 -0
  17. data/lib/aidp/harness/generated_filter_strategy.rb +197 -0
  18. data/lib/aidp/harness/output_filter.rb +50 -25
  19. data/lib/aidp/harness/output_filter_config.rb +129 -0
  20. data/lib/aidp/harness/provider_manager.rb +90 -2
  21. data/lib/aidp/harness/runner.rb +0 -11
  22. data/lib/aidp/harness/test_runner.rb +179 -41
  23. data/lib/aidp/harness/thinking_depth_manager.rb +16 -0
  24. data/lib/aidp/harness/ui/navigation/submenu.rb +0 -2
  25. data/lib/aidp/loader.rb +195 -0
  26. data/lib/aidp/metadata/compiler.rb +29 -17
  27. data/lib/aidp/metadata/query.rb +1 -1
  28. data/lib/aidp/metadata/scanner.rb +8 -1
  29. data/lib/aidp/metadata/tool_metadata.rb +13 -13
  30. data/lib/aidp/metadata/validator.rb +10 -0
  31. data/lib/aidp/metadata.rb +16 -0
  32. data/lib/aidp/pr_worktree_manager.rb +2 -2
  33. data/lib/aidp/provider_manager.rb +1 -7
  34. data/lib/aidp/setup/wizard.rb +279 -9
  35. data/lib/aidp/skills.rb +0 -5
  36. data/lib/aidp/storage/csv_storage.rb +3 -0
  37. data/lib/aidp/style_guide/selector.rb +360 -0
  38. data/lib/aidp/tooling_detector.rb +283 -16
  39. data/lib/aidp/version.rb +1 -1
  40. data/lib/aidp/watch/change_request_processor.rb +152 -14
  41. data/lib/aidp/watch/repository_client.rb +41 -0
  42. data/lib/aidp/watch/runner.rb +29 -18
  43. data/lib/aidp/watch.rb +5 -7
  44. data/lib/aidp/workstream_cleanup.rb +0 -2
  45. data/lib/aidp/workstream_executor.rb +0 -4
  46. data/lib/aidp/worktree.rb +0 -1
  47. data/lib/aidp.rb +21 -106
  48. metadata +72 -36
  49. data/lib/aidp/config/paths.rb +0 -131
@@ -6,15 +6,7 @@ require "yaml"
6
6
  require "time"
7
7
  require "fileutils"
8
8
  require "json"
9
-
10
- require_relative "../util"
11
- require_relative "../config/paths"
12
- require_relative "../harness/capability_registry"
13
- require_relative "provider_registry"
14
- require_relative "devcontainer/parser"
15
- require_relative "devcontainer/generator"
16
- require_relative "devcontainer/port_manager"
17
- require_relative "devcontainer/backup_manager"
9
+ require "ostruct"
18
10
 
19
11
  module Aidp
20
12
  module Setup
@@ -57,6 +49,7 @@ module Aidp
57
49
  configure_artifacts
58
50
  configure_nfrs
59
51
  configure_logging
52
+ configure_auto_update
60
53
  configure_modes
61
54
  configure_devcontainer
62
55
 
@@ -493,6 +486,7 @@ module Aidp
493
486
  prompt.say("-" * 40)
494
487
 
495
488
  configure_work_loop_limits
489
+ configure_output_filtering
496
490
  configure_test_commands
497
491
  configure_linting
498
492
  configure_watch_patterns
@@ -515,6 +509,216 @@ module Aidp
515
509
  set([:work_loop, :max_iterations], max_iterations)
516
510
  end
517
511
 
512
+ def configure_output_filtering
513
+ prompt.say("\nšŸ” Output filtering configuration")
514
+ prompt.say(" Reduces token consumption by filtering test/lint output")
515
+ existing = get([:work_loop, :output_filtering]) || {}
516
+
517
+ return unless prompt.yes?("Configure output filtering?", default: false)
518
+
519
+ enabled = prompt.yes?(
520
+ "Enable output filtering?",
521
+ default: existing.fetch(:enabled, true)
522
+ )
523
+
524
+ unless enabled
525
+ set([:work_loop, :output_filtering], {enabled: false})
526
+ return
527
+ end
528
+
529
+ # Test output mode
530
+ test_mode_choices = [
531
+ ["Full (no filtering)", "full"],
532
+ ["Failures only (recommended for iterations)", "failures_only"],
533
+ ["Minimal (summary + locations only)", "minimal"]
534
+ ]
535
+ test_mode_default = existing[:test_mode] || "full"
536
+ test_mode_default_label = test_mode_choices.find { |label, value| value == test_mode_default }&.first
537
+
538
+ test_mode = prompt.select("Test output mode:", default: test_mode_default_label) do |menu|
539
+ test_mode_choices.each { |label, value| menu.choice label, value }
540
+ end
541
+
542
+ # Lint output mode
543
+ lint_mode_choices = test_mode_choices
544
+ lint_mode_default = existing[:lint_mode] || "full"
545
+ lint_mode_default_label = lint_mode_choices.find { |label, value| value == lint_mode_default }&.first
546
+
547
+ lint_mode = prompt.select("Lint output mode:", default: lint_mode_default_label) do |menu|
548
+ lint_mode_choices.each { |label, value| menu.choice label, value }
549
+ end
550
+
551
+ # Max output lines
552
+ test_max_lines = ask_with_default(
553
+ "Maximum test output lines",
554
+ (existing[:test_max_lines] || 500).to_s
555
+ ) { |value| value.to_i }
556
+
557
+ lint_max_lines = ask_with_default(
558
+ "Maximum lint output lines",
559
+ (existing[:lint_max_lines] || 300).to_s
560
+ ) { |value| value.to_i }
561
+
562
+ # Context configuration
563
+ include_context = prompt.yes?(
564
+ "Include context lines around failures?",
565
+ default: existing.fetch(:include_context, true)
566
+ )
567
+
568
+ context_lines = if include_context
569
+ ask_with_default(
570
+ "Number of context lines",
571
+ (existing[:context_lines] || 3).to_s
572
+ ) { |value| value.to_i }
573
+ else
574
+ 0
575
+ end
576
+
577
+ set([:work_loop, :output_filtering], {
578
+ enabled: true,
579
+ test_mode: test_mode,
580
+ lint_mode: lint_mode,
581
+ test_max_lines: test_max_lines,
582
+ lint_max_lines: lint_max_lines,
583
+ include_context: include_context,
584
+ context_lines: context_lines
585
+ })
586
+
587
+ prompt.ok("āœ… Output filtering configured")
588
+
589
+ # Offer to generate AI-powered filter definitions
590
+ configure_filter_generation
591
+ end
592
+
593
+ def configure_filter_generation
594
+ prompt.say("\nšŸ¤– AI-Generated Filter Definitions")
595
+ prompt.say(" Generate custom filters for your test/lint tools (one-time AI call)")
596
+
597
+ return unless prompt.yes?("Generate filter definitions for your tools?", default: false)
598
+
599
+ # Collect configured commands
600
+ commands_to_filter = collect_commands_for_filtering
601
+ if commands_to_filter.empty?
602
+ prompt.warn("āš ļø No test or lint commands configured. Configure them first.")
603
+ return
604
+ end
605
+
606
+ prompt.say("\nšŸ“ Commands detected:")
607
+ commands_to_filter.each do |cmd|
608
+ prompt.say(" • #{cmd[:name]}: #{cmd[:command]}")
609
+ end
610
+
611
+ # Check if AI provider is configured
612
+ primary_provider = get([:harness, :default_provider])
613
+ unless primary_provider
614
+ prompt.warn("āš ļø No AI provider configured. Configure providers first.")
615
+ return
616
+ end
617
+
618
+ # Let user select which commands to generate filters for
619
+ prompt.say("\nšŸ’” Use ↑/↓ arrows to navigate, SPACE to select/deselect, ENTER to confirm")
620
+ selected = prompt.multi_select("Select commands to generate filters for:") do |menu|
621
+ commands_to_filter.each do |cmd|
622
+ menu.choice "#{cmd[:name]} (#{cmd[:command]})", cmd
623
+ end
624
+ end
625
+
626
+ return if selected.empty?
627
+
628
+ # Generate filter definitions
629
+ filter_definitions = {}
630
+ factory = create_filter_factory
631
+
632
+ selected.each do |cmd|
633
+ prompt.say("\nā³ Generating filter for #{cmd[:name]}...")
634
+ Aidp.log_info("setup_wizard", "generating_filter_definition",
635
+ tool_name: cmd[:name], command: cmd[:command])
636
+
637
+ begin
638
+ definition = factory.generate_from_command(
639
+ tool_command: cmd[:command],
640
+ project_dir: project_dir,
641
+ tier: "mini"
642
+ )
643
+
644
+ filter_definitions[cmd[:key]] = definition.to_h
645
+ prompt.ok(" āœ… Generated filter for #{cmd[:name]}")
646
+ Aidp.log_info("setup_wizard", "filter_definition_generated",
647
+ tool_name: cmd[:name],
648
+ pattern_count: definition.summary_patterns.size)
649
+ rescue => e
650
+ prompt.warn(" āš ļø Failed to generate filter for #{cmd[:name]}: #{e.message}")
651
+ Aidp.log_error("setup_wizard", "filter_generation_failed",
652
+ tool_name: cmd[:name], error: e.message)
653
+ end
654
+ end
655
+
656
+ if filter_definitions.any?
657
+ set([:work_loop, :output_filtering, :filter_definitions], filter_definitions)
658
+ prompt.ok("\nāœ… Generated #{filter_definitions.size} filter definition(s)")
659
+ prompt.say(" These filters will be applied deterministically (no AI calls at runtime)")
660
+ end
661
+ end
662
+
663
+ def collect_commands_for_filtering
664
+ commands = []
665
+
666
+ # Test commands
667
+ test_config = get([:work_loop, :test]) || {}
668
+ if test_config[:unit] && !test_config[:unit].start_with?("echo")
669
+ commands << {
670
+ key: "unit_test",
671
+ name: "Unit Tests",
672
+ command: test_config[:unit],
673
+ type: :test
674
+ }
675
+ end
676
+ if test_config[:integration] && !test_config[:integration].to_s.empty? && !test_config[:integration].start_with?("echo")
677
+ commands << {
678
+ key: "integration_test",
679
+ name: "Integration Tests",
680
+ command: test_config[:integration],
681
+ type: :test
682
+ }
683
+ end
684
+ if test_config[:e2e] && !test_config[:e2e].to_s.empty? && !test_config[:e2e].start_with?("echo")
685
+ commands << {
686
+ key: "e2e_test",
687
+ name: "E2E Tests",
688
+ command: test_config[:e2e],
689
+ type: :test
690
+ }
691
+ end
692
+
693
+ # Lint commands
694
+ lint_config = get([:work_loop, :lint]) || {}
695
+ if lint_config[:command] && !lint_config[:command].start_with?("echo")
696
+ commands << {
697
+ key: "lint",
698
+ name: "Linter",
699
+ command: lint_config[:command],
700
+ type: :lint
701
+ }
702
+ end
703
+
704
+ commands
705
+ end
706
+
707
+ def create_filter_factory
708
+ # Build a minimal configuration for the factory
709
+ config = build_harness_config_for_factory
710
+ Aidp::Harness::AIFilterFactory.new(config)
711
+ end
712
+
713
+ def build_harness_config_for_factory
714
+ # Create a minimal config object that the factory needs
715
+ OpenStruct.new(
716
+ default_provider: get([:harness, :default_provider]),
717
+ providers: get([:providers]) || {},
718
+ thinking_tiers: get([:thinking, :tiers])
719
+ )
720
+ end
721
+
518
722
  def configure_test_commands
519
723
  existing = get([:work_loop, :test]) || {}
520
724
 
@@ -1007,6 +1211,72 @@ module Aidp
1007
1211
  })
1008
1212
  end
1009
1213
 
1214
+ def configure_auto_update
1215
+ prompt.say("\nā™»ļø Auto-update configuration")
1216
+ prompt.say("-" * 40)
1217
+
1218
+ existing = get([:auto_update]) || {}
1219
+ enabled = prompt.yes?(
1220
+ "Enable auto-update for watch mode?",
1221
+ default: existing.fetch(:enabled, false)
1222
+ )
1223
+
1224
+ if enabled
1225
+ policy_choices = [
1226
+ ["Off (manual updates)", "off"],
1227
+ ["Patch updates", "patch"],
1228
+ ["Minor updates", "minor"],
1229
+ ["Major updates", "major"],
1230
+ ["Exact version only", "exact"]
1231
+ ]
1232
+ policy_default = existing[:policy] || "minor"
1233
+ policy_default_label = policy_choices.find { |label, value| value == policy_default }&.first
1234
+ policy = prompt.select("Auto-update policy:", default: policy_default_label) do |menu|
1235
+ policy_choices.each { |label, value| menu.choice(label, value) }
1236
+ end
1237
+
1238
+ allow_prerelease = prompt.yes?(
1239
+ "Allow prerelease versions?",
1240
+ default: existing.fetch(:allow_prerelease, false)
1241
+ )
1242
+
1243
+ interval_default = (existing[:check_interval_seconds] || 3600).to_s
1244
+ interval = ask_with_default(
1245
+ "Check interval (seconds, 300-86400)",
1246
+ interval_default
1247
+ ) { |value| value.to_i }
1248
+
1249
+ supervisor_choices = [
1250
+ ["None (manual restart)", "none"],
1251
+ ["supervisord (recommended)", "supervisord"],
1252
+ ["s6", "s6"],
1253
+ ["runit", "runit"]
1254
+ ]
1255
+ supervisor_default = existing[:supervisor] || "none"
1256
+ supervisor_default_label = supervisor_choices.find { |label, value| value == supervisor_default }&.first
1257
+
1258
+ supervisor = prompt.select("Update supervisor:", default: supervisor_default_label) do |menu|
1259
+ supervisor_choices.each { |label, value| menu.choice(label, value) }
1260
+ end
1261
+
1262
+ max_failures = ask_with_default(
1263
+ "Max consecutive update failures before backoff",
1264
+ (existing[:max_consecutive_failures] || 3).to_s
1265
+ ) { |value| value.to_i }
1266
+
1267
+ set([:auto_update], {
1268
+ enabled: true,
1269
+ policy: policy,
1270
+ allow_prerelease: allow_prerelease,
1271
+ check_interval_seconds: interval,
1272
+ supervisor: supervisor,
1273
+ max_consecutive_failures: max_failures
1274
+ })
1275
+ else
1276
+ set([:auto_update], {enabled: false, policy: "off"})
1277
+ end
1278
+ end
1279
+
1010
1280
  def configure_modes
1011
1281
  prompt.say("\nšŸš€ Operational modes")
1012
1282
  prompt.say("-" * 40)
data/lib/aidp/skills.rb CHANGED
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "skills/skill"
4
- require_relative "skills/loader"
5
- require_relative "skills/registry"
6
- require_relative "skills/composer"
7
-
8
3
  module Aidp
9
4
  # Skills subsystem for managing agent personas and capabilities
10
5
  #
@@ -237,5 +237,8 @@ module Aidp
237
237
  Kernel.warn(message)
238
238
  end
239
239
  end
240
+
241
+ # Zeitwerk inflection compatibility
242
+ CSVStorage = CsvStorage
240
243
  end
241
244
  end