aidp 0.39.9 → 0.40.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 +32 -1
- data/lib/aidp/cli.rb +1 -0
- data/lib/aidp/harness/config_manager.rb +34 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +4 -2
- data/lib/aidp/setup/wizard.rb +33 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/workflows/guided_agent.rb +136 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2414ec5ab6a21100ba8ccb1d6ae8ddc6bb137a5403b069ff941707c17c9ce9fa
|
|
4
|
+
data.tar.gz: fb0401b3daf671719690b5c5347418eab335e8090dac38ebe08aef43a921430d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c033c034fed453eb4daa2108bb653f3da5007e0d49a792d4a3d9b4f976d63de3c796647da98d3131ea4b81d41425b7aea19ab4e94e743b2eb59936eea9dcad59
|
|
7
|
+
data.tar.gz: 70ebc3d5e2f809e21398321b7b26c315309a849a35ab4be5742d58650a123a4826c69e64c0b9e8fbc0aef7402da7a99a9970c0d49e83e516aab436e57187d974
|
data/README.md
CHANGED
|
@@ -42,6 +42,37 @@ You can re-run the wizard manually:
|
|
|
42
42
|
aidp --setup-config
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Run With Docker
|
|
46
|
+
|
|
47
|
+
Use the standalone Docker image when you want Aidp without a local Ruby
|
|
48
|
+
toolchain. Build the image once, then mount any project into `/workspace`.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
docker build -t aidp:local .
|
|
52
|
+
|
|
53
|
+
docker run --rm -it \
|
|
54
|
+
--env-file .env \
|
|
55
|
+
-v "$(pwd)":/workspace \
|
|
56
|
+
-v "$(pwd)/.aidp":/workspace/.aidp \
|
|
57
|
+
aidp:local
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Watch mode uses the same image:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
docker run --rm -it \
|
|
64
|
+
--env-file .env \
|
|
65
|
+
-v "$(pwd)":/workspace \
|
|
66
|
+
-v "$(pwd)/.aidp":/workspace/.aidp \
|
|
67
|
+
aidp:local watch https://github.com/<owner>/<repo>/issues
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If you supply `GH_TOKEN` or `GITHUB_TOKEN`, the image can use the bundled `gh`
|
|
71
|
+
CLI for read-only access to private repos. Public repositories continue to work
|
|
72
|
+
without GitHub credentials. See [docs/DOCKER_QUICKSTART.md](docs/DOCKER_QUICKSTART.md)
|
|
73
|
+
for environment variables, wrapper scripts, network options, and multi-arch
|
|
74
|
+
`buildx` examples.
|
|
75
|
+
|
|
45
76
|
## Devcontainer Support
|
|
46
77
|
|
|
47
78
|
AIDP provides first-class devcontainer support for sandboxed, secure AI agent execution. Devcontainers offer:
|
|
@@ -116,7 +147,7 @@ aidp devcontainer list-backups
|
|
|
116
147
|
aidp devcontainer restore 0
|
|
117
148
|
```
|
|
118
149
|
|
|
119
|
-
See [docs/how-to/DEVELOPMENT_CONTAINER.md](docs/how-to/DEVELOPMENT_CONTAINER.md) for complete devcontainer management documentation.
|
|
150
|
+
See [docs/how-to/DEVELOPMENT_CONTAINER.md](docs/how-to/DEVELOPMENT_CONTAINER.md) for complete devcontainer management documentation. If you do not want VS Code or a local Ruby toolchain, see [Run With Docker](#run-with-docker).
|
|
120
151
|
|
|
121
152
|
### Devcontainer Detection
|
|
122
153
|
|
data/lib/aidp/cli.rb
CHANGED
|
@@ -347,6 +347,7 @@ module Aidp
|
|
|
347
347
|
opts.on("-v", "--version", "Show version information") { options[:version] = true }
|
|
348
348
|
opts.on("--setup-config", "Setup or reconfigure config file") { options[:setup_config] = true }
|
|
349
349
|
opts.on("--verbose", "Show detailed prompts and raw provider responses during guided workflow") { options[:verbose] = true }
|
|
350
|
+
opts.on("--style STYLE", "Override PRD style for guided planning (detailed, balanced, quick_sketch)") { |style| options[:style] = style }
|
|
350
351
|
opts.on("--quiet", "Suppress non-critical output (incompatible with --verbose and --interactive)") { options[:quiet] = true }
|
|
351
352
|
# Undocumented: Quick exit launch test for CI/CD validation
|
|
352
353
|
opts.on("--launch-test", nil) { options[:launch_test] = true }
|
|
@@ -10,6 +10,16 @@ module Aidp
|
|
|
10
10
|
class ConfigManager
|
|
11
11
|
include ProviderTypeChecker
|
|
12
12
|
|
|
13
|
+
DEFAULT_PRD_GENERATION = {
|
|
14
|
+
interaction_style: "balanced",
|
|
15
|
+
default_style: "balanced",
|
|
16
|
+
max_question_rounds: {
|
|
17
|
+
detailed: 30,
|
|
18
|
+
balanced: 10,
|
|
19
|
+
quick_sketch: 3
|
|
20
|
+
}
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
13
23
|
attr_reader :project_dir
|
|
14
24
|
|
|
15
25
|
def initialize(project_dir = Dir.pwd)
|
|
@@ -195,6 +205,24 @@ module Aidp
|
|
|
195
205
|
}
|
|
196
206
|
end
|
|
197
207
|
|
|
208
|
+
def prd_generation_config(options = {})
|
|
209
|
+
config = config(options)
|
|
210
|
+
raw_config = config&.dig(:prd_generation) || config&.dig("prd_generation") || {}
|
|
211
|
+
raw_rounds = raw_config[:max_question_rounds] || raw_config["max_question_rounds"] || {}
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
interaction_style: raw_config[:interaction_style] || raw_config["interaction_style"] || DEFAULT_PRD_GENERATION[:interaction_style],
|
|
215
|
+
default_style: raw_config[:default_style] || raw_config["default_style"] || DEFAULT_PRD_GENERATION[:default_style],
|
|
216
|
+
max_question_rounds: DEFAULT_PRD_GENERATION[:max_question_rounds].merge(
|
|
217
|
+
symbolize_keys(raw_rounds)
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def prd_interaction_style(options = {})
|
|
223
|
+
prd_generation_config(options)[:interaction_style]
|
|
224
|
+
end
|
|
225
|
+
|
|
198
226
|
# Get provider models
|
|
199
227
|
def provider_models(provider_name, options = {})
|
|
200
228
|
provider_config = provider_config(provider_name, options)
|
|
@@ -351,6 +379,12 @@ module Aidp
|
|
|
351
379
|
|
|
352
380
|
private
|
|
353
381
|
|
|
382
|
+
def symbolize_keys(hash)
|
|
383
|
+
hash.each_with_object({}) do |(key, value), memo|
|
|
384
|
+
memo[key.to_sym] = value
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
354
388
|
def load_config_with_options(options)
|
|
355
389
|
# Apply different loading strategies based on options
|
|
356
390
|
if options[:mode]
|
|
@@ -237,10 +237,12 @@ module Aidp
|
|
|
237
237
|
def select_guided_workflow
|
|
238
238
|
# Use the guided agent to help user select workflow
|
|
239
239
|
# Don't pass prompt so it uses EnhancedInput with full readline support
|
|
240
|
-
|
|
240
|
+
cli_options = (defined?(Aidp::CLI) && Aidp::CLI.respond_to?(:last_options) && Aidp::CLI.last_options) ? Aidp::CLI.last_options : {}
|
|
241
|
+
verbose_flag = cli_options[:verbose]
|
|
242
|
+
interaction_style = cli_options[:style]
|
|
241
243
|
# Fallback: store verbose in an env for easier access if options not available
|
|
242
244
|
verbose = verbose_flag || ENV["AIDP_VERBOSE"] == "1"
|
|
243
|
-
guided_agent = Aidp::Workflows::GuidedAgent.new(@project_dir, verbose: verbose)
|
|
245
|
+
guided_agent = Aidp::Workflows::GuidedAgent.new(@project_dir, verbose: verbose, interaction_style: interaction_style)
|
|
244
246
|
result = guided_agent.select_workflow
|
|
245
247
|
|
|
246
248
|
# Store user input for later use
|
data/lib/aidp/setup/wizard.rb
CHANGED
|
@@ -23,6 +23,16 @@ module Aidp
|
|
|
23
23
|
LEGACY_TIER_ALIASES = {
|
|
24
24
|
advanced: :pro
|
|
25
25
|
}.freeze
|
|
26
|
+
PRD_STYLE_OPTIONS = {
|
|
27
|
+
"Detailed - deep questioning, longer sessions" => "detailed",
|
|
28
|
+
"Balanced - moderate questioning (recommended)" => "balanced",
|
|
29
|
+
"Quick Sketch - minimal questions, Aidp fills gaps automatically" => "quick_sketch"
|
|
30
|
+
}.freeze
|
|
31
|
+
DEFAULT_PRD_QUESTION_ROUNDS = {
|
|
32
|
+
detailed: 30,
|
|
33
|
+
balanced: 10,
|
|
34
|
+
quick_sketch: 3
|
|
35
|
+
}.freeze
|
|
26
36
|
|
|
27
37
|
attr_reader :project_dir, :prompt, :dry_run
|
|
28
38
|
# Expose state for testability
|
|
@@ -49,6 +59,7 @@ module Aidp
|
|
|
49
59
|
configure_harness_settings
|
|
50
60
|
configure_thinking_tiers
|
|
51
61
|
configure_work_loop
|
|
62
|
+
configure_prd_generation
|
|
52
63
|
configure_branching
|
|
53
64
|
configure_artifacts
|
|
54
65
|
configure_nfrs
|
|
@@ -492,6 +503,27 @@ module Aidp
|
|
|
492
503
|
configure_vcs_behavior
|
|
493
504
|
end
|
|
494
505
|
|
|
506
|
+
def configure_prd_generation
|
|
507
|
+
prompt.say("\n🧭 PRD generation style")
|
|
508
|
+
prompt.say("-" * 40)
|
|
509
|
+
|
|
510
|
+
existing = get(%i[prd_generation]) || {}
|
|
511
|
+
current_style = existing[:interaction_style] || existing[:default_style] || "balanced"
|
|
512
|
+
default_label = PRD_STYLE_OPTIONS.key(current_style) || PRD_STYLE_OPTIONS.key("balanced")
|
|
513
|
+
|
|
514
|
+
selected_style = prompt.select("Choose PRD interaction style:", default: default_label) do |menu|
|
|
515
|
+
PRD_STYLE_OPTIONS.each do |label, value|
|
|
516
|
+
menu.choice label, value
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
set(%i[prd_generation], {
|
|
521
|
+
interaction_style: selected_style,
|
|
522
|
+
default_style: selected_style,
|
|
523
|
+
max_question_rounds: DEFAULT_PRD_QUESTION_ROUNDS
|
|
524
|
+
})
|
|
525
|
+
end
|
|
526
|
+
|
|
495
527
|
# Configure generic deterministic commands for work loop
|
|
496
528
|
# Replaces the old category-specific configure_test_commands and configure_linting
|
|
497
529
|
def configure_commands
|
|
@@ -1913,6 +1945,7 @@ module Aidp
|
|
|
1913
1945
|
.sub(/^schema_version:/, "# Tracks configuration migrations\nschema_version:")
|
|
1914
1946
|
.sub(/^providers:/, "# Provider configuration (no secrets stored)\nproviders:")
|
|
1915
1947
|
.sub(/^work_loop:/, "# Work loop execution settings\nwork_loop:")
|
|
1948
|
+
.sub(/^prd_generation:/, "# PRD questioning depth and autonomy\nprd_generation:")
|
|
1916
1949
|
.sub(/^nfrs:/, "# Non-functional requirements to reference during planning\nnfrs:")
|
|
1917
1950
|
.sub(/^logging:/, "# Logging configuration\nlogging:")
|
|
1918
1951
|
.sub(/^modes:/, "# Defaults for background/watch/quick modes\nmodes:")
|
data/lib/aidp/version.rb
CHANGED
|
@@ -19,11 +19,32 @@ module Aidp
|
|
|
19
19
|
|
|
20
20
|
class ConversationError < StandardError; end
|
|
21
21
|
|
|
22
|
+
PLANNING_STYLES = {
|
|
23
|
+
"detailed" => {
|
|
24
|
+
label: "Detailed",
|
|
25
|
+
max_question_rounds: 30,
|
|
26
|
+
autonomy: "low",
|
|
27
|
+
question_strategy: "Ask 1-2 sharply scoped questions per round. Avoid assumptions unless the user confirms them."
|
|
28
|
+
},
|
|
29
|
+
"balanced" => {
|
|
30
|
+
label: "Balanced",
|
|
31
|
+
max_question_rounds: 10,
|
|
32
|
+
autonomy: "medium",
|
|
33
|
+
question_strategy: "Ask 1-3 efficient questions per round. Make reasonable decisions when the context is already clear."
|
|
34
|
+
},
|
|
35
|
+
"quick_sketch" => {
|
|
36
|
+
label: "Quick Sketch",
|
|
37
|
+
max_question_rounds: 3,
|
|
38
|
+
autonomy: "high",
|
|
39
|
+
question_strategy: "Ask only the highest-leverage missing questions. Prefer inferring obvious MVP details from repository context and existing configuration."
|
|
40
|
+
}
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
22
43
|
# Expose for testability
|
|
23
44
|
attr_reader :project_dir, :conversation_history, :user_input, :config_manager
|
|
24
45
|
attr_writer :provider_manager
|
|
25
46
|
|
|
26
|
-
def initialize(project_dir, prompt: nil, use_enhanced_input: true, verbose: false, config_manager: nil, provider_manager: nil)
|
|
47
|
+
def initialize(project_dir, prompt: nil, use_enhanced_input: true, verbose: false, interaction_style: nil, config_manager: nil, provider_manager: nil)
|
|
27
48
|
@project_dir = project_dir
|
|
28
49
|
|
|
29
50
|
# Use EnhancedInput with Reline for full readline-style key bindings
|
|
@@ -39,6 +60,7 @@ module Aidp
|
|
|
39
60
|
@user_input = {}
|
|
40
61
|
@verbose = verbose
|
|
41
62
|
@debug_env = debug_level >= Aidp::DebugMixin::DEBUG_BASIC
|
|
63
|
+
@planning_preferences = resolve_planning_preferences(interaction_style)
|
|
42
64
|
end
|
|
43
65
|
|
|
44
66
|
# Main entry point for guided workflow selection
|
|
@@ -60,6 +82,7 @@ module Aidp
|
|
|
60
82
|
# Plan-and-execute: iterative planning followed by execution
|
|
61
83
|
def plan_and_execute_workflow
|
|
62
84
|
display_message("\n📋 Plan Phase", type: :highlight)
|
|
85
|
+
display_message("PRD style: #{planning_style[:interaction_style]}", type: :info)
|
|
63
86
|
display_message("I'll ask clarifying questions to understand your needs.\n", type: :info)
|
|
64
87
|
|
|
65
88
|
# Step 1: Iterative planning conversation
|
|
@@ -77,11 +100,25 @@ module Aidp
|
|
|
77
100
|
|
|
78
101
|
def iterative_planning
|
|
79
102
|
goal = user_goal
|
|
80
|
-
plan = {
|
|
103
|
+
plan = {
|
|
104
|
+
goal: goal,
|
|
105
|
+
scope: {},
|
|
106
|
+
users: {},
|
|
107
|
+
requirements: {},
|
|
108
|
+
constraints: {},
|
|
109
|
+
completion_criteria: [],
|
|
110
|
+
assumptions: [],
|
|
111
|
+
metadata: {
|
|
112
|
+
interaction_style: planning_style[:interaction_style],
|
|
113
|
+
autonomy: planning_style[:autonomy],
|
|
114
|
+
max_question_rounds: planning_style[:max_question_rounds]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
81
117
|
|
|
82
118
|
@conversation_history << {role: "user", content: goal}
|
|
83
119
|
|
|
84
120
|
iteration = 0
|
|
121
|
+
max_rounds = planning_style[:max_question_rounds]
|
|
85
122
|
loop do
|
|
86
123
|
iteration += 1
|
|
87
124
|
# Ask AI for next question based on current plan
|
|
@@ -96,6 +133,7 @@ module Aidp
|
|
|
96
133
|
|
|
97
134
|
# If AI says plan is complete, confirm with user
|
|
98
135
|
if question_response[:complete]
|
|
136
|
+
plan[:assumptions].concat(Array(question_response[:assumptions])).uniq!
|
|
99
137
|
display_message("\n✅ Plan Summary", type: :highlight)
|
|
100
138
|
display_plan_summary(plan)
|
|
101
139
|
|
|
@@ -118,14 +156,14 @@ module Aidp
|
|
|
118
156
|
update_plan_from_answer(plan, question, answer)
|
|
119
157
|
end
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
display_message("[WARNING] Planning loop exceeded 10 iterations. Provider may be returning generic responses.", type: :warning)
|
|
159
|
+
if iteration >= max_rounds
|
|
160
|
+
display_message("[WARNING] Planning loop exceeded #{max_rounds} iterations for #{planning_style[:interaction_style]}.", type: :warning)
|
|
124
161
|
display_message("Continuing with the plan information gathered so far...", type: :info)
|
|
125
162
|
break
|
|
126
163
|
end
|
|
127
164
|
end
|
|
128
165
|
|
|
166
|
+
apply_style_assumptions(plan)
|
|
129
167
|
plan
|
|
130
168
|
end
|
|
131
169
|
|
|
@@ -142,6 +180,7 @@ module Aidp
|
|
|
142
180
|
|
|
143
181
|
response = call_provider_for_analysis(system_prompt, user_prompt)
|
|
144
182
|
parsed = parse_planning_response(response)
|
|
183
|
+
parsed[:assumptions] = Array(parsed[:assumptions]).compact
|
|
145
184
|
# Attach raw response for debug
|
|
146
185
|
parsed[:raw_response] = response
|
|
147
186
|
emit_verbose_raw_prompt(system_prompt, user_prompt, response)
|
|
@@ -397,16 +436,19 @@ module Aidp
|
|
|
397
436
|
You are a planning assistant helping gather requirements through clarifying questions.
|
|
398
437
|
|
|
399
438
|
Your role:
|
|
400
|
-
1.
|
|
439
|
+
1. #{planning_style[:question_strategy]}
|
|
401
440
|
2. Build towards a complete understanding of: scope, users, requirements, constraints
|
|
402
441
|
3. Determine when enough information has been gathered
|
|
403
442
|
4. Be concise to preserve context window
|
|
443
|
+
5. Operate in "#{planning_style[:interaction_style]}" mode with #{planning_style[:autonomy]} autonomy
|
|
444
|
+
6. When you make an assumption, list it explicitly in "assumptions"
|
|
404
445
|
|
|
405
446
|
Response Format (JSON):
|
|
406
447
|
{
|
|
407
448
|
"complete": true/false,
|
|
408
449
|
"questions": ["question 1", "question 2"],
|
|
409
|
-
"reasoning": "brief explanation of what you're trying to learn"
|
|
450
|
+
"reasoning": "brief explanation of what you're trying to learn",
|
|
451
|
+
"assumptions": ["assumption 1"]
|
|
410
452
|
}
|
|
411
453
|
|
|
412
454
|
If complete is true, the plan is ready for execution.
|
|
@@ -470,6 +512,28 @@ module Aidp
|
|
|
470
512
|
end
|
|
471
513
|
end
|
|
472
514
|
|
|
515
|
+
def apply_style_assumptions(plan)
|
|
516
|
+
inferred_assumptions = []
|
|
517
|
+
|
|
518
|
+
if plan[:users].empty?
|
|
519
|
+
plan[:users][:personas] = [default_persona_for_style]
|
|
520
|
+
inferred_assumptions << "Primary user persona inferred as #{default_persona_for_style}."
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
if plan[:completion_criteria].empty?
|
|
524
|
+
criterion = default_completion_criterion
|
|
525
|
+
plan[:completion_criteria] << criterion
|
|
526
|
+
inferred_assumptions << "Success metric inferred as #{criterion}"
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
inferred_assumptions.concat(repository_default_assumptions)
|
|
530
|
+
plan[:assumptions].concat(inferred_assumptions).uniq!
|
|
531
|
+
|
|
532
|
+
inferred_assumptions.each do |assumption|
|
|
533
|
+
display_message("Inferred missing detail: #{assumption}", type: :muted)
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
473
537
|
def display_plan_summary(plan)
|
|
474
538
|
display_message("Goal: #{plan[:goal]}", type: :info)
|
|
475
539
|
display_message("\nScope:", type: :highlight) if plan[:scope].any?
|
|
@@ -533,6 +597,10 @@ module Aidp
|
|
|
533
597
|
prd_content = <<~PRD
|
|
534
598
|
# Product Requirements Document
|
|
535
599
|
|
|
600
|
+
## Planning Metadata
|
|
601
|
+
- PRD Style: #{plan.dig(:metadata, :interaction_style) || planning_style[:interaction_style]}
|
|
602
|
+
- Agent Autonomy: #{plan.dig(:metadata, :autonomy) || planning_style[:autonomy]}
|
|
603
|
+
|
|
536
604
|
## Goal
|
|
537
605
|
#{plan[:goal]}
|
|
538
606
|
|
|
@@ -551,6 +619,9 @@ module Aidp
|
|
|
551
619
|
## Completion Criteria
|
|
552
620
|
#{plan[:completion_criteria].map { |c| "- #{c}" }.join("\n")}
|
|
553
621
|
|
|
622
|
+
## Assumptions & Inferred Details
|
|
623
|
+
#{plan[:assumptions]&.any? ? plan[:assumptions].map { |item| "- #{item}" }.join("\n") : "None"}
|
|
624
|
+
|
|
554
625
|
## Additional Context
|
|
555
626
|
#{plan[:additional_context]&.map { |ctx| "**#{ctx[:question]}**: #{ctx[:answer]}" }&.join("\n\n")}
|
|
556
627
|
|
|
@@ -611,6 +682,64 @@ module Aidp
|
|
|
611
682
|
end
|
|
612
683
|
end.join("\n\n")
|
|
613
684
|
end
|
|
685
|
+
|
|
686
|
+
def planning_style
|
|
687
|
+
@planning_preferences
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
def resolve_planning_preferences(override_style)
|
|
691
|
+
config = @config_manager.respond_to?(:prd_generation_config) ? @config_manager.prd_generation_config : {}
|
|
692
|
+
configured_style = override_style || config[:interaction_style] || config["interaction_style"]
|
|
693
|
+
interaction_style = normalize_interaction_style(configured_style || "balanced")
|
|
694
|
+
configured_rounds = config[:max_question_rounds] || config["max_question_rounds"] || {}
|
|
695
|
+
defaults = PLANNING_STYLES.fetch(interaction_style)
|
|
696
|
+
|
|
697
|
+
defaults.merge(
|
|
698
|
+
interaction_style: interaction_style,
|
|
699
|
+
max_question_rounds: configured_rounds[interaction_style.to_sym] || configured_rounds[interaction_style] || defaults[:max_question_rounds]
|
|
700
|
+
)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
def normalize_interaction_style(style)
|
|
704
|
+
normalized = style.to_s.strip.downcase.tr(" ", "_")
|
|
705
|
+
return normalized if PLANNING_STYLES.key?(normalized)
|
|
706
|
+
|
|
707
|
+
"balanced"
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
def default_persona_for_style
|
|
711
|
+
if planning_style[:interaction_style] == "quick_sketch"
|
|
712
|
+
"Prototype users validating the MVP flow"
|
|
713
|
+
else
|
|
714
|
+
"Primary end users for this feature"
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def default_completion_criterion
|
|
719
|
+
if planning_style[:interaction_style] == "quick_sketch"
|
|
720
|
+
"A thin MVP slice is implemented and demoable end-to-end."
|
|
721
|
+
else
|
|
722
|
+
"The feature meets the documented requirements and is ready for implementation planning."
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def repository_default_assumptions
|
|
727
|
+
config = @config_manager.respond_to?(:config) ? (@config_manager.config || {}) : {}
|
|
728
|
+
assumptions = []
|
|
729
|
+
commands = config.dig(:work_loop, :commands) || config.dig("work_loop", "commands")
|
|
730
|
+
nfrs = config.dig(:nfrs) || config.dig("nfrs")
|
|
731
|
+
|
|
732
|
+
if commands.is_a?(Array) && commands.any?
|
|
733
|
+
command_label = (commands.size == 1) ? "command" : "commands"
|
|
734
|
+
assumptions << "Validation will reuse #{commands.size} configured work loop #{command_label}."
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
if nfrs.is_a?(Hash) && nfrs.any?
|
|
738
|
+
assumptions << "Repository-level non-functional requirements from aidp.yml will apply."
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
assumptions
|
|
742
|
+
end
|
|
614
743
|
end
|
|
615
744
|
end
|
|
616
745
|
end
|