aidp 0.22.0 → 0.24.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 +145 -31
- data/lib/aidp/cli.rb +19 -2
- data/lib/aidp/execute/work_loop_runner.rb +252 -45
- data/lib/aidp/execute/work_loop_unit_scheduler.rb +27 -2
- data/lib/aidp/harness/condition_detector.rb +42 -8
- data/lib/aidp/harness/config_manager.rb +7 -0
- data/lib/aidp/harness/config_schema.rb +25 -0
- data/lib/aidp/harness/configuration.rb +69 -6
- data/lib/aidp/harness/error_handler.rb +117 -44
- data/lib/aidp/harness/provider_manager.rb +64 -0
- data/lib/aidp/harness/provider_metrics.rb +138 -0
- data/lib/aidp/harness/runner.rb +110 -35
- data/lib/aidp/harness/simple_user_interface.rb +4 -0
- data/lib/aidp/harness/state/ui_state.rb +0 -10
- data/lib/aidp/harness/state_manager.rb +1 -15
- data/lib/aidp/harness/test_runner.rb +39 -2
- data/lib/aidp/logger.rb +34 -4
- data/lib/aidp/providers/adapter.rb +241 -0
- data/lib/aidp/providers/anthropic.rb +75 -7
- data/lib/aidp/providers/base.rb +29 -1
- data/lib/aidp/providers/capability_registry.rb +205 -0
- data/lib/aidp/providers/codex.rb +14 -0
- data/lib/aidp/providers/error_taxonomy.rb +195 -0
- data/lib/aidp/providers/gemini.rb +3 -2
- data/lib/aidp/setup/devcontainer/backup_manager.rb +11 -4
- data/lib/aidp/setup/provider_registry.rb +107 -0
- data/lib/aidp/setup/wizard.rb +189 -31
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +357 -27
- data/lib/aidp/watch/plan_generator.rb +16 -1
- data/lib/aidp/watch/plan_processor.rb +54 -3
- data/lib/aidp/watch/repository_client.rb +78 -4
- data/lib/aidp/watch/repository_safety_checker.rb +12 -3
- data/lib/aidp/watch/runner.rb +52 -10
- data/lib/aidp/workflows/guided_agent.rb +53 -0
- data/lib/aidp/worktree.rb +67 -10
- data/templates/work_loop/decide_whats_next.md +21 -0
- data/templates/work_loop/diagnose_failures.md +21 -0
- metadata +10 -3
- /data/{bin → exe}/aidp +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2ec07f1c36212b7ace8e467e098fc3bcd917dc0738e27125bbddcef9bbcc30e
|
|
4
|
+
data.tar.gz: edbc40f6c50b581729d185186eae3510e8c23885f12655c46868117af361f31a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6472e33b88cf45f0c0abc5131dd608ba20d73480074552275d6d886222357c435023845fa8762b7f527268ba34fc2d3841d504309c8c068adbc38d6ac41f12b
|
|
7
|
+
data.tar.gz: 4283ba15ee02985603adbdba0be476adec35efc9fa6730a01beef1eac50d068647f3560aa03d75fcdb24ffc102e742bc1c3d9ff53fe95662a20241c711b54ae7
|
data/README.md
CHANGED
|
@@ -48,8 +48,8 @@ AIDP provides first-class devcontainer support for sandboxed, secure AI agent ex
|
|
|
48
48
|
|
|
49
49
|
- **Network Security**: Strict firewall with allowlisted domains only
|
|
50
50
|
- **Sandboxed Environment**: Isolated from your host system
|
|
51
|
-
- **Elevated Permissions**: AI agents can run with full permissions inside the container
|
|
52
51
|
- **Consistent Setup**: Same environment across all developers
|
|
52
|
+
- **Automatic Management**: AIDP can generate and update your devcontainer configuration
|
|
53
53
|
|
|
54
54
|
### For AIDP Development
|
|
55
55
|
|
|
@@ -73,43 +73,54 @@ See [.devcontainer/README.md](.devcontainer/README.md) for complete documentatio
|
|
|
73
73
|
|
|
74
74
|
### Generating Devcontainers for Your Projects
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
AIDP can automatically generate and manage devcontainer configurations through the interactive wizard:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
#
|
|
80
|
-
aidp
|
|
79
|
+
# Launch the interactive configuration wizard
|
|
80
|
+
aidp config --interactive
|
|
81
81
|
|
|
82
|
-
#
|
|
83
|
-
#
|
|
82
|
+
# During the wizard, you'll be asked:
|
|
83
|
+
# - Whether you want AIDP to manage your devcontainer configuration
|
|
84
|
+
# - If you want to add custom ports beyond auto-detected ones
|
|
84
85
|
|
|
85
|
-
#
|
|
86
|
-
|
|
86
|
+
# The wizard will detect ports based on your project type and generate
|
|
87
|
+
# a complete devcontainer.json configuration
|
|
87
88
|
```
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
You can also manage devcontainer configuration manually:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
# .aidp/aidp.yml
|
|
94
|
+
devcontainer:
|
|
95
|
+
manage: true
|
|
96
|
+
custom_ports:
|
|
97
|
+
- number: 3000
|
|
98
|
+
label: "Application Server"
|
|
99
|
+
- number: 5432
|
|
100
|
+
label: "PostgreSQL"
|
|
101
|
+
```
|
|
90
102
|
|
|
91
|
-
|
|
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
|
|
103
|
+
Then apply the configuration:
|
|
95
104
|
|
|
96
|
-
|
|
105
|
+
```bash
|
|
106
|
+
# Preview changes
|
|
107
|
+
aidp devcontainer diff
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
# Apply configuration
|
|
110
|
+
aidp devcontainer apply
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
devcontainer:
|
|
103
|
-
enabled: true
|
|
104
|
-
full_permissions_when_in_devcontainer: true # Run all providers with full permissions
|
|
112
|
+
# List backups
|
|
113
|
+
aidp devcontainer list-backups
|
|
105
114
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
skip_permission_checks:
|
|
109
|
-
- claude # Adds --dangerously-skip-permissions for Claude Code
|
|
115
|
+
# Restore from backup
|
|
116
|
+
aidp devcontainer restore 0
|
|
110
117
|
```
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
See [docs/DEVELOPMENT_CONTAINER.md](docs/DEVELOPMENT_CONTAINER.md) for complete devcontainer management documentation.
|
|
120
|
+
|
|
121
|
+
### Devcontainer Detection
|
|
122
|
+
|
|
123
|
+
AIDP automatically detects when it's running inside a devcontainer and adjusts its behavior accordingly. This detection uses multiple heuristics including environment variables, filesystem markers, and cgroup information. See [DevcontainerDetector](lib/aidp/utils/devcontainer_detector.rb) for implementation details.
|
|
113
124
|
|
|
114
125
|
## Core Features
|
|
115
126
|
|
|
@@ -190,6 +201,101 @@ aidp ws rm issue-123-fix-auth --delete-branch
|
|
|
190
201
|
|
|
191
202
|
See [Workstreams Guide](docs/WORKSTREAMS.md) for detailed usage.
|
|
192
203
|
|
|
204
|
+
### Watch Mode (Automated GitHub Integration)
|
|
205
|
+
|
|
206
|
+
AIDP can automatically monitor GitHub repositories and respond to labeled issues, creating plans and executing implementations autonomously:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Start watch mode for a repository
|
|
210
|
+
aidp watch https://github.com/owner/repo/issues
|
|
211
|
+
|
|
212
|
+
# Optional: specify polling interval, provider, and verbose output
|
|
213
|
+
aidp watch owner/repo --interval 60 --provider claude --verbose
|
|
214
|
+
|
|
215
|
+
# Run a single cycle (useful for CI/testing)
|
|
216
|
+
aidp watch owner/repo --once
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Label Workflow:**
|
|
220
|
+
|
|
221
|
+
AIDP uses a smart label-based workflow to manage the lifecycle of automated issue resolution:
|
|
222
|
+
|
|
223
|
+
1. **Planning Phase** (`aidp-plan` label):
|
|
224
|
+
- Add this label to an issue to trigger plan generation
|
|
225
|
+
- AIDP generates an implementation plan with task breakdown and clarifying questions
|
|
226
|
+
- Posts the plan as a comment on the issue
|
|
227
|
+
- Automatically removes the `aidp-plan` label
|
|
228
|
+
|
|
229
|
+
2. **Review & Clarification**:
|
|
230
|
+
- **If questions exist**: AIDP adds `aidp-needs-input` label and waits for user response
|
|
231
|
+
- User responds to questions in a comment
|
|
232
|
+
- User manually removes `aidp-needs-input` and adds `aidp-build` to proceed
|
|
233
|
+
- **If no questions**: AIDP adds `aidp-ready` label, indicating it's ready to build
|
|
234
|
+
- User can review the plan before proceeding
|
|
235
|
+
- User manually adds `aidp-build` label when ready
|
|
236
|
+
|
|
237
|
+
3. **Implementation Phase** (`aidp-build` label):
|
|
238
|
+
- Triggers autonomous implementation via work loops
|
|
239
|
+
- Creates a feature branch and commits changes
|
|
240
|
+
- Runs tests and linters with automatic fixes
|
|
241
|
+
- **If clarification needed during implementation**:
|
|
242
|
+
- Posts clarification questions as a comment
|
|
243
|
+
- Automatically removes `aidp-build` label and adds `aidp-needs-input`
|
|
244
|
+
- Preserves work-in-progress for later resumption
|
|
245
|
+
- User responds to questions, then manually removes `aidp-needs-input` and re-adds `aidp-build`
|
|
246
|
+
- **On success**:
|
|
247
|
+
- Posts completion comment with summary
|
|
248
|
+
- Automatically removes the `aidp-build` label
|
|
249
|
+
|
|
250
|
+
**Customizable Labels:**
|
|
251
|
+
|
|
252
|
+
All label names are configurable to match your repository's existing label scheme. Configure via the interactive wizard or manually in `aidp.yml`:
|
|
253
|
+
|
|
254
|
+
```yaml
|
|
255
|
+
# .aidp/aidp.yml
|
|
256
|
+
watch:
|
|
257
|
+
labels:
|
|
258
|
+
plan_trigger: aidp-plan # Label to trigger plan generation
|
|
259
|
+
needs_input: aidp-needs-input # Label when plan needs user input
|
|
260
|
+
ready_to_build: aidp-ready # Label when plan is ready to build
|
|
261
|
+
build_trigger: aidp-build # Label to trigger implementation
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Run `aidp config --interactive` and enable watch mode to configure labels interactively.
|
|
265
|
+
|
|
266
|
+
**Safety Features:**
|
|
267
|
+
|
|
268
|
+
- **Public Repository Protection**: Disabled by default for public repos (require explicit opt-in)
|
|
269
|
+
- **Author Allowlist**: Restrict automation to trusted GitHub users only
|
|
270
|
+
- **Container Requirement**: Optionally require sandboxed environment
|
|
271
|
+
- **Force Override**: `--force` flag to bypass safety checks (dangerous!)
|
|
272
|
+
|
|
273
|
+
**Safety Configuration:**
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
# .aidp/aidp.yml
|
|
277
|
+
watch:
|
|
278
|
+
safety:
|
|
279
|
+
allow_public_repos: true # Required for public repositories
|
|
280
|
+
author_allowlist: # Only these users can trigger automation
|
|
281
|
+
- trusted-maintainer
|
|
282
|
+
- team-member
|
|
283
|
+
require_container: true # Require devcontainer/Docker environment
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Run `aidp config --interactive` and enable watch mode to configure safety settings interactively.
|
|
287
|
+
|
|
288
|
+
**Clarification Requests:**
|
|
289
|
+
|
|
290
|
+
AIDP can automatically request clarification when it needs more information during implementation. This works in both watch mode and interactive mode:
|
|
291
|
+
|
|
292
|
+
- **Watch Mode**: Posts clarification questions as a GitHub comment, updates labels to `aidp-needs-input`, and waits for user response
|
|
293
|
+
- **Interactive Mode**: Prompts the user directly in the terminal to answer questions before continuing
|
|
294
|
+
|
|
295
|
+
This ensures AIDP never gets stuck - if it needs more information, it will ask for it rather than making incorrect assumptions or failing silently.
|
|
296
|
+
|
|
297
|
+
See [Watch Mode Guide](docs/FULLY_AUTOMATIC_MODE.md) and [Watch Mode Safety](docs/WATCH_MODE_SAFETY.md) for complete documentation.
|
|
298
|
+
|
|
193
299
|
## Command Reference
|
|
194
300
|
|
|
195
301
|
### Copilot Mode
|
|
@@ -263,6 +369,20 @@ aidp ws rm <slug> --delete-branch # Also delete git branch
|
|
|
263
369
|
aidp ws rm <slug> --force # Skip confirmation
|
|
264
370
|
```
|
|
265
371
|
|
|
372
|
+
### Configuration Commands
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Interactive configuration wizard (recommended)
|
|
376
|
+
aidp config --interactive # Configure all settings including watch mode
|
|
377
|
+
|
|
378
|
+
# Legacy setup wizard
|
|
379
|
+
aidp --setup-config # Re-run basic setup wizard
|
|
380
|
+
|
|
381
|
+
# Help and version
|
|
382
|
+
aidp --help # Show all commands
|
|
383
|
+
aidp --version # Show version
|
|
384
|
+
```
|
|
385
|
+
|
|
266
386
|
### System Commands
|
|
267
387
|
|
|
268
388
|
```bash
|
|
@@ -275,11 +395,6 @@ aidp providers
|
|
|
275
395
|
# Harness state management
|
|
276
396
|
aidp harness status
|
|
277
397
|
aidp harness reset
|
|
278
|
-
|
|
279
|
-
# Configuration
|
|
280
|
-
aidp --setup-config # Re-run setup wizard
|
|
281
|
-
aidp --help # Show all commands
|
|
282
|
-
aidp --version # Show version
|
|
283
398
|
```
|
|
284
399
|
|
|
285
400
|
## AI Providers
|
|
@@ -291,7 +406,6 @@ AIDP intelligently manages multiple providers with automatic switching:
|
|
|
291
406
|
- **Cursor CLI** - IDE-integrated provider for code-specific tasks
|
|
292
407
|
- **Gemini CLI** - Google's Gemini command-line interface for general tasks
|
|
293
408
|
- **GitHub Copilot CLI** - GitHub's AI pair programmer command-line interface
|
|
294
|
-
- **macOS UI** - macOS-specific UI automation provider
|
|
295
409
|
- **OpenCode** - Alternative open-source code generation provider
|
|
296
410
|
|
|
297
411
|
The system automatically switches providers when:
|
data/lib/aidp/cli.rb
CHANGED
|
@@ -1231,7 +1231,7 @@ module Aidp
|
|
|
1231
1231
|
|
|
1232
1232
|
def run_watch_command(args)
|
|
1233
1233
|
if args.empty?
|
|
1234
|
-
display_message("Usage: aidp watch <issues_url> [--interval SECONDS] [--provider NAME] [--once] [--no-workstreams]", type: :info)
|
|
1234
|
+
display_message("Usage: aidp watch <issues_url> [--interval SECONDS] [--provider NAME] [--once] [--no-workstreams] [--force] [--verbose]", type: :info)
|
|
1235
1235
|
return
|
|
1236
1236
|
end
|
|
1237
1237
|
|
|
@@ -1240,6 +1240,8 @@ module Aidp
|
|
|
1240
1240
|
provider_name = nil
|
|
1241
1241
|
once = false
|
|
1242
1242
|
use_workstreams = true # Default to using workstreams
|
|
1243
|
+
force = false
|
|
1244
|
+
verbose = false
|
|
1243
1245
|
|
|
1244
1246
|
until args.empty?
|
|
1245
1247
|
token = args.shift
|
|
@@ -1253,11 +1255,23 @@ module Aidp
|
|
|
1253
1255
|
once = true
|
|
1254
1256
|
when "--no-workstreams"
|
|
1255
1257
|
use_workstreams = false
|
|
1258
|
+
when "--force"
|
|
1259
|
+
force = true
|
|
1260
|
+
when "--verbose"
|
|
1261
|
+
verbose = true
|
|
1256
1262
|
else
|
|
1257
1263
|
display_message("⚠️ Unknown watch option: #{token}", type: :warn)
|
|
1258
1264
|
end
|
|
1259
1265
|
end
|
|
1260
1266
|
|
|
1267
|
+
# Initialize logger for watch mode
|
|
1268
|
+
setup_logging(Dir.pwd)
|
|
1269
|
+
|
|
1270
|
+
# Load watch safety configuration
|
|
1271
|
+
config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
|
|
1272
|
+
config = config_manager.config || {}
|
|
1273
|
+
watch_config = config[:watch] || config["watch"] || {}
|
|
1274
|
+
|
|
1261
1275
|
runner = Aidp::Watch::Runner.new(
|
|
1262
1276
|
issues_url: issues_url,
|
|
1263
1277
|
interval: interval.positive? ? interval : Aidp::Watch::Runner::DEFAULT_INTERVAL,
|
|
@@ -1265,7 +1279,10 @@ module Aidp
|
|
|
1265
1279
|
project_dir: Dir.pwd,
|
|
1266
1280
|
once: once,
|
|
1267
1281
|
use_workstreams: use_workstreams,
|
|
1268
|
-
prompt: create_prompt
|
|
1282
|
+
prompt: create_prompt,
|
|
1283
|
+
safety_config: watch_config,
|
|
1284
|
+
force: force,
|
|
1285
|
+
verbose: verbose
|
|
1269
1286
|
)
|
|
1270
1287
|
runner.start
|
|
1271
1288
|
rescue ArgumentError => e
|
|
@@ -77,7 +77,7 @@ module Aidp
|
|
|
77
77
|
display_guard_policy_status
|
|
78
78
|
display_pending_tasks
|
|
79
79
|
|
|
80
|
-
@unit_scheduler = WorkLoopUnitScheduler.new(units_config)
|
|
80
|
+
@unit_scheduler = WorkLoopUnitScheduler.new(units_config, project_dir: @project_dir)
|
|
81
81
|
base_context = context.dup
|
|
82
82
|
|
|
83
83
|
loop do
|
|
@@ -97,6 +97,8 @@ module Aidp
|
|
|
97
97
|
|
|
98
98
|
agentic_payload = if unit.name == :decide_whats_next
|
|
99
99
|
run_decider_agentic_unit(enriched_context)
|
|
100
|
+
elsif unit.name == :diagnose_failures
|
|
101
|
+
run_diagnose_agentic_unit(enriched_context)
|
|
100
102
|
else
|
|
101
103
|
run_primary_agentic_unit(step_spec, enriched_context)
|
|
102
104
|
end
|
|
@@ -148,7 +150,27 @@ module Aidp
|
|
|
148
150
|
transition_to(:ready) unless @current_state == :ready
|
|
149
151
|
|
|
150
152
|
transition_to(:apply_patch)
|
|
151
|
-
|
|
153
|
+
|
|
154
|
+
# Wrap agent call in exception handling for true fix-forward
|
|
155
|
+
begin
|
|
156
|
+
agent_result = apply_patch
|
|
157
|
+
rescue => e
|
|
158
|
+
# Convert exception to error result for fix-forward handling
|
|
159
|
+
Aidp.logger.error("work_loop", "Exception during agent call",
|
|
160
|
+
step: @step_name,
|
|
161
|
+
iteration: @iteration_count,
|
|
162
|
+
error: e.message,
|
|
163
|
+
error_class: e.class.name,
|
|
164
|
+
backtrace: e.backtrace&.first(5))
|
|
165
|
+
|
|
166
|
+
display_message(" ⚠️ Exception during agent call: #{e.class.name}: #{e.message}", type: :error)
|
|
167
|
+
|
|
168
|
+
# Append exception to PROMPT.md so agent can see and fix it
|
|
169
|
+
append_exception_to_prompt(e)
|
|
170
|
+
|
|
171
|
+
# Continue to next iteration with fix-forward pattern
|
|
172
|
+
next
|
|
173
|
+
end
|
|
152
174
|
|
|
153
175
|
# Process agent output for task filing signals
|
|
154
176
|
process_task_filing(agent_result)
|
|
@@ -223,6 +245,34 @@ module Aidp
|
|
|
223
245
|
)
|
|
224
246
|
end
|
|
225
247
|
|
|
248
|
+
def run_diagnose_agentic_unit(context)
|
|
249
|
+
Aidp.logger.info("work_loop", "Running diagnose_failures agentic unit", step: @step_name)
|
|
250
|
+
|
|
251
|
+
prompt = build_diagnose_prompt(context)
|
|
252
|
+
|
|
253
|
+
agent_result = @provider_manager.execute_with_provider(
|
|
254
|
+
@provider_manager.current_provider,
|
|
255
|
+
prompt,
|
|
256
|
+
{
|
|
257
|
+
step_name: @step_name,
|
|
258
|
+
iteration: @iteration_count,
|
|
259
|
+
project_dir: @project_dir,
|
|
260
|
+
mode: :diagnose_failures
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
requested = AgentSignalParser.extract_next_unit(agent_result[:output])
|
|
265
|
+
|
|
266
|
+
build_agentic_payload(
|
|
267
|
+
agent_result: agent_result,
|
|
268
|
+
response: agent_result,
|
|
269
|
+
summary: agent_result[:output],
|
|
270
|
+
completed: false,
|
|
271
|
+
terminate: false,
|
|
272
|
+
requested_next: requested
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
|
|
226
276
|
def units_config
|
|
227
277
|
if @config.respond_to?(:work_loop_units_config)
|
|
228
278
|
@config.work_loop_units_config
|
|
@@ -243,41 +293,94 @@ module Aidp
|
|
|
243
293
|
end
|
|
244
294
|
|
|
245
295
|
def build_decider_prompt(context)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
sections << ""
|
|
254
|
-
sections << "## Recent Deterministic Outputs"
|
|
255
|
-
|
|
256
|
-
if outputs.empty?
|
|
257
|
-
sections << "- None recorded yet."
|
|
258
|
-
else
|
|
259
|
-
outputs.each do |entry|
|
|
260
|
-
sections << "- #{entry[:name]} (status: #{entry[:status]}, finished_at: #{entry[:finished_at]})"
|
|
261
|
-
sections << " Output: #{entry[:output_path] || "n/a"}"
|
|
262
|
-
end
|
|
263
|
-
end
|
|
296
|
+
template = load_work_loop_template("decide_whats_next.md", default_decider_template)
|
|
297
|
+
replacements = {
|
|
298
|
+
"{{DETERMINISTIC_OUTPUTS}}" => format_deterministic_outputs(context[:deterministic_outputs]),
|
|
299
|
+
"{{PREVIOUS_AGENT_SUMMARY}}" => format_previous_agent_summary(context[:previous_agent_summary])
|
|
300
|
+
}
|
|
301
|
+
replacements.reduce(template) { |body, (token, value)| body.gsub(token, value) }
|
|
302
|
+
end
|
|
264
303
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
304
|
+
def build_diagnose_prompt(context)
|
|
305
|
+
template = load_work_loop_template("diagnose_failures.md", default_diagnose_template)
|
|
306
|
+
replacements = {
|
|
307
|
+
"{{DETERMINISTIC_OUTPUTS}}" => format_deterministic_outputs(context[:deterministic_outputs]),
|
|
308
|
+
"{{PREVIOUS_AGENT_SUMMARY}}" => format_previous_agent_summary(context[:previous_agent_summary])
|
|
309
|
+
}
|
|
310
|
+
replacements.reduce(template) { |body, (token, value)| body.gsub(token, value) }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def load_work_loop_template(relative_path, fallback)
|
|
314
|
+
template_path = File.join(@project_dir, "templates", "work_loop", relative_path)
|
|
315
|
+
return File.read(template_path) if File.exist?(template_path)
|
|
270
316
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
317
|
+
fallback
|
|
318
|
+
rescue => e
|
|
319
|
+
Aidp.logger.warn("work_loop", "Unable to load #{relative_path}", error: e.message)
|
|
320
|
+
fallback
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def default_decider_template
|
|
324
|
+
<<~TEMPLATE
|
|
325
|
+
# Decide Next Work Loop Unit
|
|
326
|
+
|
|
327
|
+
## Deterministic Outputs
|
|
328
|
+
|
|
329
|
+
{{DETERMINISTIC_OUTPUTS}}
|
|
330
|
+
|
|
331
|
+
## Previous Agent Summary
|
|
332
|
+
|
|
333
|
+
{{PREVIOUS_AGENT_SUMMARY}}
|
|
334
|
+
|
|
335
|
+
## Guidance
|
|
336
|
+
- Decide whether to run another deterministic unit or resume agentic editing.
|
|
337
|
+
- Announce your decision with `NEXT_UNIT: <unit_name>`.
|
|
338
|
+
- Valid values: names defined in configuration, `agentic`, or `wait_for_github`.
|
|
339
|
+
- Provide a concise rationale below.
|
|
279
340
|
|
|
280
|
-
|
|
341
|
+
## Rationale
|
|
342
|
+
TEMPLATE
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def default_diagnose_template
|
|
346
|
+
<<~TEMPLATE
|
|
347
|
+
# Diagnose Failures
|
|
348
|
+
|
|
349
|
+
## Recent Deterministic Outputs
|
|
350
|
+
|
|
351
|
+
{{DETERMINISTIC_OUTPUTS}}
|
|
352
|
+
|
|
353
|
+
## Previous Agent Summary
|
|
354
|
+
|
|
355
|
+
{{PREVIOUS_AGENT_SUMMARY}}
|
|
356
|
+
|
|
357
|
+
## Instructions
|
|
358
|
+
- Identify the root cause of the failures above.
|
|
359
|
+
- Recommend the next concrete action (another deterministic unit, agentic editing, or waiting).
|
|
360
|
+
- Emit `NEXT_UNIT: <unit_name>` on its own line.
|
|
361
|
+
|
|
362
|
+
## Analysis
|
|
363
|
+
TEMPLATE
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def format_deterministic_outputs(entries)
|
|
367
|
+
data = Array(entries)
|
|
368
|
+
return "- None recorded yet." if data.empty?
|
|
369
|
+
|
|
370
|
+
data.map do |entry|
|
|
371
|
+
name = entry[:name] || "unknown_unit"
|
|
372
|
+
status = entry[:status] || "unknown"
|
|
373
|
+
finished_at = entry[:finished_at]&.to_s || "unknown"
|
|
374
|
+
output = entry[:output_path] || "n/a"
|
|
375
|
+
"- #{name} (status: #{status}, finished_at: #{finished_at})\n Output: #{output}"
|
|
376
|
+
end.join("\n")
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def format_previous_agent_summary(summary)
|
|
380
|
+
content = summary.to_s.strip
|
|
381
|
+
return "_No previous agent summary._" if content.empty?
|
|
382
|
+
|
|
383
|
+
content
|
|
281
384
|
end
|
|
282
385
|
|
|
283
386
|
# Transition to a new state in the fix-forward state machine
|
|
@@ -544,16 +647,50 @@ module Aidp
|
|
|
544
647
|
prompt_content = @prompt_manager.read
|
|
545
648
|
return {status: "error", message: "PROMPT.md not found"} unless prompt_content
|
|
546
649
|
|
|
547
|
-
#
|
|
548
|
-
@
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
650
|
+
# Prepend work loop instructions to every iteration
|
|
651
|
+
full_prompt = build_work_loop_header(@step_name, @iteration_count) + "\n\n" + prompt_content
|
|
652
|
+
|
|
653
|
+
# CRITICAL: Change to project directory before calling provider
|
|
654
|
+
# This ensures Claude CLI runs in the correct directory and can create files
|
|
655
|
+
Dir.chdir(@project_dir) do
|
|
656
|
+
# Send to provider via provider_manager
|
|
657
|
+
@provider_manager.execute_with_provider(
|
|
658
|
+
@provider_manager.current_provider,
|
|
659
|
+
full_prompt,
|
|
660
|
+
{
|
|
661
|
+
step_name: @step_name,
|
|
662
|
+
iteration: @iteration_count,
|
|
663
|
+
project_dir: @project_dir
|
|
664
|
+
}
|
|
665
|
+
)
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def build_work_loop_header(step_name, iteration)
|
|
670
|
+
parts = []
|
|
671
|
+
parts << "# Work Loop: #{step_name} (Iteration #{iteration})"
|
|
672
|
+
parts << ""
|
|
673
|
+
parts << "## Instructions"
|
|
674
|
+
parts << "You are working in a work loop. Your responsibilities:"
|
|
675
|
+
parts << "1. Read the task description below to understand what needs to be done"
|
|
676
|
+
parts << "2. **Write/edit code files** to implement the required changes"
|
|
677
|
+
parts << "3. Run tests to verify your changes work correctly"
|
|
678
|
+
parts << "4. Update the task list in PROMPT.md as you complete items"
|
|
679
|
+
parts << "5. When ALL tasks are complete and tests pass, mark the step COMPLETE"
|
|
680
|
+
parts << ""
|
|
681
|
+
parts << "## Important Notes"
|
|
682
|
+
parts << "- You have full file system access - create and edit files as needed"
|
|
683
|
+
parts << "- The working directory is: #{@project_dir}"
|
|
684
|
+
parts << "- After you finish, tests and linters will run automatically"
|
|
685
|
+
parts << "- If tests/linters fail, you'll see the errors in the next iteration and can fix them"
|
|
686
|
+
parts << ""
|
|
687
|
+
parts << "## Completion Criteria"
|
|
688
|
+
parts << "Mark this step COMPLETE by adding this line to PROMPT.md:"
|
|
689
|
+
parts << "```"
|
|
690
|
+
parts << "STATUS: COMPLETE"
|
|
691
|
+
parts << "```"
|
|
692
|
+
parts << ""
|
|
693
|
+
parts.join("\n")
|
|
557
694
|
end
|
|
558
695
|
|
|
559
696
|
def prompt_marked_complete?
|
|
@@ -598,6 +735,9 @@ module Aidp
|
|
|
598
735
|
failures << ""
|
|
599
736
|
end
|
|
600
737
|
|
|
738
|
+
strategy = build_failure_strategy(test_results, lint_results)
|
|
739
|
+
failures.concat(strategy) unless strategy.empty?
|
|
740
|
+
|
|
601
741
|
failures << "**Fix-forward instructions**: Do not rollback changes. Build on what exists and fix the failures above."
|
|
602
742
|
failures << ""
|
|
603
743
|
|
|
@@ -608,7 +748,48 @@ module Aidp
|
|
|
608
748
|
updated_prompt = current_prompt + "\n\n---\n\n" + failures.join("\n")
|
|
609
749
|
@prompt_manager.write(updated_prompt, step_name: @step_name)
|
|
610
750
|
|
|
611
|
-
display_message(" [NEXT_PATCH] Added failure reports and diagnostic to PROMPT.md", type: :warning)
|
|
751
|
+
display_message(" [NEXT_PATCH] Added failure reports, strategy, and diagnostic to PROMPT.md", type: :warning)
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
# Append exception details to PROMPT.md for fix-forward handling
|
|
755
|
+
# This allows the agent to see and fix errors that occur during execution
|
|
756
|
+
def append_exception_to_prompt(exception)
|
|
757
|
+
error_report = []
|
|
758
|
+
|
|
759
|
+
error_report << "## Fix-Forward Exception in Iteration #{@iteration_count}"
|
|
760
|
+
error_report << ""
|
|
761
|
+
error_report << "**CRITICAL**: An exception occurred during this iteration. Please analyze and fix the underlying issue."
|
|
762
|
+
error_report << ""
|
|
763
|
+
error_report << "### Exception Details"
|
|
764
|
+
error_report << "- **Type**: `#{exception.class.name}`"
|
|
765
|
+
error_report << "- **Message**: #{exception.message}"
|
|
766
|
+
error_report << ""
|
|
767
|
+
|
|
768
|
+
if exception.backtrace && !exception.backtrace.empty?
|
|
769
|
+
error_report << "### Stack Trace (First 10 lines)"
|
|
770
|
+
error_report << "```"
|
|
771
|
+
exception.backtrace.first(10).each do |line|
|
|
772
|
+
error_report << line
|
|
773
|
+
end
|
|
774
|
+
error_report << "```"
|
|
775
|
+
error_report << ""
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
error_report << "### Required Action"
|
|
779
|
+
error_report << "1. Analyze the exception type and message"
|
|
780
|
+
error_report << "2. Review the stack trace to identify the source"
|
|
781
|
+
error_report << "3. Fix the underlying code issue"
|
|
782
|
+
error_report << "4. Ensure the fix doesn't break existing functionality"
|
|
783
|
+
error_report << ""
|
|
784
|
+
error_report << "**Fix-forward instructions**: Do not rollback changes. Identify the root cause and fix it in the next iteration."
|
|
785
|
+
error_report << ""
|
|
786
|
+
|
|
787
|
+
# Append to PROMPT.md
|
|
788
|
+
current_prompt = @prompt_manager.read
|
|
789
|
+
updated_prompt = current_prompt + "\n\n---\n\n" + error_report.join("\n")
|
|
790
|
+
@prompt_manager.write(updated_prompt, step_name: @step_name)
|
|
791
|
+
|
|
792
|
+
display_message(" [EXCEPTION] Added exception details to PROMPT.md for fix-forward", type: :error)
|
|
612
793
|
end
|
|
613
794
|
|
|
614
795
|
# Check if we should reinject the style guide at this iteration
|
|
@@ -651,6 +832,32 @@ module Aidp
|
|
|
651
832
|
reminder.join("\n")
|
|
652
833
|
end
|
|
653
834
|
|
|
835
|
+
def build_failure_strategy(test_results, lint_results)
|
|
836
|
+
return [] if test_results[:success] && lint_results[:success]
|
|
837
|
+
|
|
838
|
+
lines = ["### Recovery Strategy", ""]
|
|
839
|
+
|
|
840
|
+
unless test_results[:success]
|
|
841
|
+
commands = format_command_list(test_results[:failures])
|
|
842
|
+
lines << "- Re-run #{commands} locally to reproduce the failing specs listed above."
|
|
843
|
+
lines << "- Triage the exact failures before moving on to new work."
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
unless lint_results[:success]
|
|
847
|
+
commands = format_command_list(lint_results[:failures])
|
|
848
|
+
lines << "- Execute #{commands} and fix each reported offense."
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
lines << ""
|
|
852
|
+
lines
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def format_command_list(failures)
|
|
856
|
+
commands = Array(failures).map { |failure| failure[:command] }.compact
|
|
857
|
+
commands = ["the configured command"] if commands.empty?
|
|
858
|
+
commands.map { |cmd| "`#{cmd}`" }.join(" or ")
|
|
859
|
+
end
|
|
860
|
+
|
|
654
861
|
# Load current step's template content
|
|
655
862
|
def load_current_template
|
|
656
863
|
return nil unless @step_name
|