aidp 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/README.md +69 -0
- data/lib/aidp/analyze/kb_inspector.rb +2 -3
- data/lib/aidp/analyze/progress.rb +5 -10
- data/lib/aidp/cli/mcp_dashboard.rb +1 -1
- data/lib/aidp/cli.rb +64 -29
- data/lib/aidp/config.rb +9 -14
- data/lib/aidp/execute/progress.rb +5 -8
- data/lib/aidp/execute/prompt_manager.rb +128 -1
- data/lib/aidp/execute/repl_macros.rb +555 -0
- data/lib/aidp/execute/work_loop_runner.rb +108 -1
- data/lib/aidp/harness/ai_decision_engine.rb +376 -0
- data/lib/aidp/harness/capability_registry.rb +273 -0
- data/lib/aidp/harness/config_loader.rb +2 -2
- data/lib/aidp/harness/config_schema.rb +305 -1
- data/lib/aidp/harness/configuration.rb +452 -0
- data/lib/aidp/harness/enhanced_runner.rb +23 -8
- data/lib/aidp/harness/error_handler.rb +12 -5
- data/lib/aidp/harness/provider_factory.rb +0 -2
- data/lib/aidp/harness/provider_manager.rb +4 -19
- data/lib/aidp/harness/runner.rb +9 -3
- data/lib/aidp/harness/state/persistence.rb +9 -10
- data/lib/aidp/harness/state/workflow_state.rb +3 -2
- data/lib/aidp/harness/state_manager.rb +33 -97
- data/lib/aidp/harness/status_display.rb +22 -12
- data/lib/aidp/harness/thinking_depth_manager.rb +335 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +3 -4
- data/lib/aidp/harness/user_interface.rb +11 -6
- data/lib/aidp/harness/zfc_condition_detector.rb +395 -0
- data/lib/aidp/init/devcontainer_generator.rb +274 -0
- data/lib/aidp/init/runner.rb +37 -10
- data/lib/aidp/init.rb +1 -0
- data/lib/aidp/jobs/background_runner.rb +7 -1
- data/lib/aidp/logger.rb +1 -1
- data/lib/aidp/message_display.rb +9 -2
- data/lib/aidp/prompt_optimization/context_composer.rb +286 -0
- data/lib/aidp/prompt_optimization/optimizer.rb +335 -0
- data/lib/aidp/prompt_optimization/prompt_builder.rb +309 -0
- data/lib/aidp/prompt_optimization/relevance_scorer.rb +256 -0
- data/lib/aidp/prompt_optimization/source_code_fragmenter.rb +308 -0
- data/lib/aidp/prompt_optimization/style_guide_indexer.rb +240 -0
- data/lib/aidp/prompt_optimization/template_indexer.rb +250 -0
- data/lib/aidp/provider_manager.rb +0 -2
- data/lib/aidp/providers/anthropic.rb +20 -1
- data/lib/aidp/providers/base.rb +4 -4
- data/lib/aidp/providers/codex.rb +1 -1
- data/lib/aidp/providers/cursor.rb +1 -1
- data/lib/aidp/providers/gemini.rb +1 -1
- data/lib/aidp/providers/github_copilot.rb +1 -1
- data/lib/aidp/providers/opencode.rb +1 -1
- data/lib/aidp/setup/wizard.rb +299 -4
- data/lib/aidp/skills/wizard/prompter.rb +2 -2
- data/lib/aidp/utils/devcontainer_detector.rb +166 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +72 -6
- data/lib/aidp/watch/plan_generator.rb +1 -1
- data/lib/aidp/watch/repository_client.rb +15 -10
- data/lib/aidp/workflows/guided_agent.rb +2 -312
- data/lib/aidp/workstream_executor.rb +8 -2
- data/lib/aidp.rb +0 -1
- data/templates/aidp.yml.example +128 -0
- metadata +14 -2
- data/lib/aidp/providers/macos_ui.rb +0 -102
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 95269030b5c3fe91c2cfb6e29066a4f37afbcf1903e930a377ec02dd1e8a738c
|
|
4
|
+
data.tar.gz: 6c343d45e804a73751c99e0e63491703c1eb9ba18fb2acf4e828198e774c2c92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc54a6f0b9424c18ebdd8e6df75e77f0f233db98dd59543a9dd6152eacd835d8820c437ccd220c3dbcfb43cf91bfeb47e40ce415773e8cd0e24783770b2ac033
|
|
7
|
+
data.tar.gz: f89cf387121e7edc3c384cead9369bc6ae5be97e639969fa036978a249779152ccd7a50832897be97d7fd04f4083ccf744e23e93c1117c6ed5d1cc7d8529615d
|
data/README.md
CHANGED
|
@@ -42,6 +42,75 @@ You can re-run the wizard manually:
|
|
|
42
42
|
aidp --setup-config
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Devcontainer Support
|
|
46
|
+
|
|
47
|
+
AIDP provides first-class devcontainer support for sandboxed, secure AI agent execution. Devcontainers offer:
|
|
48
|
+
|
|
49
|
+
- **Network Security**: Strict firewall with allowlisted domains only
|
|
50
|
+
- **Sandboxed Environment**: Isolated from your host system
|
|
51
|
+
- **Elevated Permissions**: AI agents can run with full permissions inside the container
|
|
52
|
+
- **Consistent Setup**: Same environment across all developers
|
|
53
|
+
|
|
54
|
+
### For AIDP Development
|
|
55
|
+
|
|
56
|
+
This repository includes a `.devcontainer/` setup for developing AIDP itself:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Open in VS Code
|
|
60
|
+
code .
|
|
61
|
+
|
|
62
|
+
# Press F1 → "Dev Containers: Reopen in Container"
|
|
63
|
+
# Container builds automatically with Ruby 3.4.5, all tools, and firewall
|
|
64
|
+
|
|
65
|
+
# Run tests inside container
|
|
66
|
+
bundle exec rspec
|
|
67
|
+
|
|
68
|
+
# Run AIDP inside container
|
|
69
|
+
bundle exec aidp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See [.devcontainer/README.md](.devcontainer/README.md) for complete documentation.
|
|
73
|
+
|
|
74
|
+
### Generating Devcontainers for Your Projects
|
|
75
|
+
|
|
76
|
+
Use `aidp init` to generate a devcontainer for any project:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Initialize project with devcontainer
|
|
80
|
+
aidp init
|
|
81
|
+
|
|
82
|
+
# When prompted:
|
|
83
|
+
# "Generate devcontainer configuration for sandboxed development?" → Yes
|
|
84
|
+
|
|
85
|
+
# Or use the flag directly
|
|
86
|
+
aidp init --with-devcontainer
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This creates:
|
|
90
|
+
|
|
91
|
+
- `.devcontainer/Dockerfile` - Customized for your project's language/framework
|
|
92
|
+
- `.devcontainer/devcontainer.json` - VS Code configuration and extensions
|
|
93
|
+
- `.devcontainer/init-firewall.sh` - Network security rules
|
|
94
|
+
- `.devcontainer/README.md` - Setup and usage documentation
|
|
95
|
+
|
|
96
|
+
### Elevated Permissions in Devcontainers
|
|
97
|
+
|
|
98
|
+
When running inside a devcontainer, you can enable elevated permissions for AI agents:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
# aidp.yml
|
|
102
|
+
devcontainer:
|
|
103
|
+
enabled: true
|
|
104
|
+
full_permissions_when_in_devcontainer: true # Run all providers with full permissions
|
|
105
|
+
|
|
106
|
+
# Or enable per-provider
|
|
107
|
+
permissions:
|
|
108
|
+
skip_permission_checks:
|
|
109
|
+
- claude # Adds --dangerously-skip-permissions for Claude Code
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
AIDP automatically detects when it's running in a devcontainer and adjusts agent permissions accordingly. This is safe because the container is sandboxed from your host system.
|
|
113
|
+
|
|
45
114
|
## Core Features
|
|
46
115
|
|
|
47
116
|
### Work Loops
|
|
@@ -80,7 +80,7 @@ module Aidp
|
|
|
80
80
|
display_message(box)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def load_kb_data
|
|
83
|
+
def load_kb_data(suppress_parse_warnings: false)
|
|
84
84
|
data = {}
|
|
85
85
|
|
|
86
86
|
%w[symbols imports calls metrics seams hotspots tests cycles].each do |type|
|
|
@@ -89,8 +89,7 @@ module Aidp
|
|
|
89
89
|
begin
|
|
90
90
|
data[type.to_sym] = JSON.parse(File.read(file_path), symbolize_names: true)
|
|
91
91
|
rescue JSON::ParserError => e
|
|
92
|
-
|
|
93
|
-
unless ENV["RACK_ENV"] == "test" || defined?(RSpec)
|
|
92
|
+
unless suppress_parse_warnings
|
|
94
93
|
display_message("Warning: Could not parse #{file_path}: #{e.message}", type: :warn)
|
|
95
94
|
end
|
|
96
95
|
data[type.to_sym] = []
|
|
@@ -9,9 +9,10 @@ module Aidp
|
|
|
9
9
|
class Progress
|
|
10
10
|
attr_reader :project_dir, :progress_file
|
|
11
11
|
|
|
12
|
-
def initialize(project_dir)
|
|
12
|
+
def initialize(project_dir, skip_persistence: false)
|
|
13
13
|
@project_dir = project_dir
|
|
14
14
|
@progress_file = File.join(project_dir, ".aidp", "progress", "analyze.yml")
|
|
15
|
+
@skip_persistence = skip_persistence
|
|
15
16
|
load_progress
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -60,26 +61,20 @@ module Aidp
|
|
|
60
61
|
private
|
|
61
62
|
|
|
62
63
|
def load_progress
|
|
63
|
-
|
|
64
|
-
if (ENV["RACK_ENV"] == "test" || defined?(RSpec)) && !File.exist?(@progress_file)
|
|
64
|
+
if @skip_persistence && !File.exist?(@progress_file)
|
|
65
65
|
@progress = {}
|
|
66
66
|
return
|
|
67
67
|
end
|
|
68
|
-
|
|
69
|
-
@progress = if File.exist?(@progress_file)
|
|
68
|
+
@progress = if !@skip_persistence && File.exist?(@progress_file)
|
|
70
69
|
YAML.safe_load_file(@progress_file, permitted_classes: [Date, Time, Symbol], aliases: true) || {}
|
|
71
70
|
else
|
|
72
71
|
{}
|
|
73
72
|
end
|
|
74
|
-
|
|
75
|
-
# Ensure @progress is never nil
|
|
76
73
|
@progress = {} if @progress.nil?
|
|
77
74
|
end
|
|
78
75
|
|
|
79
76
|
def save_progress
|
|
80
|
-
|
|
81
|
-
return if ENV["RACK_ENV"] == "test" || defined?(RSpec)
|
|
82
|
-
|
|
77
|
+
return if @skip_persistence
|
|
83
78
|
FileUtils.mkdir_p(File.dirname(@progress_file))
|
|
84
79
|
File.write(@progress_file, @progress.to_yaml)
|
|
85
80
|
end
|
data/lib/aidp/cli.rb
CHANGED
|
@@ -147,6 +147,10 @@ module Aidp
|
|
|
147
147
|
class << self
|
|
148
148
|
extend Aidp::MessageDisplay::ClassMethods
|
|
149
149
|
|
|
150
|
+
def create_prompt
|
|
151
|
+
::TTY::Prompt.new
|
|
152
|
+
end
|
|
153
|
+
|
|
150
154
|
def run(args = ARGV)
|
|
151
155
|
# Handle subcommands first (status, jobs, kb, harness)
|
|
152
156
|
return run_subcommand(args) if subcommand?(args)
|
|
@@ -173,7 +177,7 @@ module Aidp
|
|
|
173
177
|
|
|
174
178
|
# Handle configuration setup
|
|
175
179
|
# Create a prompt for the wizard
|
|
176
|
-
prompt =
|
|
180
|
+
prompt = create_prompt
|
|
177
181
|
|
|
178
182
|
if options[:setup_config]
|
|
179
183
|
# Force setup/reconfigure even if config exists
|
|
@@ -398,7 +402,7 @@ module Aidp
|
|
|
398
402
|
|
|
399
403
|
def run_jobs_command(args = [])
|
|
400
404
|
require_relative "cli/jobs_command"
|
|
401
|
-
jobs_cmd = Aidp::CLI::JobsCommand.new(prompt:
|
|
405
|
+
jobs_cmd = Aidp::CLI::JobsCommand.new(prompt: create_prompt)
|
|
402
406
|
subcommand = args.shift
|
|
403
407
|
jobs_cmd.run(subcommand, args)
|
|
404
408
|
end
|
|
@@ -504,21 +508,6 @@ module Aidp
|
|
|
504
508
|
if step
|
|
505
509
|
display_message("Running #{mode} step '#{step}' with enhanced TUI harness", type: :highlight)
|
|
506
510
|
display_message("progress indicators", type: :info)
|
|
507
|
-
if step.start_with?("00_PRD") && (defined?(RSpec) || ENV["RSPEC_RUNNING"])
|
|
508
|
-
# Simulate questions & completion similar to TUI test mode
|
|
509
|
-
root = ENV["AIDP_ROOT"] || Dir.pwd
|
|
510
|
-
file = Dir.glob(File.join(root, "templates", (mode == :execute) ? "EXECUTE" : "ANALYZE", "00_PRD*.md")).first
|
|
511
|
-
if file && File.file?(file)
|
|
512
|
-
content = File.read(file)
|
|
513
|
-
questions_section = content.split(/## Questions/i)[1]
|
|
514
|
-
if questions_section
|
|
515
|
-
questions_section.lines.select { |l| l.strip.start_with?("-") }.each do |line|
|
|
516
|
-
display_message(line.strip.sub(/^-\s*/, ""), type: :info)
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
end
|
|
520
|
-
display_message("PRD completed", type: :success)
|
|
521
|
-
end
|
|
522
511
|
return
|
|
523
512
|
end
|
|
524
513
|
display_message("Starting enhanced TUI harness", type: :highlight)
|
|
@@ -601,7 +590,7 @@ module Aidp
|
|
|
601
590
|
when "clear"
|
|
602
591
|
force = args.include?("--force")
|
|
603
592
|
unless force
|
|
604
|
-
prompt =
|
|
593
|
+
prompt = create_prompt
|
|
605
594
|
confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
|
|
606
595
|
return unless confirm
|
|
607
596
|
end
|
|
@@ -696,7 +685,7 @@ module Aidp
|
|
|
696
685
|
end
|
|
697
686
|
end
|
|
698
687
|
config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
|
|
699
|
-
pm = Aidp::Harness::ProviderManager.new(config_manager, prompt:
|
|
688
|
+
pm = Aidp::Harness::ProviderManager.new(config_manager, prompt: create_prompt)
|
|
700
689
|
|
|
701
690
|
# Use TTY::Spinner for progress indication
|
|
702
691
|
require "tty-spinner"
|
|
@@ -738,7 +727,12 @@ module Aidp
|
|
|
738
727
|
end
|
|
739
728
|
tokens = (r[:total_tokens].to_i > 0) ? r[:total_tokens].to_s : "0"
|
|
740
729
|
reason = r[:unhealthy_reason] || "-"
|
|
741
|
-
|
|
730
|
+
is_tty = begin
|
|
731
|
+
$stdout.respond_to?(:tty?) && $stdout.tty?
|
|
732
|
+
rescue
|
|
733
|
+
false
|
|
734
|
+
end
|
|
735
|
+
if no_color || !is_tty
|
|
742
736
|
[r[:provider], r[:status], (r[:available] ? "yes" : "no"), cb, rl, tokens, last_used, reason]
|
|
743
737
|
else
|
|
744
738
|
[
|
|
@@ -757,7 +751,7 @@ module Aidp
|
|
|
757
751
|
table = TTY::Table.new header, table_rows
|
|
758
752
|
display_message(table.render(:basic), type: :info)
|
|
759
753
|
rescue => e
|
|
760
|
-
|
|
754
|
+
Aidp.logger.warn("cli", "Failed to display provider health", error_class: e.class.name, error_message: e.message)
|
|
761
755
|
display_message("Failed to display provider health: #{e.message}", type: :error)
|
|
762
756
|
end
|
|
763
757
|
|
|
@@ -765,9 +759,10 @@ module Aidp
|
|
|
765
759
|
require_relative "harness/provider_info"
|
|
766
760
|
|
|
767
761
|
provider_name = args.shift
|
|
762
|
+
|
|
763
|
+
# If no provider specified, show models catalog table
|
|
768
764
|
unless provider_name
|
|
769
|
-
|
|
770
|
-
display_message("Example: aidp providers info claude", type: :info)
|
|
765
|
+
run_providers_models_catalog
|
|
771
766
|
return
|
|
772
767
|
end
|
|
773
768
|
|
|
@@ -777,7 +772,7 @@ module Aidp
|
|
|
777
772
|
display_message("=" * 60, type: :muted)
|
|
778
773
|
|
|
779
774
|
provider_info = Aidp::Harness::ProviderInfo.new(provider_name, Dir.pwd)
|
|
780
|
-
info = provider_info.
|
|
775
|
+
info = provider_info.info(force_refresh: force_refresh)
|
|
781
776
|
|
|
782
777
|
if info.nil?
|
|
783
778
|
display_message("No information available for provider: #{provider_name}", type: :error)
|
|
@@ -845,6 +840,46 @@ module Aidp
|
|
|
845
840
|
display_message("Tip: Use --refresh to update this information", type: :muted)
|
|
846
841
|
end
|
|
847
842
|
|
|
843
|
+
def run_providers_models_catalog
|
|
844
|
+
require_relative "harness/capability_registry"
|
|
845
|
+
require "tty-table"
|
|
846
|
+
|
|
847
|
+
display_message("Models Catalog - Thinking Depth Tiers", type: :highlight)
|
|
848
|
+
display_message("=" * 80, type: :muted)
|
|
849
|
+
|
|
850
|
+
registry = Aidp::Harness::CapabilityRegistry.new
|
|
851
|
+
unless registry.load_catalog
|
|
852
|
+
display_message("No models catalog found. Create .aidp/models_catalog.yml first.", type: :error)
|
|
853
|
+
return
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
rows = []
|
|
857
|
+
registry.provider_names.sort.each do |provider|
|
|
858
|
+
models = registry.models_for_provider(provider)
|
|
859
|
+
models.each do |model_name, model_data|
|
|
860
|
+
tier = model_data["tier"] || "-"
|
|
861
|
+
context = model_data["context_window"] ? "#{model_data["context_window"] / 1000}k" : "-"
|
|
862
|
+
tools = model_data["supports_tools"] ? "yes" : "no"
|
|
863
|
+
cost_input = model_data["cost_per_mtok_input"]
|
|
864
|
+
cost = cost_input ? "$#{cost_input}/MTok" : "-"
|
|
865
|
+
|
|
866
|
+
rows << [provider, model_name, tier, context, tools, cost]
|
|
867
|
+
end
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
if rows.empty?
|
|
871
|
+
display_message("No models found in catalog", type: :info)
|
|
872
|
+
return
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
header = ["Provider", "Model", "Tier", "Context", "Tools", "Cost"]
|
|
876
|
+
table = TTY::Table.new(header, rows)
|
|
877
|
+
display_message(table.render(:basic), type: :info)
|
|
878
|
+
|
|
879
|
+
display_message("\n" + "=" * 80, type: :muted)
|
|
880
|
+
display_message("Use '/thinking show' in REPL to see current tier configuration", type: :muted)
|
|
881
|
+
end
|
|
882
|
+
|
|
848
883
|
def run_providers_refresh_command(args)
|
|
849
884
|
require_relative "harness/provider_info"
|
|
850
885
|
require "tty-spinner"
|
|
@@ -1044,7 +1079,7 @@ module Aidp
|
|
|
1044
1079
|
return
|
|
1045
1080
|
end
|
|
1046
1081
|
|
|
1047
|
-
wizard = Aidp::Setup::Wizard.new(Dir.pwd, prompt:
|
|
1082
|
+
wizard = Aidp::Setup::Wizard.new(Dir.pwd, prompt: create_prompt, dry_run: dry_run)
|
|
1048
1083
|
wizard.run
|
|
1049
1084
|
end
|
|
1050
1085
|
|
|
@@ -1071,7 +1106,7 @@ module Aidp
|
|
|
1071
1106
|
end
|
|
1072
1107
|
|
|
1073
1108
|
require_relative "init/runner"
|
|
1074
|
-
runner = Aidp::Init::Runner.new(Dir.pwd, prompt:
|
|
1109
|
+
runner = Aidp::Init::Runner.new(Dir.pwd, prompt: create_prompt, options: options)
|
|
1075
1110
|
runner.run
|
|
1076
1111
|
end
|
|
1077
1112
|
|
|
@@ -1127,7 +1162,7 @@ module Aidp
|
|
|
1127
1162
|
project_dir: Dir.pwd,
|
|
1128
1163
|
once: once,
|
|
1129
1164
|
use_workstreams: use_workstreams,
|
|
1130
|
-
prompt:
|
|
1165
|
+
prompt: create_prompt
|
|
1131
1166
|
)
|
|
1132
1167
|
runner.start
|
|
1133
1168
|
rescue ArgumentError => e
|
|
@@ -1244,7 +1279,7 @@ module Aidp
|
|
|
1244
1279
|
|
|
1245
1280
|
# Confirm removal unless --force
|
|
1246
1281
|
unless force
|
|
1247
|
-
prompt =
|
|
1282
|
+
prompt = create_prompt
|
|
1248
1283
|
confirm = prompt.yes?("Remove workstream '#{slug}'?#{" (will also delete branch)" if delete_branch}")
|
|
1249
1284
|
return unless confirm
|
|
1250
1285
|
end
|
|
@@ -2124,7 +2159,7 @@ module Aidp
|
|
|
2124
2159
|
|
|
2125
2160
|
# Confirm deletion
|
|
2126
2161
|
require "tty-prompt"
|
|
2127
|
-
prompt =
|
|
2162
|
+
prompt = create_prompt
|
|
2128
2163
|
confirmed = prompt.yes?("Delete skill '#{skill.name}' (#{skill_id})? This cannot be undone.")
|
|
2129
2164
|
|
|
2130
2165
|
unless confirmed
|
data/lib/aidp/config.rb
CHANGED
|
@@ -15,8 +15,7 @@ module Aidp
|
|
|
15
15
|
no_api_keys_required: false,
|
|
16
16
|
provider_weights: {
|
|
17
17
|
"cursor" => 3,
|
|
18
|
-
"anthropic" => 2
|
|
19
|
-
"macos" => 1
|
|
18
|
+
"anthropic" => 2
|
|
20
19
|
},
|
|
21
20
|
circuit_breaker: {
|
|
22
21
|
enabled: true,
|
|
@@ -74,6 +73,7 @@ module Aidp
|
|
|
74
73
|
cursor: {
|
|
75
74
|
type: "subscription",
|
|
76
75
|
priority: 1,
|
|
76
|
+
model_family: "auto",
|
|
77
77
|
default_flags: [],
|
|
78
78
|
models: ["cursor-default", "cursor-fast", "cursor-precise"],
|
|
79
79
|
model_weights: {
|
|
@@ -108,6 +108,7 @@ module Aidp
|
|
|
108
108
|
anthropic: {
|
|
109
109
|
type: "usage_based",
|
|
110
110
|
priority: 2,
|
|
111
|
+
model_family: "claude",
|
|
111
112
|
max_tokens: 100_000,
|
|
112
113
|
default_flags: ["--dangerously-skip-permissions"],
|
|
113
114
|
models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"],
|
|
@@ -149,18 +150,6 @@ module Aidp
|
|
|
149
150
|
enabled: true,
|
|
150
151
|
metrics_interval: 60
|
|
151
152
|
}
|
|
152
|
-
},
|
|
153
|
-
macos: {
|
|
154
|
-
type: "passthrough",
|
|
155
|
-
priority: 4,
|
|
156
|
-
underlying_service: "cursor",
|
|
157
|
-
models: ["cursor-chat"],
|
|
158
|
-
features: {
|
|
159
|
-
file_upload: false,
|
|
160
|
-
code_generation: true,
|
|
161
|
-
analysis: true,
|
|
162
|
-
interactive: true
|
|
163
|
-
}
|
|
164
153
|
}
|
|
165
154
|
},
|
|
166
155
|
skills: {
|
|
@@ -342,6 +331,12 @@ module Aidp
|
|
|
342
331
|
merged[:skills] = merged[:skills].merge(symbolize_keys(skills_section))
|
|
343
332
|
end
|
|
344
333
|
|
|
334
|
+
# Deep merge thinking config
|
|
335
|
+
if config[:thinking] || config["thinking"]
|
|
336
|
+
thinking_section = config[:thinking] || config["thinking"]
|
|
337
|
+
merged[:thinking] = symbolize_keys(thinking_section)
|
|
338
|
+
end
|
|
339
|
+
|
|
345
340
|
merged
|
|
346
341
|
end
|
|
347
342
|
|
|
@@ -9,9 +9,10 @@ module Aidp
|
|
|
9
9
|
class Progress
|
|
10
10
|
attr_reader :project_dir, :progress_file
|
|
11
11
|
|
|
12
|
-
def initialize(project_dir)
|
|
12
|
+
def initialize(project_dir, skip_persistence: false)
|
|
13
13
|
@project_dir = project_dir
|
|
14
14
|
@progress_file = File.join(project_dir, ".aidp", "progress", "execute.yml")
|
|
15
|
+
@skip_persistence = skip_persistence
|
|
15
16
|
load_progress
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -61,13 +62,11 @@ module Aidp
|
|
|
61
62
|
private
|
|
62
63
|
|
|
63
64
|
def load_progress
|
|
64
|
-
|
|
65
|
-
if (ENV["RACK_ENV"] == "test" || defined?(RSpec)) && !File.exist?(@progress_file)
|
|
65
|
+
if @skip_persistence && !File.exist?(@progress_file)
|
|
66
66
|
@progress = {}
|
|
67
67
|
return
|
|
68
68
|
end
|
|
69
|
-
|
|
70
|
-
@progress = if File.exist?(@progress_file)
|
|
69
|
+
@progress = if !@skip_persistence && File.exist?(@progress_file)
|
|
71
70
|
YAML.safe_load_file(@progress_file, permitted_classes: [Date, Time, Symbol], aliases: true) || {}
|
|
72
71
|
else
|
|
73
72
|
{}
|
|
@@ -75,9 +74,7 @@ module Aidp
|
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
def save_progress
|
|
78
|
-
|
|
79
|
-
return if ENV["RACK_ENV"] == "test" || defined?(RSpec)
|
|
80
|
-
|
|
77
|
+
return if @skip_persistence
|
|
81
78
|
FileUtils.mkdir_p(File.dirname(@progress_file))
|
|
82
79
|
File.write(@progress_file, @progress.to_yaml)
|
|
83
80
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require_relative "../prompt_optimization/optimizer"
|
|
4
5
|
|
|
5
6
|
module Aidp
|
|
6
7
|
module Execute
|
|
@@ -9,21 +10,91 @@ module Aidp
|
|
|
9
10
|
# - Read/write PROMPT.md
|
|
10
11
|
# - Check existence
|
|
11
12
|
# - Archive completed prompts
|
|
13
|
+
# - Optionally optimize prompts using intelligent fragment selection (ZFC)
|
|
12
14
|
class PromptManager
|
|
13
15
|
PROMPT_FILENAME = "PROMPT.md"
|
|
14
16
|
ARCHIVE_DIR = ".aidp/prompt_archive"
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
attr_reader :optimizer, :last_optimization_stats
|
|
19
|
+
|
|
20
|
+
def initialize(project_dir, config: nil)
|
|
17
21
|
@project_dir = project_dir
|
|
18
22
|
@prompt_path = File.join(project_dir, PROMPT_FILENAME)
|
|
19
23
|
@archive_dir = File.join(project_dir, ARCHIVE_DIR)
|
|
24
|
+
@config = config
|
|
25
|
+
@optimizer = nil
|
|
26
|
+
@last_optimization_stats = nil
|
|
27
|
+
|
|
28
|
+
# Initialize optimizer if enabled
|
|
29
|
+
if config&.respond_to?(:prompt_optimization_enabled?) && config.prompt_optimization_enabled?
|
|
30
|
+
@optimizer = Aidp::PromptOptimization::Optimizer.new(
|
|
31
|
+
project_dir: project_dir,
|
|
32
|
+
config: config.prompt_optimization_config
|
|
33
|
+
)
|
|
34
|
+
end
|
|
20
35
|
end
|
|
21
36
|
|
|
22
37
|
# Write content to PROMPT.md
|
|
38
|
+
# If optimization is enabled, stores the content but doesn't write yet
|
|
39
|
+
# (use write_optimized instead)
|
|
23
40
|
def write(content)
|
|
24
41
|
File.write(@prompt_path, content)
|
|
25
42
|
end
|
|
26
43
|
|
|
44
|
+
# Write optimized prompt using intelligent fragment selection
|
|
45
|
+
#
|
|
46
|
+
# Uses Zero Framework Cognition to select only the most relevant fragments
|
|
47
|
+
# from style guides, templates, and source code based on task context.
|
|
48
|
+
#
|
|
49
|
+
# @param task_context [Hash] Context about the current task
|
|
50
|
+
# @option task_context [Symbol] :task_type Type of task (:feature, :bugfix, etc.)
|
|
51
|
+
# @option task_context [String] :description Task description
|
|
52
|
+
# @option task_context [Array<String>] :affected_files Files being modified
|
|
53
|
+
# @option task_context [String] :step_name Current work loop step
|
|
54
|
+
# @option task_context [Array<String>] :tags Additional context tags
|
|
55
|
+
# @param options [Hash] Optimization options
|
|
56
|
+
# @option options [Boolean] :include_metadata Include debug metadata
|
|
57
|
+
# @return [Boolean] True if optimization was used, false if fallback to regular write
|
|
58
|
+
def write_optimized(task_context, options = {})
|
|
59
|
+
unless @optimizer
|
|
60
|
+
Aidp.logger.warn("prompt_manager", "Optimization requested but not enabled")
|
|
61
|
+
return false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
begin
|
|
65
|
+
# Use optimizer to build intelligent prompt
|
|
66
|
+
result = @optimizer.optimize_prompt(
|
|
67
|
+
task_type: task_context[:task_type],
|
|
68
|
+
description: task_context[:description],
|
|
69
|
+
affected_files: task_context[:affected_files] || [],
|
|
70
|
+
step_name: task_context[:step_name],
|
|
71
|
+
tags: task_context[:tags] || [],
|
|
72
|
+
options: options
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Write optimized prompt
|
|
76
|
+
result.write_to_file(@prompt_path)
|
|
77
|
+
|
|
78
|
+
# Store statistics for inspection
|
|
79
|
+
@last_optimization_stats = result.composition_result
|
|
80
|
+
|
|
81
|
+
# Log optimization results
|
|
82
|
+
Aidp.logger.info(
|
|
83
|
+
"prompt_manager",
|
|
84
|
+
"Optimized prompt written",
|
|
85
|
+
selected_fragments: result.composition_result.selected_count,
|
|
86
|
+
excluded_fragments: result.composition_result.excluded_count,
|
|
87
|
+
tokens: result.estimated_tokens,
|
|
88
|
+
budget_utilization: result.composition_result.budget_utilization
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
true
|
|
92
|
+
rescue => e
|
|
93
|
+
Aidp.logger.error("prompt_manager", "Optimization failed, using fallback", error: e.message)
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
27
98
|
# Read content from PROMPT.md
|
|
28
99
|
def read
|
|
29
100
|
return nil unless exists?
|
|
@@ -57,6 +128,62 @@ module Aidp
|
|
|
57
128
|
def path
|
|
58
129
|
@prompt_path
|
|
59
130
|
end
|
|
131
|
+
|
|
132
|
+
# Get optimization report for last optimization
|
|
133
|
+
#
|
|
134
|
+
# @return [String, nil] Markdown report or nil if no optimization performed
|
|
135
|
+
def optimization_report
|
|
136
|
+
return nil unless @last_optimization_stats
|
|
137
|
+
|
|
138
|
+
# Build report from composition result
|
|
139
|
+
lines = []
|
|
140
|
+
lines << "# Prompt Optimization Report"
|
|
141
|
+
lines << ""
|
|
142
|
+
lines << "## Statistics"
|
|
143
|
+
lines << "- **Selected Fragments**: #{@last_optimization_stats.selected_count}"
|
|
144
|
+
lines << "- **Excluded Fragments**: #{@last_optimization_stats.excluded_count}"
|
|
145
|
+
lines << "- **Total Tokens**: #{@last_optimization_stats.total_tokens} / #{@last_optimization_stats.budget}"
|
|
146
|
+
lines << "- **Budget Utilization**: #{@last_optimization_stats.budget_utilization.round(1)}%"
|
|
147
|
+
lines << "- **Average Relevance Score**: #{(@last_optimization_stats.average_score * 100).round(1)}%"
|
|
148
|
+
lines << ""
|
|
149
|
+
lines << "## Selected Fragments"
|
|
150
|
+
@last_optimization_stats.selected_fragments.each do |scored|
|
|
151
|
+
fragment = scored[:fragment]
|
|
152
|
+
score = scored[:score]
|
|
153
|
+
lines << "- #{fragment_name(fragment)} (#{(score * 100).round(0)}%)"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
lines.join("\n")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Check if optimization is enabled
|
|
160
|
+
#
|
|
161
|
+
# @return [Boolean] True if optimizer is available
|
|
162
|
+
def optimization_enabled?
|
|
163
|
+
!@optimizer.nil?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Get optimizer statistics
|
|
167
|
+
#
|
|
168
|
+
# @return [Hash, nil] Statistics hash or nil if optimizer not available
|
|
169
|
+
def optimizer_stats
|
|
170
|
+
@optimizer&.statistics
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
|
|
175
|
+
# Get human-readable name for a fragment
|
|
176
|
+
def fragment_name(fragment)
|
|
177
|
+
if fragment.respond_to?(:heading)
|
|
178
|
+
fragment.heading
|
|
179
|
+
elsif fragment.respond_to?(:name)
|
|
180
|
+
fragment.name
|
|
181
|
+
elsif fragment.respond_to?(:id)
|
|
182
|
+
fragment.id
|
|
183
|
+
else
|
|
184
|
+
"Unknown fragment"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
60
187
|
end
|
|
61
188
|
end
|
|
62
189
|
end
|