Class: Aidp::Config
- Inherits:
-
Object
- Object
- Aidp::Config
- Defined in:
- lib/aidp/config.rb
Overview
Configuration management for both execute and analyze modes
Constant Summary collapse
- DEFAULT_HARNESS_CONFIG =
Default configuration for harness
{ harness: { max_retries: 2, default_provider: "cursor", fallback_providers: ["cursor"], no_api_keys_required: false, provider_weights: { "cursor" => 3, "anthropic" => 2 }, circuit_breaker: { enabled: true, failure_threshold: 5, timeout: 300, half_open_max_calls: 3 }, retry: { enabled: true, max_attempts: 3, base_delay: 1.0, max_delay: 60.0, exponential_base: 2.0, jitter: true }, rate_limit: { enabled: true, default_reset_time: 3600, burst_limit: 10, sustained_limit: 5 }, load_balancing: { enabled: true, strategy: "weighted_round_robin", health_check_interval: 30, unhealthy_threshold: 3 }, model_switching: { enabled: true, auto_switch_on_error: true, auto_switch_on_rate_limit: true, fallback_strategy: "sequential" }, health_check: { enabled: true, interval: 60, timeout: 10, failure_threshold: 3, success_threshold: 2 }, metrics: { enabled: true, retention_days: 30, aggregation_interval: 300, export_interval: 3600 }, session: { enabled: true, timeout: 1800, sticky_sessions: true, session_affinity: "provider_model" } }, providers: { cursor: { type: "subscription", priority: 1, model_family: "auto", default_flags: [], models: ["cursor-default", "cursor-fast", "cursor-precise"], model_weights: { "cursor-default" => 3, "cursor-fast" => 2, "cursor-precise" => 1 }, models_config: { "cursor-default" => { flags: [], timeout: 600 }, "cursor-fast" => { flags: ["--fast"], timeout: 300 }, "cursor-precise" => { flags: ["--precise"], timeout: 900 } }, features: { file_upload: true, code_generation: true, analysis: true }, monitoring: { enabled: true, metrics_interval: 60 } }, anthropic: { type: "usage_based", priority: 2, model_family: "claude", max_tokens: 100_000, default_flags: ["--dangerously-skip-permissions"], models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"], model_weights: { "claude-3-5-sonnet-20241022" => 3, "claude-3-5-haiku-20241022" => 2 }, models_config: { "claude-3-5-sonnet-20241022" => { flags: ["--dangerously-skip-permissions"], max_tokens: 200_000, timeout: 300 }, "claude-3-5-haiku-20241022" => { flags: ["--dangerously-skip-permissions"], max_tokens: 200_000, timeout: 180 } }, auth: { api_key_env: "ANTHROPIC_API_KEY" }, endpoints: { default: "https://api.anthropic.com/v1/messages" }, features: { file_upload: true, code_generation: true, analysis: true, vision: true }, monitoring: { enabled: true, metrics_interval: 60 } } }, skills: { search_paths: [], default_provider_filter: true, enable_custom_skills: true }, waterfall: { enabled: true, docs_directory: ".aidp/docs", generate_decisions_md: true, gantt_format: "mermaid", wbs_phases: [ "Requirements", "Design", "Implementation", "Testing", "Deployment" ], effort_estimation: { method: "llm_relative", units: "story_points" }, persona_assignment: { method: "zfc_automatic", allow_parallel: true } }, evaluations: { enabled: true, prompt_after_work_loop: false, capture_full_context: true, directory: ".aidp/evaluations" }, security: { rule_of_two: { enabled: true, policy: "strict" # strict or relaxed }, secrets_proxy: { enabled: true, token_ttl: 300 # seconds }, watch_mode: { max_retry_attempts: 3, fail_forward_enabled: true, needs_input_label: "aidp-needs-input" } }, watch: { enabled: false, polling_interval: 30, labels: { plan_trigger: "aidp-plan", project_trigger: "aidp-project", build_trigger: "aidp-build", blocked_trigger: "aidp-blocked", review_trigger: "aidp-review", fix_ci_trigger: "aidp-fix-ci", change_request_trigger: "aidp-request-changes", auto_trigger: "aidp-auto", parent_pr: "aidp-parent-pr", sub_pr: "aidp-sub-pr" }, projects: { enabled: false, default_project_id: nil, field_mappings: { status: "Status", priority: "Priority", skills: "Skills", personas: "Personas", blocking: "Blocking", start_date: "Start Date", target_date: "Target Date", dependencies: "Dependencies", critical_path: "Critical Path" }, auto_create_fields: true, sync_interval: 60, prd_path: nil, auto_sync_gantt: false, gantt_format: "auto", default_status_values: ["Backlog", "Todo", "In Progress", "In Review", "Done"], default_priority_values: ["Low", "Medium", "High", "Critical"] }, auto_merge: { enabled: true, sub_issue_prs_only: true, require_ci_success: true, require_reviews: 0, merge_method: "squash", delete_branch: true }, worktree_cleanup: { enabled: true, frequency: "weekly", # daily or weekly base_branch: "main", delete_branch: true } } }.freeze
Class Method Summary collapse
-
.agile_config(project_dir = Dir.pwd) ⇒ Object
Get agile configuration.
- .aidp_dir(project_dir = Dir.pwd) ⇒ Object
- .config_dir(project_dir = Dir.pwd) ⇒ Object
-
.config_exists?(project_dir = Dir.pwd) ⇒ Boolean
Check if configuration file exists.
-
.config_file(project_dir = Dir.pwd) ⇒ Object
Expose path methods for convenience.
-
.configured_providers(project_dir = Dir.pwd) ⇒ Object
Get all configured providers.
-
.create_example_config(project_dir = Dir.pwd) ⇒ Object
Create example configuration file.
-
.evaluations_config(project_dir = Dir.pwd) ⇒ Object
Get evaluations configuration.
-
.harness_config(project_dir = Dir.pwd) ⇒ Object
Get harness configuration.
- .load(project_dir = Dir.pwd) ⇒ Object
-
.load_harness_config(project_dir = Dir.pwd) ⇒ Object
Load harness configuration with defaults.
-
.provider_config(provider_name, project_dir = Dir.pwd) ⇒ Object
Get provider configuration.
-
.rule_of_two_enabled?(project_dir = Dir.pwd) ⇒ Boolean
Check if Rule of Two enforcement is enabled.
-
.secrets_proxy_enabled?(project_dir = Dir.pwd) ⇒ Boolean
Check if Secrets Proxy is enabled.
-
.security_config(project_dir = Dir.pwd) ⇒ Object
Get security configuration.
-
.skills_config(project_dir = Dir.pwd) ⇒ Object
Get skills configuration.
-
.tool_metadata_config(project_dir = Dir.pwd) ⇒ Object
Get tool metadata configuration.
-
.validate_harness_config(config, project_dir = Dir.pwd) ⇒ Object
Validate harness configuration.
-
.waterfall_config(project_dir = Dir.pwd) ⇒ Object
Get waterfall configuration.
-
.worktree_cleanup_config(project_dir = Dir.pwd) ⇒ Object
Get worktree cleanup configuration.
Class Method Details
.agile_config(project_dir = Dir.pwd) ⇒ Object
Get agile configuration
346 347 348 349 350 351 352 |
# File 'lib/aidp/config.rb', line 346 def self.agile_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) agile_section = config[:agile] || config["agile"] || {} # Convert string keys to symbols for consistency symbolize_keys(agile_section) end |
.aidp_dir(project_dir = Dir.pwd) ⇒ Object
463 464 465 |
# File 'lib/aidp/config.rb', line 463 def self.aidp_dir(project_dir = Dir.pwd) ConfigPaths.aidp_dir(project_dir) end |
.config_dir(project_dir = Dir.pwd) ⇒ Object
459 460 461 |
# File 'lib/aidp/config.rb', line 459 def self.config_dir(project_dir = Dir.pwd) ConfigPaths.config_dir(project_dir) end |
.config_exists?(project_dir = Dir.pwd) ⇒ Boolean
Check if configuration file exists
405 406 407 |
# File 'lib/aidp/config.rb', line 405 def self.config_exists?(project_dir = Dir.pwd) ConfigPaths.config_exists?(project_dir) end |
.config_file(project_dir = Dir.pwd) ⇒ Object
Expose path methods for convenience
455 456 457 |
# File 'lib/aidp/config.rb', line 455 def self.config_file(project_dir = Dir.pwd) ConfigPaths.config_file(project_dir) end |
.configured_providers(project_dir = Dir.pwd) ⇒ Object
Get all configured providers
321 322 323 324 325 |
# File 'lib/aidp/config.rb', line 321 def self.configured_providers(project_dir = Dir.pwd) config = load_harness_config(project_dir) providers_section = config[:providers] || config["providers"] || {} providers_section.keys.map(&:to_s) end |
.create_example_config(project_dir = Dir.pwd) ⇒ Object
Create example configuration file
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/aidp/config.rb', line 410 def self.create_example_config(project_dir = Dir.pwd) config_path = ConfigPaths.config_file(project_dir) return false if File.exist?(config_path) ConfigPaths.ensure_config_dir(project_dir) example_config = { harness: { max_retries: 2, default_provider: "cursor", fallback_providers: ["cursor"], no_api_keys_required: false }, providers: { cursor: { type: "subscription", default_flags: [] }, claude: { type: "usage_based", max_tokens: 100_000, default_flags: ["--dangerously-skip-permissions"] }, gemini: { type: "usage_based", max_tokens: 50_000, default_flags: [] } }, agile: { mvp_first: true, feedback_loops: true, auto_iteration: false, research_enabled: true, marketing_enabled: true, legacy_analysis: true, personas: ["product_manager", "ux_researcher", "architect", "senior_developer", "qa_engineer", "devops_engineer", "tech_writer", "marketing_strategist"] } } File.write(config_path, YAML.dump(example_config)) true end |
.evaluations_config(project_dir = Dir.pwd) ⇒ Object
Get evaluations configuration
364 365 366 367 368 369 370 |
# File 'lib/aidp/config.rb', line 364 def self.evaluations_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) evaluations_section = config[:evaluations] || config["evaluations"] || {} # Convert string keys to symbols for consistency symbolize_keys(evaluations_section) end |
.harness_config(project_dir = Dir.pwd) ⇒ Object
Get harness configuration
303 304 305 306 307 308 309 |
# File 'lib/aidp/config.rb', line 303 def self.harness_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) harness_section = config[:harness] || config["harness"] || {} # Convert string keys to symbols for consistency symbolize_keys(harness_section) end |
.load(project_dir = Dir.pwd) ⇒ Object
249 250 251 252 253 254 255 256 257 |
# File 'lib/aidp/config.rb', line 249 def self.load(project_dir = Dir.pwd) config_file = ConfigPaths.config_file(project_dir) if File.exist?(config_file) load_yaml_config(config_file) else {} end end |
.load_harness_config(project_dir = Dir.pwd) ⇒ Object
Load harness configuration with defaults
260 261 262 263 |
# File 'lib/aidp/config.rb', line 260 def self.load_harness_config(project_dir = Dir.pwd) config = load(project_dir) merge_harness_defaults(config) end |
.provider_config(provider_name, project_dir = Dir.pwd) ⇒ Object
Get provider configuration
312 313 314 315 316 317 318 |
# File 'lib/aidp/config.rb', line 312 def self.provider_config(provider_name, project_dir = Dir.pwd) config = load_harness_config(project_dir) providers_section = config[:providers] || config["providers"] || {} provider_config = providers_section[provider_name.to_s] || providers_section[provider_name.to_sym] || {} symbolize_keys(provider_config) end |
.rule_of_two_enabled?(project_dir = Dir.pwd) ⇒ Boolean
Check if Rule of Two enforcement is enabled
382 383 384 385 386 |
# File 'lib/aidp/config.rb', line 382 def self.rule_of_two_enabled?(project_dir = Dir.pwd) sec_config = security_config(project_dir) rule_of_two = sec_config[:rule_of_two] || {} rule_of_two.fetch(:enabled, true) end |
.secrets_proxy_enabled?(project_dir = Dir.pwd) ⇒ Boolean
Check if Secrets Proxy is enabled
389 390 391 392 393 |
# File 'lib/aidp/config.rb', line 389 def self.secrets_proxy_enabled?(project_dir = Dir.pwd) sec_config = security_config(project_dir) proxy_config = sec_config[:secrets_proxy] || {} proxy_config.fetch(:enabled, true) end |
.security_config(project_dir = Dir.pwd) ⇒ Object
Get security configuration
373 374 375 376 377 378 379 |
# File 'lib/aidp/config.rb', line 373 def self.security_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) security_section = config[:security] || config["security"] || {} # Convert string keys to symbols for consistency symbolize_keys(security_section) end |
.skills_config(project_dir = Dir.pwd) ⇒ Object
Get skills configuration
328 329 330 331 332 333 334 |
# File 'lib/aidp/config.rb', line 328 def self.skills_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) skills_section = config[:skills] || config["skills"] || {} # Convert string keys to symbols for consistency symbolize_keys(skills_section) end |
.tool_metadata_config(project_dir = Dir.pwd) ⇒ Object
Get tool metadata configuration
355 356 357 358 359 360 361 |
# File 'lib/aidp/config.rb', line 355 def self.(project_dir = Dir.pwd) config = load_harness_config(project_dir) = config[:tool_metadata] || config["tool_metadata"] || {} # Convert string keys to symbols for consistency symbolize_keys() end |
.validate_harness_config(config, project_dir = Dir.pwd) ⇒ Object
Validate harness configuration
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/aidp/config.rb', line 266 def self.validate_harness_config(config, project_dir = Dir.pwd) errors = [] # Validate harness section (check the merged config, not original) harness_config = config[:harness] || config["harness"] if harness_config unless harness_config[:default_provider] || harness_config["default_provider"] errors << "Default provider not specified in harness config" end end # Validate providers section using config_validator # Only validate providers that exist in the original YAML file, not merged defaults original_config = load(project_dir) original_providers = original_config[:providers] || original_config["providers"] if original_providers&.any? require_relative "harness/config_validator" validator = Aidp::Harness::ConfigValidator.new(project_dir) # Only validate if the config file exists # Skip validation if we're validating a simple test config (no project_dir specified or simple config) should_validate = validator.config_exists? && (project_dir != Dir.pwd || config[:harness]&.keys&.size.to_i > 2) if should_validate original_providers.each do |provider_name, _provider_config| validation_result = validator.validate_provider(provider_name) unless validation_result[:valid] errors.concat(validation_result[:errors]) end end end end errors end |
.waterfall_config(project_dir = Dir.pwd) ⇒ Object
Get waterfall configuration
337 338 339 340 341 342 343 |
# File 'lib/aidp/config.rb', line 337 def self.waterfall_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) waterfall_section = config[:waterfall] || config["waterfall"] || {} # Convert string keys to symbols for consistency symbolize_keys(waterfall_section) end |
.worktree_cleanup_config(project_dir = Dir.pwd) ⇒ Object
Get worktree cleanup configuration
396 397 398 399 400 401 402 |
# File 'lib/aidp/config.rb', line 396 def self.worktree_cleanup_config(project_dir = Dir.pwd) config = load_harness_config(project_dir) watch_section = config[:watch] || config["watch"] || {} cleanup_section = watch_section[:worktree_cleanup] || watch_section["worktree_cleanup"] || {} symbolize_keys(cleanup_section) end |