aidp 0.27.0 โ 0.28.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 +89 -0
- data/lib/aidp/cli/models_command.rb +5 -6
- data/lib/aidp/cli.rb +10 -8
- data/lib/aidp/config.rb +54 -0
- data/lib/aidp/debug_mixin.rb +23 -1
- data/lib/aidp/execute/agent_signal_parser.rb +22 -0
- data/lib/aidp/execute/repl_macros.rb +2 -2
- data/lib/aidp/execute/steps.rb +94 -1
- data/lib/aidp/execute/work_loop_runner.rb +209 -17
- data/lib/aidp/execute/workflow_selector.rb +2 -25
- data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
- data/lib/aidp/harness/ai_decision_engine.rb +35 -2
- data/lib/aidp/harness/config_manager.rb +0 -5
- data/lib/aidp/harness/config_schema.rb +8 -0
- data/lib/aidp/harness/configuration.rb +27 -19
- data/lib/aidp/harness/enhanced_runner.rb +1 -4
- data/lib/aidp/harness/error_handler.rb +1 -72
- data/lib/aidp/harness/provider_factory.rb +11 -2
- data/lib/aidp/harness/state_manager.rb +0 -7
- data/lib/aidp/harness/thinking_depth_manager.rb +47 -68
- data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
- data/lib/aidp/harness/ui/progress_display.rb +6 -2
- data/lib/aidp/harness/user_interface.rb +0 -58
- data/lib/aidp/init/runner.rb +7 -2
- data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
- data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
- data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
- data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
- data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
- data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
- data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
- data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
- data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
- data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
- data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
- data/lib/aidp/planning/parsers/document_parser.rb +141 -0
- data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
- data/lib/aidp/provider_manager.rb +8 -32
- data/lib/aidp/providers/aider.rb +264 -0
- data/lib/aidp/providers/anthropic.rb +74 -2
- data/lib/aidp/providers/base.rb +25 -1
- data/lib/aidp/providers/codex.rb +26 -3
- data/lib/aidp/providers/cursor.rb +16 -0
- data/lib/aidp/providers/gemini.rb +13 -0
- data/lib/aidp/providers/github_copilot.rb +17 -0
- data/lib/aidp/providers/kilocode.rb +11 -0
- data/lib/aidp/providers/opencode.rb +11 -0
- data/lib/aidp/setup/wizard.rb +249 -39
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +211 -30
- data/lib/aidp/watch/change_request_processor.rb +128 -14
- data/lib/aidp/watch/ci_fix_processor.rb +103 -37
- data/lib/aidp/watch/ci_log_extractor.rb +258 -0
- data/lib/aidp/watch/github_state_extractor.rb +177 -0
- data/lib/aidp/watch/implementation_verifier.rb +284 -0
- data/lib/aidp/watch/plan_generator.rb +7 -43
- data/lib/aidp/watch/plan_processor.rb +7 -6
- data/lib/aidp/watch/repository_client.rb +245 -17
- data/lib/aidp/watch/review_processor.rb +98 -17
- data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
- data/lib/aidp/watch/runner.rb +181 -29
- data/lib/aidp/watch/state_store.rb +22 -1
- data/lib/aidp/workflows/definitions.rb +147 -0
- data/lib/aidp/workstream_cleanup.rb +245 -0
- data/lib/aidp/worktree.rb +19 -0
- data/templates/aidp.yml.example +57 -0
- data/templates/implementation/generate_tdd_specs.md +213 -0
- data/templates/implementation/iterative_implementation.md +122 -0
- data/templates/planning/agile/analyze_feedback.md +183 -0
- data/templates/planning/agile/generate_iteration_plan.md +179 -0
- data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
- data/templates/planning/agile/generate_marketing_report.md +162 -0
- data/templates/planning/agile/generate_mvp_scope.md +127 -0
- data/templates/planning/agile/generate_user_test_plan.md +143 -0
- data/templates/planning/agile/ingest_feedback.md +174 -0
- data/templates/planning/assemble_project_plan.md +113 -0
- data/templates/planning/assign_personas.md +108 -0
- data/templates/planning/create_tasks.md +52 -6
- data/templates/planning/generate_gantt.md +86 -0
- data/templates/planning/generate_wbs.md +85 -0
- data/templates/planning/initialize_planning_mode.md +70 -0
- data/templates/skills/README.md +2 -2
- data/templates/skills/marketing_strategist/SKILL.md +279 -0
- data/templates/skills/product_manager/SKILL.md +177 -0
- data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
- data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
- data/templates/skills/ux_researcher/SKILL.md +222 -0
- metadata +39 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "worktree"
|
|
4
|
+
require_relative "workstream_state"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "tty-prompt"
|
|
7
|
+
|
|
8
|
+
module Aidp
|
|
9
|
+
# Service for interactively cleaning up inactive workstreams
|
|
10
|
+
# Displays comprehensive status and prompts user for deletion decisions
|
|
11
|
+
class WorkstreamCleanup
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
|
|
14
|
+
def initialize(project_dir: Dir.pwd, prompt: TTY::Prompt.new)
|
|
15
|
+
@project_dir = project_dir
|
|
16
|
+
@prompt = prompt
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Run interactive cleanup workflow
|
|
20
|
+
def run
|
|
21
|
+
Aidp.log_debug("workstream_cleanup", "start", project_dir: @project_dir)
|
|
22
|
+
|
|
23
|
+
workstreams = Aidp::Worktree.list(project_dir: @project_dir)
|
|
24
|
+
|
|
25
|
+
if workstreams.empty?
|
|
26
|
+
@prompt.say("No workstreams found.")
|
|
27
|
+
Aidp.log_debug("workstream_cleanup", "no_workstreams")
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@prompt.say("Found #{workstreams.size} workstream(s)\n")
|
|
32
|
+
|
|
33
|
+
workstreams.each do |ws|
|
|
34
|
+
process_workstream(ws)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@prompt.say("\nโ Cleanup complete")
|
|
38
|
+
Aidp.log_debug("workstream_cleanup", "complete")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def process_workstream(ws)
|
|
44
|
+
Aidp.log_debug("workstream_cleanup", "process", slug: ws[:slug])
|
|
45
|
+
|
|
46
|
+
status = gather_status(ws)
|
|
47
|
+
display_status(ws, status)
|
|
48
|
+
|
|
49
|
+
choice = prompt_action(ws, status)
|
|
50
|
+
execute_action(ws, choice, status)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def gather_status(ws)
|
|
54
|
+
Aidp.log_debug("workstream_cleanup", "gather_status", slug: ws[:slug])
|
|
55
|
+
|
|
56
|
+
status = {
|
|
57
|
+
exists: ws[:active],
|
|
58
|
+
state: Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: @project_dir) || {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return status unless status[:exists]
|
|
62
|
+
|
|
63
|
+
# Gather git status information
|
|
64
|
+
Dir.chdir(ws[:path]) do
|
|
65
|
+
status[:uncommitted_changes] = uncommitted_changes?
|
|
66
|
+
status[:unpushed_commits] = unpushed_commits?
|
|
67
|
+
status[:upstream_exists] = upstream_exists?
|
|
68
|
+
status[:last_commit_date] = last_commit_date
|
|
69
|
+
status[:behind_upstream] = behind_upstream? if status[:upstream_exists]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
status
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def uncommitted_changes?
|
|
76
|
+
stdout, _stderr, status = Open3.capture3("git", "status", "--porcelain")
|
|
77
|
+
status.success? && !stdout.strip.empty?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def unpushed_commits?
|
|
81
|
+
# Check if there are commits not in the upstream branch
|
|
82
|
+
stdout, _stderr, status = Open3.capture3("git", "log", "@{upstream}..", "--oneline")
|
|
83
|
+
status.success? && !stdout.strip.empty?
|
|
84
|
+
rescue
|
|
85
|
+
# If @{upstream} doesn't exist, check for any commits
|
|
86
|
+
false
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def upstream_exists?
|
|
90
|
+
_stdout, _stderr, status = Open3.capture3("git", "rev-parse", "--abbrev-ref", "@{upstream}")
|
|
91
|
+
status.success?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def behind_upstream?
|
|
95
|
+
stdout, _stderr, status = Open3.capture3("git", "log", "..@{upstream}", "--oneline")
|
|
96
|
+
status.success? && !stdout.strip.empty?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def last_commit_date
|
|
100
|
+
stdout, _stderr, status = Open3.capture3("git", "log", "-1", "--format=%ci")
|
|
101
|
+
status.success? ? stdout.strip : nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def display_status(ws, status)
|
|
105
|
+
@prompt.say("\n" + "=" * 60)
|
|
106
|
+
@prompt.say("Workstream: #{ws[:slug]}")
|
|
107
|
+
@prompt.say("=" * 60)
|
|
108
|
+
@prompt.say("Branch: #{ws[:branch]}")
|
|
109
|
+
@prompt.say("Created: #{ws[:created_at]}")
|
|
110
|
+
@prompt.say("Status: #{status[:state][:status] || "unknown"}")
|
|
111
|
+
@prompt.say("Iterations: #{status[:state][:iterations] || 0}")
|
|
112
|
+
|
|
113
|
+
if status[:state][:task]
|
|
114
|
+
@prompt.say("Task: #{status[:state][:task]}")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
unless status[:exists]
|
|
118
|
+
@prompt.say("\nโ ๏ธ Worktree directory does not exist")
|
|
119
|
+
return
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Display git status
|
|
123
|
+
@prompt.say("\nGit Status:")
|
|
124
|
+
@prompt.say(" Uncommitted changes: #{status[:uncommitted_changes] ? "Yes" : "No"}")
|
|
125
|
+
|
|
126
|
+
if status[:upstream_exists]
|
|
127
|
+
@prompt.say(" Upstream: exists")
|
|
128
|
+
@prompt.say(" Unpushed commits: #{status[:unpushed_commits] ? "Yes" : "No"}")
|
|
129
|
+
@prompt.say(" Behind upstream: #{status[:behind_upstream] ? "Yes" : "No"}")
|
|
130
|
+
else
|
|
131
|
+
@prompt.say(" Upstream: none (local branch)")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if status[:last_commit_date]
|
|
135
|
+
@prompt.say(" Last commit: #{status[:last_commit_date]}")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def prompt_action(ws, status)
|
|
140
|
+
Aidp.log_debug("workstream_cleanup", "prompt_action", slug: ws[:slug])
|
|
141
|
+
|
|
142
|
+
choices = build_choices(ws, status)
|
|
143
|
+
@prompt.select("\nWhat would you like to do?", choices, per_page: 10)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def build_choices(ws, status)
|
|
147
|
+
choices = [
|
|
148
|
+
{name: "Keep (skip)", value: :keep},
|
|
149
|
+
{name: "Delete worktree only", value: :delete_worktree}
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
if status[:exists]
|
|
153
|
+
choices << if has_risk_factors?(status)
|
|
154
|
+
{name: "Delete worktree and local branch (has uncommitted/unpushed work!)", value: :delete_all}
|
|
155
|
+
else
|
|
156
|
+
{name: "Delete worktree and local branch", value: :delete_all}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
if status[:upstream_exists]
|
|
160
|
+
choices << {name: "Delete worktree, local branch, and remote branch", value: :delete_all_remote}
|
|
161
|
+
end
|
|
162
|
+
else
|
|
163
|
+
choices << {name: "Delete registration (worktree already gone)", value: :delete_worktree}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
choices
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def has_risk_factors?(status)
|
|
170
|
+
status[:uncommitted_changes] || status[:unpushed_commits]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def execute_action(ws, choice, status)
|
|
174
|
+
Aidp.log_debug("workstream_cleanup", "execute_action", slug: ws[:slug], action: choice)
|
|
175
|
+
|
|
176
|
+
case choice
|
|
177
|
+
when :keep
|
|
178
|
+
@prompt.say("Keeping workstream")
|
|
179
|
+
when :delete_worktree
|
|
180
|
+
delete_worktree(ws, delete_branch: false)
|
|
181
|
+
when :delete_all
|
|
182
|
+
if confirm_deletion(ws, status, remote: false)
|
|
183
|
+
delete_worktree(ws, delete_branch: true)
|
|
184
|
+
else
|
|
185
|
+
@prompt.say("Deletion cancelled")
|
|
186
|
+
end
|
|
187
|
+
when :delete_all_remote
|
|
188
|
+
if confirm_deletion(ws, status, remote: true)
|
|
189
|
+
delete_remote_branch(ws)
|
|
190
|
+
delete_worktree(ws, delete_branch: true)
|
|
191
|
+
else
|
|
192
|
+
@prompt.say("Deletion cancelled")
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def confirm_deletion(ws, status, remote:)
|
|
198
|
+
if has_risk_factors?(status)
|
|
199
|
+
warning = "โ ๏ธ WARNING: This workstream has uncommitted changes or unpushed commits!"
|
|
200
|
+
@prompt.say("\n#{warning}")
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
message = if remote
|
|
204
|
+
"Delete worktree, local branch, AND remote branch for '#{ws[:slug]}'?"
|
|
205
|
+
else
|
|
206
|
+
"Delete worktree and local branch for '#{ws[:slug]}'?"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
@prompt.yes?(message)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def delete_remote_branch(ws)
|
|
213
|
+
Aidp.log_debug("workstream_cleanup", "delete_remote_branch", slug: ws[:slug], branch: ws[:branch])
|
|
214
|
+
|
|
215
|
+
# Extract remote and branch name
|
|
216
|
+
# Branch format is typically "aidp/slug", we need to push to origin
|
|
217
|
+
Dir.chdir(@project_dir) do
|
|
218
|
+
_, stderr, status = Open3.capture3("git", "push", "origin", "--delete", ws[:branch])
|
|
219
|
+
if status.success?
|
|
220
|
+
@prompt.say("โ Deleted remote branch: #{ws[:branch]}")
|
|
221
|
+
else
|
|
222
|
+
@prompt.say("โ ๏ธ Failed to delete remote branch: #{stderr.strip}")
|
|
223
|
+
Aidp.log_debug("workstream_cleanup", "delete_remote_failed", branch: ws[:branch], error: stderr.strip)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def delete_worktree(ws, delete_branch:)
|
|
229
|
+
Aidp.log_debug("workstream_cleanup", "delete_worktree", slug: ws[:slug], delete_branch: delete_branch)
|
|
230
|
+
|
|
231
|
+
begin
|
|
232
|
+
Aidp::Worktree.remove(
|
|
233
|
+
slug: ws[:slug],
|
|
234
|
+
project_dir: @project_dir,
|
|
235
|
+
delete_branch: delete_branch
|
|
236
|
+
)
|
|
237
|
+
@prompt.say("โ Deleted workstream: #{ws[:slug]}")
|
|
238
|
+
@prompt.say(" Branch deleted") if delete_branch
|
|
239
|
+
rescue Aidp::Worktree::Error => e
|
|
240
|
+
@prompt.say("โ Error: #{e.message}")
|
|
241
|
+
Aidp.log_error("workstream_cleanup", "delete_failed", slug: ws[:slug], error: e.message)
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
data/lib/aidp/worktree.rb
CHANGED
|
@@ -141,6 +141,25 @@ module Aidp
|
|
|
141
141
|
!info(slug: slug, project_dir: project_dir).nil?
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
# Find a worktree by branch name
|
|
145
|
+
#
|
|
146
|
+
# @param branch [String] Branch name to search for
|
|
147
|
+
# @param project_dir [String] Project root directory
|
|
148
|
+
# @return [Hash, nil] Worktree info or nil if not found
|
|
149
|
+
def find_by_branch(branch:, project_dir: Dir.pwd)
|
|
150
|
+
registry = load_registry(project_dir)
|
|
151
|
+
slug, data = registry.find { |_slug, info| info["branch"] == branch }
|
|
152
|
+
return nil unless data
|
|
153
|
+
|
|
154
|
+
{
|
|
155
|
+
slug: slug,
|
|
156
|
+
path: data["path"],
|
|
157
|
+
branch: data["branch"],
|
|
158
|
+
created_at: data["created_at"],
|
|
159
|
+
active: Dir.exist?(data["path"])
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
144
163
|
private
|
|
145
164
|
|
|
146
165
|
# Ensure we're in a git repository
|
data/templates/aidp.yml.example
CHANGED
|
@@ -239,6 +239,25 @@ providers:
|
|
|
239
239
|
flags: ["--precise"]
|
|
240
240
|
timeout: 900 # 15 minutes
|
|
241
241
|
|
|
242
|
+
# Thinking tier model configuration
|
|
243
|
+
# Maps thinking depth tiers to specific models for this provider
|
|
244
|
+
thinking_tiers:
|
|
245
|
+
mini:
|
|
246
|
+
models:
|
|
247
|
+
- cursor-fast # Fast model for simple tasks
|
|
248
|
+
standard:
|
|
249
|
+
models:
|
|
250
|
+
- cursor-default # Default model for standard tasks
|
|
251
|
+
thinking:
|
|
252
|
+
models:
|
|
253
|
+
- cursor-precise # Precise model for complex reasoning
|
|
254
|
+
pro:
|
|
255
|
+
models:
|
|
256
|
+
- cursor-precise # Use best model for pro tier
|
|
257
|
+
max:
|
|
258
|
+
models:
|
|
259
|
+
- cursor-precise # Use best model for max tier
|
|
260
|
+
|
|
242
261
|
# Provider features
|
|
243
262
|
features:
|
|
244
263
|
file_upload: true
|
|
@@ -343,6 +362,25 @@ providers:
|
|
|
343
362
|
max_tokens: 200000
|
|
344
363
|
timeout: 600 # 10 minutes
|
|
345
364
|
|
|
365
|
+
# Thinking tier model configuration
|
|
366
|
+
# Maps thinking depth tiers to specific models for this provider
|
|
367
|
+
thinking_tiers:
|
|
368
|
+
mini:
|
|
369
|
+
models:
|
|
370
|
+
- claude-3-5-haiku-20241022 # Fastest, cheapest model
|
|
371
|
+
standard:
|
|
372
|
+
models:
|
|
373
|
+
- claude-3-5-sonnet-20241022 # Balanced performance
|
|
374
|
+
thinking:
|
|
375
|
+
models:
|
|
376
|
+
- claude-3-5-sonnet-20241022 # Best balance for complex tasks
|
|
377
|
+
pro:
|
|
378
|
+
models:
|
|
379
|
+
- claude-3-opus-20240229 # Most capable model
|
|
380
|
+
max:
|
|
381
|
+
models:
|
|
382
|
+
- claude-3-opus-20240229 # Maximum capability
|
|
383
|
+
|
|
346
384
|
# Authentication configuration
|
|
347
385
|
auth:
|
|
348
386
|
api_key_env: "ANTHROPIC_API_KEY" # Environment variable for API key
|
|
@@ -394,6 +432,25 @@ providers:
|
|
|
394
432
|
max_tokens: 30000
|
|
395
433
|
timeout: 300 # 5 minutes
|
|
396
434
|
|
|
435
|
+
# Thinking tier model configuration
|
|
436
|
+
# Maps thinking depth tiers to specific models for this provider
|
|
437
|
+
thinking_tiers:
|
|
438
|
+
mini:
|
|
439
|
+
models:
|
|
440
|
+
- gemini-1.5-flash # Fast model for simple tasks
|
|
441
|
+
standard:
|
|
442
|
+
models:
|
|
443
|
+
- gemini-1.5-pro # Most capable model
|
|
444
|
+
thinking:
|
|
445
|
+
models:
|
|
446
|
+
- gemini-1.5-pro # Best model for complex reasoning
|
|
447
|
+
pro:
|
|
448
|
+
models:
|
|
449
|
+
- gemini-1.5-pro # Use best available
|
|
450
|
+
max:
|
|
451
|
+
models:
|
|
452
|
+
- gemini-1.5-pro # Use best available
|
|
453
|
+
|
|
397
454
|
# Authentication configuration
|
|
398
455
|
auth:
|
|
399
456
|
api_key_env: "GEMINI_API_KEY" # Environment variable for API key
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Generate TDD Test Specifications
|
|
2
|
+
|
|
3
|
+
You are creating **test specifications** following Test-Driven Development (TDD) principles.
|
|
4
|
+
|
|
5
|
+
## TDD Philosophy
|
|
6
|
+
|
|
7
|
+
### RED โ GREEN โ REFACTOR
|
|
8
|
+
|
|
9
|
+
1. **RED**: Write failing tests that specify desired behavior
|
|
10
|
+
2. **GREEN**: Write minimal code to make tests pass
|
|
11
|
+
3. **REFACTOR**: Improve code while keeping tests green
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
Read these artifacts to understand requirements:
|
|
16
|
+
|
|
17
|
+
- `.aidp/docs/PRD.md` - Product requirements (if exists)
|
|
18
|
+
- `.aidp/docs/TECH_DESIGN.md` - Technical design (if exists)
|
|
19
|
+
- `.aidp/docs/TASK_LIST.md` - Task breakdown (if exists)
|
|
20
|
+
- `.aidp/docs/WBS.md` - Work breakdown structure (if exists)
|
|
21
|
+
|
|
22
|
+
## Your Task
|
|
23
|
+
|
|
24
|
+
Generate comprehensive test specifications BEFORE implementation.
|
|
25
|
+
|
|
26
|
+
## Test Specification Structure
|
|
27
|
+
|
|
28
|
+
Create `docs/tdd_specifications.md`:
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
# TDD Test Specifications
|
|
32
|
+
|
|
33
|
+
Generated: <timestamp>
|
|
34
|
+
|
|
35
|
+
## Overview
|
|
36
|
+
|
|
37
|
+
This document defines tests to write BEFORE implementing features.
|
|
38
|
+
Follow TDD: write tests first, watch them fail, then implement.
|
|
39
|
+
|
|
40
|
+
## Test Categories
|
|
41
|
+
|
|
42
|
+
### Unit Tests
|
|
43
|
+
|
|
44
|
+
#### Feature: [Feature Name]
|
|
45
|
+
|
|
46
|
+
**Test Cases:**
|
|
47
|
+
1. **should handle valid input**
|
|
48
|
+
- Given: Valid input parameters
|
|
49
|
+
- When: Feature is invoked
|
|
50
|
+
- Then: Returns expected output
|
|
51
|
+
- Status: โญ Not yet written
|
|
52
|
+
|
|
53
|
+
2. **should reject invalid input**
|
|
54
|
+
- Given: Invalid parameters
|
|
55
|
+
- When: Feature is invoked
|
|
56
|
+
- Then: Raises appropriate error
|
|
57
|
+
- Status: โญ Not yet written
|
|
58
|
+
|
|
59
|
+
3. **should handle edge cases**
|
|
60
|
+
- Given: Edge case inputs (empty, nil, boundary values)
|
|
61
|
+
- When: Feature is invoked
|
|
62
|
+
- Then: Handles gracefully
|
|
63
|
+
- Status: โญ Not yet written
|
|
64
|
+
|
|
65
|
+
### Integration Tests
|
|
66
|
+
|
|
67
|
+
#### Integration: [Component A + Component B]
|
|
68
|
+
|
|
69
|
+
**Test Cases:**
|
|
70
|
+
1. **should integrate successfully**
|
|
71
|
+
- Given: Both components configured
|
|
72
|
+
- When: Integration point is called
|
|
73
|
+
- Then: Data flows correctly
|
|
74
|
+
- Status: โญ Not yet written
|
|
75
|
+
|
|
76
|
+
### Acceptance Tests
|
|
77
|
+
|
|
78
|
+
#### User Story: [As a user, I want to...]
|
|
79
|
+
|
|
80
|
+
**Test Cases:**
|
|
81
|
+
1. **should complete user workflow**
|
|
82
|
+
- Given: User starts workflow
|
|
83
|
+
- When: User performs actions
|
|
84
|
+
- Then: Achieves expected outcome
|
|
85
|
+
- Status: โญ Not yet written
|
|
86
|
+
|
|
87
|
+
## Test Implementation Order
|
|
88
|
+
|
|
89
|
+
1. **Phase 1: Critical Path**
|
|
90
|
+
- Write tests for core functionality first
|
|
91
|
+
- Focus on happy path
|
|
92
|
+
- Estimated: X hours
|
|
93
|
+
|
|
94
|
+
2. **Phase 2: Edge Cases**
|
|
95
|
+
- Add tests for error conditions
|
|
96
|
+
- Boundary testing
|
|
97
|
+
- Estimated: Y hours
|
|
98
|
+
|
|
99
|
+
3. **Phase 3: Integration**
|
|
100
|
+
- Test component interactions
|
|
101
|
+
- End-to-end workflows
|
|
102
|
+
- Estimated: Z hours
|
|
103
|
+
|
|
104
|
+
## Coverage Goals
|
|
105
|
+
|
|
106
|
+
- **Unit Tests:** 90%+ coverage
|
|
107
|
+
- **Integration Tests:** All integration points
|
|
108
|
+
- **Acceptance Tests:** All user stories from PRD
|
|
109
|
+
|
|
110
|
+
## Notes
|
|
111
|
+
|
|
112
|
+
- Tests should be **fast** (< 1s per test ideal)
|
|
113
|
+
- Tests should be **isolated** (no shared state)
|
|
114
|
+
- Tests should be **deterministic** (same result every time)
|
|
115
|
+
- Mock external dependencies (APIs, databases, file I/O)
|
|
116
|
+
- Test behavior, not implementation
|
|
117
|
+
|
|
118
|
+
## Next Steps
|
|
119
|
+
|
|
120
|
+
1. Review this specification
|
|
121
|
+
2. Write tests (they should fail - RED)
|
|
122
|
+
3. Implement minimal code (make tests pass - GREEN)
|
|
123
|
+
4. Refactor code (keep tests green - REFACTOR)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## TDD Best Practices
|
|
127
|
+
|
|
128
|
+
### 1. Test One Thing
|
|
129
|
+
|
|
130
|
+
Each test should verify ONE behavior.
|
|
131
|
+
|
|
132
|
+
โ BAD: Testing multiple behaviors in one test
|
|
133
|
+
โ
GOOD: Separate tests for separate behaviors
|
|
134
|
+
|
|
135
|
+
### 2. Use Descriptive Names
|
|
136
|
+
|
|
137
|
+
Test names should describe the behavior being tested, not implementation details.
|
|
138
|
+
|
|
139
|
+
### 3. Follow Given-When-Then
|
|
140
|
+
|
|
141
|
+
Structure tests clearly:
|
|
142
|
+
|
|
143
|
+
- **GIVEN**: Setup test data and preconditions
|
|
144
|
+
- **WHEN**: Execute the behavior being tested
|
|
145
|
+
- **THEN**: Verify expected outcomes
|
|
146
|
+
|
|
147
|
+
### 4. Mock External Dependencies
|
|
148
|
+
|
|
149
|
+
Don't hit real APIs, databases, or file systems in unit tests.
|
|
150
|
+
Use test doubles/mocks for external dependencies.
|
|
151
|
+
|
|
152
|
+
### 5. Test Behavior, Not Implementation
|
|
153
|
+
|
|
154
|
+
โ BAD: Testing that internal helper methods are called
|
|
155
|
+
โ
GOOD: Testing that public interface returns correct results
|
|
156
|
+
|
|
157
|
+
## Framework-Specific Implementation
|
|
158
|
+
|
|
159
|
+
**For language/framework-specific test generation, use the appropriate skill:**
|
|
160
|
+
|
|
161
|
+
- **Ruby/RSpec**: Use `ruby_rspec_tdd` skill for RSpec-specific test files, fixtures, and syntax
|
|
162
|
+
- **Python/pytest**: Use `python_pytest_tdd` skill for pytest-specific implementation
|
|
163
|
+
- **JavaScript/Jest**: Use `javascript_jest_tdd` skill for Jest-specific implementation
|
|
164
|
+
- **Other frameworks**: Use ZFC skill matching to find appropriate testing skill
|
|
165
|
+
|
|
166
|
+
## Skill Delegation
|
|
167
|
+
|
|
168
|
+
After creating the language-agnostic test specification above, delegate to the framework-specific skill to generate:
|
|
169
|
+
|
|
170
|
+
1. **Skeleton test files** in the framework's directory structure and naming conventions
|
|
171
|
+
2. **Test fixtures** or factories in the framework's format
|
|
172
|
+
3. **Test execution commands** for the specific framework
|
|
173
|
+
4. **Framework-specific examples** and best practices
|
|
174
|
+
|
|
175
|
+
**Example skill invocation:**
|
|
176
|
+
|
|
177
|
+
For a Ruby project using RSpec:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
Use the `ruby_rspec_tdd` skill to:
|
|
181
|
+
1. Generate skeleton RSpec test files in spec/ directory
|
|
182
|
+
2. Create FactoryBot factories or fixtures as needed
|
|
183
|
+
3. Provide RSpec-specific examples and matchers
|
|
184
|
+
4. Include bundle exec rspec execution commands
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
For a Python project using pytest:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
Use the `python_pytest_tdd` skill to:
|
|
191
|
+
1. Generate skeleton pytest test files in tests/ directory
|
|
192
|
+
2. Create pytest fixtures as needed
|
|
193
|
+
3. Provide pytest-specific examples and assertions
|
|
194
|
+
4. Include pytest execution commands
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Output Files
|
|
198
|
+
|
|
199
|
+
Generate:
|
|
200
|
+
|
|
201
|
+
1. **`docs/tdd_specifications.md`** - Framework-agnostic test specifications (as shown above)
|
|
202
|
+
2. **Framework-specific test files** - Via appropriate skill delegation
|
|
203
|
+
3. **Test data/fixtures** - Via appropriate skill delegation
|
|
204
|
+
|
|
205
|
+
## Remember
|
|
206
|
+
|
|
207
|
+
- **Write tests FIRST** - before any implementation
|
|
208
|
+
- **Watch tests FAIL** - ensure they actually test something
|
|
209
|
+
- **Write minimal code** - just enough to pass tests
|
|
210
|
+
- **Refactor** - improve code with confidence (tests protect you)
|
|
211
|
+
- **Use skills for framework-specific implementation** - keep this template language-agnostic
|
|
212
|
+
|
|
213
|
+
**TDD gives you confidence, better design, and executable documentation!** ๐งช
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Iterative Implementation
|
|
2
|
+
|
|
3
|
+
You are implementing a feature or fix within the AIDP work loop using an iterative, task-based approach.
|
|
4
|
+
|
|
5
|
+
## Your Mission
|
|
6
|
+
|
|
7
|
+
{{task_description}}
|
|
8
|
+
|
|
9
|
+
## Important Instructions
|
|
10
|
+
|
|
11
|
+
### 1. Break Down the Work
|
|
12
|
+
|
|
13
|
+
If this is a multi-step feature, **break it into concrete subtasks** using the persistent tasklist:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
File task: "Subtask description here" priority: high|medium|low tags: tag1,tag2
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
File task: "Add worktree lookup logic to find existing worktrees" priority: high tags: implementation,git
|
|
23
|
+
File task: "Implement worktree creation for PR branches" priority: high tags: implementation,git
|
|
24
|
+
File task: "Add comprehensive logging using Aidp.log_debug" priority: medium tags: observability
|
|
25
|
+
File task: "Add tests for worktree operations" priority: high tags: testing
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Implement One Subtask at a Time
|
|
29
|
+
|
|
30
|
+
**Focus on completing ONE subtask per iteration.** Keep changes minimal and focused.
|
|
31
|
+
|
|
32
|
+
- Read the pending tasks from the tasklist
|
|
33
|
+
- Pick the highest priority task that's ready to implement
|
|
34
|
+
- Implement it completely with tests
|
|
35
|
+
- Mark it done: `Update task: task_id_here status: done`
|
|
36
|
+
|
|
37
|
+
### 3. Request Next Iteration
|
|
38
|
+
|
|
39
|
+
When you've completed a subtask and there's more work:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
NEXT_UNIT: agentic
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This tells the work loop to continue with the next subtask after running tests/linters.
|
|
46
|
+
|
|
47
|
+
### 4. Track Progress in PROMPT.md
|
|
48
|
+
|
|
49
|
+
Update this file to:
|
|
50
|
+
|
|
51
|
+
- Remove completed items
|
|
52
|
+
- Show current status
|
|
53
|
+
- List what remains
|
|
54
|
+
|
|
55
|
+
### 5. Mark Complete When Done
|
|
56
|
+
|
|
57
|
+
When ALL work is complete (all subtasks done, tests passing):
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
STATUS: COMPLETE
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Completion Criteria
|
|
64
|
+
|
|
65
|
+
โ
All subtasks filed and completed
|
|
66
|
+
โ
All tests passing
|
|
67
|
+
โ
All linters passing
|
|
68
|
+
โ
Code follows project style guide
|
|
69
|
+
โ
Comprehensive logging added
|
|
70
|
+
โ
STATUS: COMPLETE added to PROMPT.md
|
|
71
|
+
|
|
72
|
+
## Context
|
|
73
|
+
|
|
74
|
+
{{additional_context}}
|
|
75
|
+
|
|
76
|
+
## Best Practices
|
|
77
|
+
|
|
78
|
+
- **Small iterations**: Better to do 5 small focused iterations than 1 giant one
|
|
79
|
+
- **Test as you go**: Write tests for each subtask before moving on
|
|
80
|
+
- **Use signals**: `File task:`, `Update task:`, `NEXT_UNIT:` keep the system coordinated
|
|
81
|
+
- **Log extensively**: Use `Aidp.log_debug()` at all important code paths
|
|
82
|
+
- **Fail fast**: Let bugs surface early rather than masking with rescues
|
|
83
|
+
|
|
84
|
+
## Example Flow
|
|
85
|
+
|
|
86
|
+
Iteration 1:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
File task: "Create WorktreeBranchManager class" priority: high tags: implementation
|
|
90
|
+
File task: "Add worktree lookup logic" priority: high tags: implementation
|
|
91
|
+
File task: "Add tests for WorktreeBranchManager" priority: high tags: testing
|
|
92
|
+
|
|
93
|
+
[Implement WorktreeBranchManager class with basic structure]
|
|
94
|
+
|
|
95
|
+
Update task: task_abc123 status: done
|
|
96
|
+
NEXT_UNIT: agentic
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Iteration 2:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
[Implement worktree lookup logic]
|
|
103
|
+
|
|
104
|
+
Update task: task_def456 status: done
|
|
105
|
+
NEXT_UNIT: agentic
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Iteration 3:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
[Add comprehensive tests]
|
|
112
|
+
|
|
113
|
+
Update task: task_ghi789 status: done
|
|
114
|
+
STATUS: COMPLETE
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Notes
|
|
118
|
+
|
|
119
|
+
- The work loop will automatically run tests/linters after each iteration
|
|
120
|
+
- If tests fail, you'll see the errors in the next iteration - fix them before continuing
|
|
121
|
+
- Use the persistent tasklist to coordinate work across sessions
|
|
122
|
+
- Each iteration should leave the codebase in a working state
|