aidp 0.28.0 → 0.29.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.
@@ -37,6 +37,16 @@ module Aidp
37
37
  TIMEOUT_REFACTORING_RECOMMENDATIONS = 600 # 10 minutes - refactoring
38
38
  TIMEOUT_IMPLEMENTATION = 900 # 15 minutes - implementation (write files, run tests, fix issues)
39
39
 
40
+ # Tier-based timeout multipliers (applied on top of base timeouts)
41
+ # Higher tiers need more time for deeper reasoning
42
+ TIER_TIMEOUT_MULTIPLIERS = {
43
+ "mini" => 1.0, # 5 minutes default (300s base)
44
+ "standard" => 2.0, # 10 minutes default (600s base)
45
+ "thinking" => 6.0, # 30 minutes default (1800s base)
46
+ "pro" => 6.0, # 30 minutes default (1800s base)
47
+ "max" => 12.0 # 60 minutes default (3600s base)
48
+ }.freeze
49
+
40
50
  attr_reader :activity_state, :last_activity_time, :start_time, :step_name, :model
41
51
 
42
52
  def initialize(output: nil, prompt: TTY::Prompt.new)
@@ -78,10 +88,12 @@ module Aidp
78
88
  # Configure the provider with options
79
89
  # @param config [Hash] Configuration options, may include :model
80
90
  def configure(config)
81
- @model = config[:model] if config[:model]
91
+ if config[:model]
92
+ @model = resolve_model_name(config[:model].to_s)
93
+ end
82
94
  end
83
95
 
84
- def send_message(prompt:, session: nil)
96
+ def send_message(prompt:, session: nil, options: {})
85
97
  raise NotImplementedError, "#{self.class} must implement #send_message"
86
98
  end
87
99
 
@@ -482,11 +494,12 @@ module Aidp
482
494
  # Priority order:
483
495
  # 1. Quick mode (for testing)
484
496
  # 2. Provider-specific environment variable override
485
- # 3. Adaptive timeout based on step type
486
- # 4. Default timeout
497
+ # 3. Adaptive timeout based on step type and thinking tier
498
+ # 4. Default timeout (with tier multiplier)
487
499
  #
488
500
  # Override provider_env_var to customize the environment variable name
489
- def calculate_timeout
501
+ # @param options [Hash] Options hash that may include :tier
502
+ def calculate_timeout(options = {})
490
503
  if ENV["AIDP_QUICK_MODE"]
491
504
  display_message("⚔ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
492
505
  return TIMEOUT_QUICK_MODE
@@ -495,47 +508,104 @@ module Aidp
495
508
  provider_env_var = "AIDP_#{name.upcase}_TIMEOUT"
496
509
  return ENV[provider_env_var].to_i if ENV[provider_env_var]
497
510
 
498
- step_timeout = adaptive_timeout
511
+ tier = options[:tier]&.to_s
512
+ step_timeout = adaptive_timeout(tier)
499
513
  if step_timeout
500
- display_message("🧠 Using adaptive timeout: #{step_timeout} seconds", type: :info)
514
+ tier_label = tier ? " (tier: #{tier})" : ""
515
+ display_message("🧠 Using adaptive timeout: #{step_timeout} seconds#{tier_label}", type: :info)
501
516
  return step_timeout
502
517
  end
503
518
 
504
- # Default timeout
505
- display_message("šŸ“‹ Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
506
- TIMEOUT_DEFAULT
519
+ # Default timeout with tier multiplier
520
+ base_timeout = TIMEOUT_DEFAULT
521
+ final_timeout = apply_tier_multiplier(base_timeout, tier)
522
+ tier_label = tier ? " (tier: #{tier})" : ""
523
+ display_message("šŸ“‹ Using default timeout: #{final_timeout / 60} minutes#{tier_label}", type: :info)
524
+ final_timeout
507
525
  end
508
526
 
509
- # Get adaptive timeout based on step type
527
+ # Get adaptive timeout based on step type and thinking tier
510
528
  #
511
529
  # This method returns different timeout values based on the type of operation
512
- # being performed, as indicated by the AIDP_CURRENT_STEP environment variable.
530
+ # being performed, as indicated by the AIDP_CURRENT_STEP environment variable,
531
+ # and applies a multiplier based on the thinking tier (mini/standard/thinking/pro/max).
513
532
  # Returns nil for unknown steps to allow calculate_timeout to use the default.
514
- def adaptive_timeout
515
- @adaptive_timeout ||= begin
516
- # Timeout recommendations based on step type patterns
517
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
518
-
519
- case step_name
520
- when /REPOSITORY_ANALYSIS/
521
- TIMEOUT_REPOSITORY_ANALYSIS
522
- when /ARCHITECTURE_ANALYSIS/
523
- TIMEOUT_ARCHITECTURE_ANALYSIS
524
- when /TEST_ANALYSIS/
525
- TIMEOUT_TEST_ANALYSIS
526
- when /FUNCTIONALITY_ANALYSIS/
527
- TIMEOUT_FUNCTIONALITY_ANALYSIS
528
- when /DOCUMENTATION_ANALYSIS/
529
- TIMEOUT_DOCUMENTATION_ANALYSIS
530
- when /STATIC_ANALYSIS/
531
- TIMEOUT_STATIC_ANALYSIS
532
- when /REFACTORING_RECOMMENDATIONS/
533
- TIMEOUT_REFACTORING_RECOMMENDATIONS
534
- when /IMPLEMENTATION/
535
- TIMEOUT_IMPLEMENTATION
533
+ #
534
+ # @param tier [String, nil] The thinking tier (mini, standard, thinking, pro, max)
535
+ def adaptive_timeout(tier = nil)
536
+ # Don't cache - tier may change between calls
537
+ step_name = ENV["AIDP_CURRENT_STEP"] || ""
538
+
539
+ base_timeout = case step_name
540
+ when /REPOSITORY_ANALYSIS/
541
+ TIMEOUT_REPOSITORY_ANALYSIS
542
+ when /ARCHITECTURE_ANALYSIS/
543
+ TIMEOUT_ARCHITECTURE_ANALYSIS
544
+ when /TEST_ANALYSIS/
545
+ TIMEOUT_TEST_ANALYSIS
546
+ when /FUNCTIONALITY_ANALYSIS/
547
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
548
+ when /DOCUMENTATION_ANALYSIS/
549
+ TIMEOUT_DOCUMENTATION_ANALYSIS
550
+ when /STATIC_ANALYSIS/
551
+ TIMEOUT_STATIC_ANALYSIS
552
+ when /REFACTORING_RECOMMENDATIONS/
553
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
554
+ when /IMPLEMENTATION/
555
+ TIMEOUT_IMPLEMENTATION
556
+ else
557
+ nil # Return nil for unknown steps
558
+ end
559
+
560
+ return nil unless base_timeout
561
+
562
+ apply_tier_multiplier(base_timeout, tier)
563
+ end
564
+
565
+ # Apply tier-based multiplier to a base timeout
566
+ #
567
+ # @param base_timeout [Integer] The base timeout in seconds
568
+ # @param tier [String, nil] The thinking tier (mini, standard, thinking, pro, max)
569
+ # @return [Integer] The adjusted timeout with tier multiplier applied
570
+ def apply_tier_multiplier(base_timeout, tier)
571
+ return base_timeout unless tier
572
+
573
+ multiplier = TIER_TIMEOUT_MULTIPLIERS[tier.to_s] || 1.0
574
+ (base_timeout * multiplier).to_i
575
+ end
576
+
577
+ # Resolve a model name using the RubyLLM registry
578
+ #
579
+ # Attempts to resolve a model family name (e.g., "claude-3-5-haiku") to a
580
+ # versioned model name (e.g., "claude-3-5-haiku-20241022") using the RubyLLM
581
+ # registry. Falls back to using the name as-is if resolution fails.
582
+ #
583
+ # @param model_name [String] The model family name or versioned name
584
+ # @return [String] The resolved model name (versioned if found, original if not)
585
+ def resolve_model_name(model_name)
586
+ require_relative "../harness/ruby_llm_registry" unless defined?(Aidp::Harness::RubyLLMRegistry)
587
+
588
+ begin
589
+ registry = Aidp::Harness::RubyLLMRegistry.new
590
+ resolved = registry.resolve_model(model_name, provider: name)
591
+
592
+ if resolved
593
+ Aidp.log_debug(name, "Resolved model using registry",
594
+ requested: model_name,
595
+ resolved: resolved)
596
+ resolved
536
597
  else
537
- nil # Return nil for unknown steps
598
+ # Fall back to using the name as-is
599
+ Aidp.log_warn(name, "Model not found in registry, using as-is",
600
+ model: model_name)
601
+ model_name
538
602
  end
603
+ rescue => e
604
+ # If registry fails, fall back to using the name as-is
605
+ Aidp.log_error(name, "Registry lookup failed, using model name as-is",
606
+ model: model_name,
607
+ error: e.message)
608
+ model_name
539
609
  end
540
610
  end
541
611
 
@@ -76,7 +76,7 @@ module Aidp
76
76
  end
77
77
  end
78
78
 
79
- def send_message(prompt:, session: nil)
79
+ def send_message(prompt:, session: nil, options: {})
80
80
  raise "codex CLI not available" unless self.class.available?
81
81
 
82
82
  # Smart timeout calculation (store prompt length for adaptive logic)
@@ -117,7 +117,7 @@ module Aidp
117
117
  fetch_mcp_servers_cli || fetch_mcp_servers_config
118
118
  end
119
119
 
120
- def send_message(prompt:, session: nil)
120
+ def send_message(prompt:, session: nil, options: {})
121
121
  raise "cursor-agent not available" unless self.class.available?
122
122
 
123
123
  # Smart timeout calculation
@@ -76,7 +76,7 @@ module Aidp
76
76
  "Google Gemini"
77
77
  end
78
78
 
79
- def send_message(prompt:, session: nil)
79
+ def send_message(prompt:, session: nil, options: {})
80
80
  raise "gemini CLI not available" unless self.class.available?
81
81
 
82
82
  # Smart timeout calculation
@@ -51,7 +51,7 @@ module Aidp
51
51
  end
52
52
  end
53
53
 
54
- def send_message(prompt:, session: nil)
54
+ def send_message(prompt:, session: nil, options: {})
55
55
  raise "copilot CLI not available" unless self.class.available?
56
56
 
57
57
  # Smart timeout calculation
@@ -56,7 +56,7 @@ module Aidp
56
56
  "Kilocode"
57
57
  end
58
58
 
59
- def send_message(prompt:, session: nil)
59
+ def send_message(prompt:, session: nil, options: {})
60
60
  raise "kilocode not available" unless self.class.available?
61
61
 
62
62
  # Smart timeout calculation
@@ -56,7 +56,7 @@ module Aidp
56
56
  "OpenCode"
57
57
  end
58
58
 
59
- def send_message(prompt:, session: nil)
59
+ def send_message(prompt:, session: nil, options: {})
60
60
  raise "opencode not available" unless self.class.available?
61
61
 
62
62
  # Smart timeout calculation
@@ -59,9 +59,6 @@ module Aidp
59
59
  configure_modes
60
60
  configure_devcontainer
61
61
 
62
- # Finalize any background model discovery
63
- finalize_background_discovery
64
-
65
62
  yaml_content = generate_yaml
66
63
  display_preview(yaml_content)
67
64
  display_diff(yaml_content) if @existing_config.any?
@@ -355,110 +352,14 @@ module Aidp
355
352
  end
356
353
  end
357
354
 
358
- # Trigger background model discovery for a provider
359
- # Runs asynchronously and caches results without blocking the wizard
360
- def trigger_background_discovery(provider_name)
361
- return unless provider_available_for_discovery?(provider_name)
362
-
363
- # Store reference to display notification later
364
- @discovery_threads ||= []
365
-
366
- thread = Thread.new do
367
- discover_and_cache_models(provider_name)
368
- rescue => e
369
- Aidp.log_debug("setup_wizard", "background discovery failed",
370
- provider: provider_name, error: e.message)
371
- end
372
-
373
- @discovery_threads << {thread: thread, provider: provider_name}
374
- end
375
-
376
- # Check if provider CLI is available for discovery
377
- def provider_available_for_discovery?(provider_name)
378
- provider_class = get_provider_class(provider_name)
379
- return false unless provider_class
380
-
381
- provider_class.respond_to?(:available?) && provider_class.available?
382
- rescue => e
383
- Aidp.log_debug("setup_wizard", "provider availability check failed",
384
- provider: provider_name, error: e.message)
385
- false
386
- end
387
-
388
- # Perform model discovery and cache results
389
- def discover_and_cache_models(provider_name)
390
- require_relative "../harness/model_discovery_service"
391
-
392
- service = Aidp::Harness::ModelDiscoveryService.new
393
- models = service.discover_models(provider_name, use_cache: false)
394
-
395
- if models.any?
396
- Aidp.log_info("setup_wizard", "discovered models in background",
397
- provider: provider_name, count: models.size)
398
- end
399
-
400
- models
401
- rescue => e
402
- Aidp.log_debug("setup_wizard", "background discovery failed",
403
- provider: provider_name, error: e.message)
404
- []
405
- end
406
-
407
- # Get provider class for discovery
408
- def get_provider_class(provider_name)
409
- class_name = "Aidp::Providers::#{provider_name.capitalize}"
410
- Object.const_get(class_name)
411
- rescue NameError
412
- nil
413
- end
414
-
415
- # Wait for background discovery to complete and show notifications
416
- #
417
- # @param timeout [Numeric] Maximum seconds to wait per thread (default: 5)
418
- def finalize_background_discovery(timeout: 5)
419
- return unless @discovery_threads&.any?
420
-
421
- @discovery_threads.each do |entry|
422
- thread = entry[:thread]
423
- provider = entry[:provider]
424
-
425
- # Wait up to timeout seconds for discovery to complete
426
- thread.join(timeout)
427
-
428
- if thread.alive?
429
- Aidp.log_debug("setup_wizard", "discovery timeout, killing thread",
430
- provider: provider)
431
- # Kill thread to prevent hanging
432
- thread.kill
433
- thread.join(0.1) # Brief wait for cleanup
434
- else
435
- # Discovery completed - show notification
436
- begin
437
- require_relative "../harness/model_cache"
438
- cache = Aidp::Harness::ModelCache.new
439
- cached_models = cache.get_cached_models(provider)
440
-
441
- if cached_models&.any?
442
- prompt.say(" šŸ’¾ Discovered #{cached_models.size} model#{"s" unless cached_models.size == 1} for #{provider}")
443
- end
444
- rescue => e
445
- Aidp.log_debug("setup_wizard", "failed to check cached models",
446
- provider: provider, error: e.message)
447
- end
448
- end
449
- end
450
-
451
- @discovery_threads = []
452
- end
453
-
454
355
  def discover_models_for_providers(providers)
455
- require_relative "../harness/model_discovery_service"
356
+ require_relative "../harness/ruby_llm_registry"
456
357
 
457
- service = Aidp::Harness::ModelDiscoveryService.new
358
+ registry = Aidp::Harness::RubyLLMRegistry.new
458
359
  all_models = {}
459
360
 
460
361
  providers.each do |provider|
461
- models = service.discover_models(provider, use_cache: true)
362
+ models = discover_models_from_registry(provider, registry)
462
363
  all_models[provider] = models if models.any?
463
364
  rescue => e
464
365
  Aidp.log_debug("setup_wizard", "discovery failed", provider: provider, error: e.message)
@@ -468,6 +369,30 @@ module Aidp
468
369
  all_models
469
370
  end
470
371
 
372
+ # Discover models for a provider from RubyLLM registry
373
+ #
374
+ # @param provider [String] Provider name (e.g., "anthropic")
375
+ # @param registry [RubyLLMRegistry] Registry instance
376
+ # @return [Array<Hash>] Models with tier classification
377
+ def discover_models_from_registry(provider, registry)
378
+ model_ids = registry.models_for_provider(provider)
379
+
380
+ # Convert to model info hashes with tier classification
381
+ model_ids.map do |model_id|
382
+ info = registry.get_model_info(model_id)
383
+ {
384
+ name: model_id,
385
+ tier: info[:tier],
386
+ context_window: info[:context_window],
387
+ capabilities: info[:capabilities]
388
+ }
389
+ end
390
+ rescue => e
391
+ Aidp.log_error("setup_wizard", "failed to discover models from registry",
392
+ provider: provider, error: e.message)
393
+ []
394
+ end
395
+
471
396
  def display_discovered_models(discovered_models)
472
397
  discovered_models.each do |provider, models|
473
398
  prompt.say("\nāœ“ Found #{models.size} models for #{provider}:")
@@ -510,13 +435,19 @@ module Aidp
510
435
  def find_model_for_tier(models, target_tier)
511
436
  return nil unless models
512
437
 
513
- models.find { |m| m[:tier] == target_tier }
438
+ # Map pro -> advanced for registry compatibility
439
+ registry_tier = (target_tier == "pro") ? "advanced" : target_tier
440
+
441
+ models.find { |m| m[:tier] == registry_tier }
514
442
  end
515
443
 
516
444
  def find_models_for_tier(models, target_tier)
517
445
  return [] unless models
518
446
 
519
- models.select { |m| m[:tier] == target_tier }
447
+ # Map pro -> advanced for registry compatibility
448
+ registry_tier = (target_tier == "pro") ? "advanced" : target_tier
449
+
450
+ models.select { |m| m[:tier] == registry_tier }
520
451
  end
521
452
 
522
453
  def display_provider_tier_preview(provider_configs)
@@ -1717,9 +1648,6 @@ module Aidp
1717
1648
  # Enhance messaging with display name when available
1718
1649
  display_name = discover_available_providers.invert.fetch(provider_name, provider_name)
1719
1650
  prompt.say(" • #{action_word.capitalize} provider '#{display_name}' (#{provider_name}) with billing type '#{provider_type}' and model family '#{model_family}'")
1720
-
1721
- # Trigger background model discovery for newly added/updated provider
1722
- trigger_background_discovery(provider_name) unless @dry_run
1723
1651
  end
1724
1652
 
1725
1653
  def edit_or_remove_provider(provider_name, primary_provider, fallbacks)
data/lib/aidp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aidp
4
- VERSION = "0.28.0"
4
+ VERSION = "0.29.0"
5
5
  end
data/lib/aidp.rb CHANGED
@@ -49,6 +49,15 @@ require_relative "aidp/analyze/seams"
49
49
  require_relative "aidp/analyze/tree_sitter_scan"
50
50
  require_relative "aidp/analyze/kb_inspector"
51
51
 
52
+ # Metadata system
53
+ require_relative "aidp/metadata/tool_metadata"
54
+ require_relative "aidp/metadata/validator"
55
+ require_relative "aidp/metadata/parser"
56
+ require_relative "aidp/metadata/scanner"
57
+ require_relative "aidp/metadata/compiler"
58
+ require_relative "aidp/metadata/query"
59
+ require_relative "aidp/metadata/cache"
60
+
52
61
  # Workflows
53
62
  require_relative "aidp/workflows/definitions"
54
63
  require_relative "aidp/workflows/selector"
@@ -83,6 +92,8 @@ require_relative "aidp/harness/config_schema"
83
92
  require_relative "aidp/harness/config_validator"
84
93
  require_relative "aidp/harness/config_loader"
85
94
  require_relative "aidp/harness/config_manager"
95
+ require_relative "aidp/harness/ruby_llm_registry"
96
+ require_relative "aidp/harness/model_registry"
86
97
  require_relative "aidp/harness/condition_detector"
87
98
  require_relative "aidp/harness/user_interface"
88
99
  require_relative "aidp/harness/simple_user_interface"
@@ -23,7 +23,10 @@ You are a senior engineer writing **implementation guidance** for domain agents.
23
23
 
24
24
  ## Output
25
25
 
26
- - `docs/ImplementationGuide.md` with examples and a pattern-to-use-case matrix.
26
+ - `docs/<FEATURE_NAME>_IMPLEMENTATION.md` with examples and a pattern-to-use-case matrix.
27
+ - Use a descriptive name based on the feature/issue (e.g., `METADATA_TOOL_DISCOVERY.md`, `WORKTREE_PR_CHANGE_REQUESTS.md`)
28
+ - Do NOT use generic names like `ImplementationGuide.md`, `IMPLEMENTATION_SUMMARY.md`, or `SESSION_SUMMARY.md`
29
+ - Do NOT create files at the project root
27
30
 
28
31
  ## Regeneration Policy
29
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aidp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.0
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -219,6 +219,20 @@ dependencies:
219
219
  - - "~>"
220
220
  - !ruby/object:Gem::Version
221
221
  version: '0.10'
222
+ - !ruby/object:Gem::Dependency
223
+ name: ruby_llm
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - "~>"
227
+ - !ruby/object:Gem::Version
228
+ version: '1.9'
229
+ type: :runtime
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: '1.9'
222
236
  description: The AI-Dev-Pipeline (AIDP) CLI provides a powerful, markdown-driven workflow
223
237
  for software development. It supports in-depth project analysis to understand existing
224
238
  codebases and an execution mode to systematically implement new features.
@@ -269,6 +283,7 @@ files:
269
283
  - lib/aidp/cli/models_command.rb
270
284
  - lib/aidp/cli/providers_command.rb
271
285
  - lib/aidp/cli/terminal_io.rb
286
+ - lib/aidp/cli/tools_command.rb
272
287
  - lib/aidp/concurrency.rb
273
288
  - lib/aidp/concurrency/backoff.rb
274
289
  - lib/aidp/concurrency/exec.rb
@@ -314,7 +329,6 @@ files:
314
329
  - lib/aidp/harness/filter_strategy.rb
315
330
  - lib/aidp/harness/generic_filter_strategy.rb
316
331
  - lib/aidp/harness/model_cache.rb
317
- - lib/aidp/harness/model_discovery_service.rb
318
332
  - lib/aidp/harness/model_registry.rb
319
333
  - lib/aidp/harness/output_filter.rb
320
334
  - lib/aidp/harness/provider_config.rb
@@ -324,6 +338,7 @@ files:
324
338
  - lib/aidp/harness/provider_metrics.rb
325
339
  - lib/aidp/harness/provider_type_checker.rb
326
340
  - lib/aidp/harness/rspec_filter_strategy.rb
341
+ - lib/aidp/harness/ruby_llm_registry.rb
327
342
  - lib/aidp/harness/runner.rb
328
343
  - lib/aidp/harness/simple_user_interface.rb
329
344
  - lib/aidp/harness/state/errors.rb
@@ -365,6 +380,13 @@ files:
365
380
  - lib/aidp/jobs/background_runner.rb
366
381
  - lib/aidp/logger.rb
367
382
  - lib/aidp/message_display.rb
383
+ - lib/aidp/metadata/cache.rb
384
+ - lib/aidp/metadata/compiler.rb
385
+ - lib/aidp/metadata/parser.rb
386
+ - lib/aidp/metadata/query.rb
387
+ - lib/aidp/metadata/scanner.rb
388
+ - lib/aidp/metadata/tool_metadata.rb
389
+ - lib/aidp/metadata/validator.rb
368
390
  - lib/aidp/planning/analyzers/feedback_analyzer.rb
369
391
  - lib/aidp/planning/builders/agile_plan_builder.rb
370
392
  - lib/aidp/planning/builders/project_plan_builder.rb