aidp 0.17.0 ā 0.17.1
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/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 +21 -27
- data/lib/aidp/execute/progress.rb +5 -8
- data/lib/aidp/harness/config_loader.rb +2 -2
- data/lib/aidp/harness/enhanced_runner.rb +16 -7
- data/lib/aidp/harness/error_handler.rb +12 -5
- data/lib/aidp/harness/provider_manager.rb +4 -19
- data/lib/aidp/harness/runner.rb +2 -2
- 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/ui/enhanced_tui.rb +3 -4
- data/lib/aidp/harness/user_interface.rb +11 -6
- 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/providers/anthropic.rb +1 -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/macos_ui.rb +1 -1
- data/lib/aidp/providers/opencode.rb +1 -1
- data/lib/aidp/skills/wizard/prompter.rb +2 -2
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/plan_generator.rb +1 -1
- data/lib/aidp/watch/repository_client.rb +13 -9
- data/lib/aidp/workflows/guided_agent.rb +2 -312
- data/lib/aidp/workstream_executor.rb +8 -2
- metadata +1 -1
|
@@ -201,79 +201,10 @@ module Aidp
|
|
|
201
201
|
goal
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
def analyze_user_intent(user_goal)
|
|
205
|
-
display_message("\nš Analyzing your request...", type: :info)
|
|
206
|
-
|
|
207
|
-
# Build the system prompt with AIDP capabilities
|
|
208
|
-
system_prompt = build_system_prompt
|
|
209
|
-
|
|
210
|
-
# Build the user prompt
|
|
211
|
-
user_prompt = build_analysis_prompt(user_goal)
|
|
212
|
-
|
|
213
|
-
# Call provider to analyze intent
|
|
214
|
-
response = call_provider_for_analysis(system_prompt, user_prompt)
|
|
215
|
-
|
|
216
|
-
# Parse the response
|
|
217
|
-
parse_recommendation(response)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def build_system_prompt
|
|
221
|
-
# Try to load from project dir first, fall back to gem's docs
|
|
222
|
-
capabilities_path = File.join(@project_dir, "docs", "AIDP_CAPABILITIES.md")
|
|
223
|
-
unless File.exist?(capabilities_path)
|
|
224
|
-
# Use the gem's copy
|
|
225
|
-
gem_root = File.expand_path("../../..", __dir__)
|
|
226
|
-
capabilities_path = File.join(gem_root, "docs", "AIDP_CAPABILITIES.md")
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
capabilities_doc = File.read(capabilities_path)
|
|
230
|
-
|
|
231
|
-
<<~PROMPT
|
|
232
|
-
You are an expert AI assistant helping users select the right AIDP workflow for their needs.
|
|
233
|
-
|
|
234
|
-
Your role:
|
|
235
|
-
1. Understand what the user wants to accomplish
|
|
236
|
-
2. Match their intent to AIDP's capabilities
|
|
237
|
-
3. Recommend the most appropriate workflow
|
|
238
|
-
4. Explain why this workflow fits their needs
|
|
239
|
-
5. Identify if custom steps or templates are needed
|
|
240
|
-
|
|
241
|
-
AIDP Capabilities Reference:
|
|
242
|
-
#{capabilities_doc}
|
|
243
|
-
|
|
244
|
-
Response Format:
|
|
245
|
-
Provide a JSON response with:
|
|
246
|
-
{
|
|
247
|
-
"mode": "analyze|execute|hybrid",
|
|
248
|
-
"workflow_key": "specific_workflow_key",
|
|
249
|
-
"reasoning": "brief explanation of why this fits",
|
|
250
|
-
"additional_steps": ["any", "custom", "steps", "if", "needed"],
|
|
251
|
-
"questions": ["any", "clarifying", "questions"],
|
|
252
|
-
"confidence": "high|medium|low"
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
Be concise to preserve the user's context window.
|
|
256
|
-
PROMPT
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def build_analysis_prompt(user_goal)
|
|
260
|
-
<<~PROMPT
|
|
261
|
-
User Goal: #{user_goal}
|
|
262
|
-
|
|
263
|
-
Analyze this goal and recommend the most appropriate AIDP workflow.
|
|
264
|
-
Consider:
|
|
265
|
-
- Is this analysis or development?
|
|
266
|
-
- What level of rigor is needed?
|
|
267
|
-
- Are there any gaps in existing workflows?
|
|
268
|
-
|
|
269
|
-
Provide your recommendation in JSON format.
|
|
270
|
-
PROMPT
|
|
271
|
-
end
|
|
272
|
-
|
|
273
204
|
def call_provider_for_analysis(system_prompt, user_prompt)
|
|
274
205
|
attempts = 0
|
|
275
206
|
max_attempts = (@provider_manager.respond_to?(:configured_providers) ? @provider_manager.configured_providers.size : 2)
|
|
276
|
-
max_attempts = 2 if max_attempts < 2
|
|
207
|
+
max_attempts = 2 if max_attempts < 2
|
|
277
208
|
|
|
278
209
|
begin
|
|
279
210
|
attempts += 1
|
|
@@ -283,7 +214,6 @@ module Aidp
|
|
|
283
214
|
raise ConversationError, "No provider configured for guided workflow"
|
|
284
215
|
end
|
|
285
216
|
|
|
286
|
-
# Create provider instance using ProviderFactory
|
|
287
217
|
provider_factory = Aidp::Harness::ProviderFactory.new(@config_manager)
|
|
288
218
|
provider = provider_factory.create_provider(provider_name, prompt: @prompt)
|
|
289
219
|
|
|
@@ -292,7 +222,7 @@ module Aidp
|
|
|
292
222
|
end
|
|
293
223
|
|
|
294
224
|
combined_prompt = "#{system_prompt}\n\n#{user_prompt}"
|
|
295
|
-
result = provider.
|
|
225
|
+
result = provider.send_message(prompt: combined_prompt)
|
|
296
226
|
|
|
297
227
|
if result.nil? || result.empty?
|
|
298
228
|
raise ConversationError, "Provider request failed: empty response"
|
|
@@ -319,20 +249,6 @@ module Aidp
|
|
|
319
249
|
end
|
|
320
250
|
end
|
|
321
251
|
|
|
322
|
-
def parse_recommendation(response_text)
|
|
323
|
-
# Extract JSON from response (might be wrapped in markdown code blocks)
|
|
324
|
-
json_match = response_text.match(/```json\s*(\{.*?\})\s*```/m) ||
|
|
325
|
-
response_text.match(/(\{.*\})/m)
|
|
326
|
-
|
|
327
|
-
unless json_match
|
|
328
|
-
raise ConversationError, "Could not parse recommendation from response"
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
JSON.parse(json_match[1], symbolize_names: true)
|
|
332
|
-
rescue JSON::ParserError => e
|
|
333
|
-
raise ConversationError, "Invalid JSON in recommendation: #{e.message}"
|
|
334
|
-
end
|
|
335
|
-
|
|
336
252
|
def validate_provider_configuration!
|
|
337
253
|
configured = @provider_manager.configured_providers
|
|
338
254
|
if configured.nil? || configured.empty?
|
|
@@ -349,232 +265,6 @@ module Aidp
|
|
|
349
265
|
end
|
|
350
266
|
end
|
|
351
267
|
|
|
352
|
-
def present_recommendation(recommendation)
|
|
353
|
-
display_message("\n⨠Recommendation", type: :highlight)
|
|
354
|
-
display_message("ā" * 60, type: :muted)
|
|
355
|
-
|
|
356
|
-
mode = recommendation[:mode].to_sym
|
|
357
|
-
workflow_key = recommendation[:workflow_key].to_sym
|
|
358
|
-
|
|
359
|
-
# Get workflow details
|
|
360
|
-
workflow = Definitions.get_workflow(mode, workflow_key)
|
|
361
|
-
|
|
362
|
-
unless workflow
|
|
363
|
-
# Handle custom workflow or error
|
|
364
|
-
return handle_custom_workflow(recommendation)
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
# Display the recommendation
|
|
368
|
-
display_message("Mode: #{mode.to_s.capitalize}", type: :info)
|
|
369
|
-
display_message("Workflow: #{workflow[:name]}", type: :info)
|
|
370
|
-
display_message("\n#{workflow[:description]}", type: :muted)
|
|
371
|
-
display_message("\nReasoning: #{recommendation[:reasoning]}\n", type: :info)
|
|
372
|
-
|
|
373
|
-
# Show what's included
|
|
374
|
-
display_message("This workflow includes:", type: :highlight)
|
|
375
|
-
workflow[:details].each do |detail|
|
|
376
|
-
display_message(" ⢠#{detail}", type: :info)
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Handle additional steps if needed
|
|
380
|
-
steps = workflow[:steps]
|
|
381
|
-
if recommendation[:additional_steps]&.any?
|
|
382
|
-
display_message("\nAdditional custom steps recommended:", type: :highlight)
|
|
383
|
-
recommendation[:additional_steps].each do |step|
|
|
384
|
-
display_message(" ⢠#{step}", type: :info)
|
|
385
|
-
end
|
|
386
|
-
steps = workflow[:steps] + recommendation[:additional_steps]
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
# Ask for confirmation
|
|
390
|
-
display_message("")
|
|
391
|
-
confirmed = @prompt.yes?("Does this workflow fit your needs?")
|
|
392
|
-
|
|
393
|
-
if confirmed
|
|
394
|
-
{
|
|
395
|
-
mode: mode,
|
|
396
|
-
workflow_key: workflow_key,
|
|
397
|
-
workflow_type: workflow_key,
|
|
398
|
-
steps: steps,
|
|
399
|
-
user_input: @user_input,
|
|
400
|
-
workflow: workflow
|
|
401
|
-
}
|
|
402
|
-
else
|
|
403
|
-
# Offer alternatives
|
|
404
|
-
offer_alternatives(mode)
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
def handle_custom_workflow(recommendation)
|
|
409
|
-
display_message("\nš” Custom Workflow Needed", type: :highlight)
|
|
410
|
-
display_message("The AI recommends creating a custom workflow:\n", type: :info)
|
|
411
|
-
display_message(recommendation[:reasoning], type: :muted)
|
|
412
|
-
|
|
413
|
-
if recommendation[:questions]&.any?
|
|
414
|
-
display_message("\nLet me gather some more information:\n", type: :highlight)
|
|
415
|
-
recommendation[:questions].each do |question|
|
|
416
|
-
answer = @prompt.ask(question)
|
|
417
|
-
@user_input[question.downcase.gsub(/\s+/, "_").to_sym] = answer
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
# Let user select from available steps
|
|
422
|
-
mode = recommendation[:mode].to_sym
|
|
423
|
-
display_message("\nLet's build a custom workflow by selecting specific steps:", type: :info)
|
|
424
|
-
|
|
425
|
-
steps = select_custom_steps(mode, recommendation[:additional_steps])
|
|
426
|
-
|
|
427
|
-
{
|
|
428
|
-
mode: mode,
|
|
429
|
-
workflow_key: :custom,
|
|
430
|
-
workflow_type: :custom,
|
|
431
|
-
steps: steps,
|
|
432
|
-
user_input: @user_input,
|
|
433
|
-
workflow: {
|
|
434
|
-
name: "Custom Workflow",
|
|
435
|
-
description: recommendation[:reasoning],
|
|
436
|
-
details: steps
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
def select_custom_steps(mode, suggested_steps = [])
|
|
442
|
-
# Get available steps for the mode
|
|
443
|
-
spec = case mode
|
|
444
|
-
when :analyze
|
|
445
|
-
Aidp::Analyze::Steps::SPEC
|
|
446
|
-
when :execute
|
|
447
|
-
Aidp::Execute::Steps::SPEC
|
|
448
|
-
when :hybrid
|
|
449
|
-
# Combine both
|
|
450
|
-
Aidp::Analyze::Steps::SPEC.merge(Aidp::Execute::Steps::SPEC)
|
|
451
|
-
else
|
|
452
|
-
{}
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
step_choices = spec.map do |step_key, step_spec|
|
|
456
|
-
{
|
|
457
|
-
name: "#{step_key} - #{step_spec["description"]}",
|
|
458
|
-
value: step_key
|
|
459
|
-
}
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
# Pre-select suggested steps
|
|
463
|
-
default_indices = suggested_steps.map do |step|
|
|
464
|
-
step_choices.index { |choice| choice[:value].to_s == step.to_s }
|
|
465
|
-
end.compact
|
|
466
|
-
|
|
467
|
-
selected_steps = @prompt.multi_select(
|
|
468
|
-
"Select steps for your custom workflow:",
|
|
469
|
-
step_choices,
|
|
470
|
-
default: default_indices,
|
|
471
|
-
per_page: 20
|
|
472
|
-
)
|
|
473
|
-
|
|
474
|
-
selected_steps.empty? ? suggested_steps : selected_steps
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
def offer_alternatives(current_mode)
|
|
478
|
-
display_message("\nš Let's find a better fit", type: :highlight)
|
|
479
|
-
|
|
480
|
-
choices = [
|
|
481
|
-
{name: "Try a different #{current_mode} workflow", value: :different_workflow},
|
|
482
|
-
{name: "Switch to a different mode", value: :different_mode},
|
|
483
|
-
{name: "Build a custom workflow", value: :custom},
|
|
484
|
-
{name: "Start over", value: :restart}
|
|
485
|
-
]
|
|
486
|
-
|
|
487
|
-
choice = @prompt.select("What would you like to do?", choices)
|
|
488
|
-
|
|
489
|
-
case choice
|
|
490
|
-
when :different_workflow
|
|
491
|
-
select_manual_workflow(current_mode)
|
|
492
|
-
when :different_mode
|
|
493
|
-
# Let user pick mode manually then workflow
|
|
494
|
-
new_mode = @prompt.select(
|
|
495
|
-
"Select mode:",
|
|
496
|
-
{
|
|
497
|
-
"š¬ Analyze Mode" => :analyze,
|
|
498
|
-
"šļø Execute Mode" => :execute,
|
|
499
|
-
"š Hybrid Mode" => :hybrid
|
|
500
|
-
}
|
|
501
|
-
)
|
|
502
|
-
select_manual_workflow(new_mode)
|
|
503
|
-
when :custom
|
|
504
|
-
select_custom_steps(current_mode)
|
|
505
|
-
when :restart
|
|
506
|
-
select_workflow # Recursive call to start over
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
def select_manual_workflow(mode)
|
|
511
|
-
workflows = Definitions.workflows_for_mode(mode)
|
|
512
|
-
|
|
513
|
-
choices = workflows.map do |key, workflow|
|
|
514
|
-
{
|
|
515
|
-
name: "#{workflow[:icon]} #{workflow[:name]} - #{workflow[:description]}",
|
|
516
|
-
value: key
|
|
517
|
-
}
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
selected_key = @prompt.select("Choose a workflow:", choices, per_page: 15)
|
|
521
|
-
workflow = workflows[selected_key]
|
|
522
|
-
|
|
523
|
-
{
|
|
524
|
-
mode: mode,
|
|
525
|
-
workflow_key: selected_key,
|
|
526
|
-
workflow_type: selected_key,
|
|
527
|
-
steps: workflow[:steps],
|
|
528
|
-
user_input: @user_input,
|
|
529
|
-
workflow: workflow
|
|
530
|
-
}
|
|
531
|
-
end
|
|
532
|
-
|
|
533
|
-
def collect_workflow_details(workflow_selection)
|
|
534
|
-
# Collect additional information based on mode
|
|
535
|
-
case workflow_selection[:mode]
|
|
536
|
-
when :execute
|
|
537
|
-
collect_execute_details
|
|
538
|
-
when :analyze
|
|
539
|
-
# Analyze mode typically doesn't need much user input
|
|
540
|
-
@user_input[:analysis_goal] = @user_input[:original_goal]
|
|
541
|
-
when :hybrid
|
|
542
|
-
collect_execute_details # Hybrid often needs project details
|
|
543
|
-
end
|
|
544
|
-
|
|
545
|
-
# Update the workflow selection with collected input
|
|
546
|
-
workflow_selection[:user_input] = @user_input
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
def collect_execute_details
|
|
550
|
-
return if @user_input[:project_description] # Already collected
|
|
551
|
-
|
|
552
|
-
display_message("\nš Project Information", type: :highlight)
|
|
553
|
-
display_message("Let me gather some details for the PRD:\n", type: :info)
|
|
554
|
-
|
|
555
|
-
@user_input[:project_description] = @prompt.ask(
|
|
556
|
-
"Describe what you're building (can reference your original goal):",
|
|
557
|
-
default: @user_input[:original_goal]
|
|
558
|
-
)
|
|
559
|
-
|
|
560
|
-
@user_input[:tech_stack] = @prompt.ask(
|
|
561
|
-
"Tech stack (e.g., Ruby/Rails, Node.js, Python)? [optional]",
|
|
562
|
-
required: false
|
|
563
|
-
)
|
|
564
|
-
|
|
565
|
-
@user_input[:target_users] = @prompt.ask(
|
|
566
|
-
"Who will use this? [optional]",
|
|
567
|
-
required: false
|
|
568
|
-
)
|
|
569
|
-
|
|
570
|
-
@user_input[:success_criteria] = @prompt.ask(
|
|
571
|
-
"How will you measure success? [optional]",
|
|
572
|
-
required: false
|
|
573
|
-
)
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
# Plan-then-execute helper methods
|
|
577
|
-
|
|
578
268
|
def build_planning_system_prompt
|
|
579
269
|
<<~PROMPT
|
|
580
270
|
You are a planning assistant helping gather requirements through clarifying questions.
|
|
@@ -25,11 +25,17 @@ module Aidp
|
|
|
25
25
|
keyword_init: true
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# @param project_dir [String] root directory of the project
|
|
29
|
+
# @param max_concurrent [Integer] maximum number of concurrent workstreams (thread pool size)
|
|
30
|
+
# @param runner_factory [Proc] factory that builds a harness runner. Signature: (path, mode, options) => object responding to #run
|
|
31
|
+
def initialize(project_dir: Dir.pwd, max_concurrent: 3, runner_factory: nil)
|
|
29
32
|
@project_dir = project_dir
|
|
30
33
|
@max_concurrent = max_concurrent
|
|
31
34
|
@results = Concurrent::Hash.new
|
|
32
35
|
@start_times = Concurrent::Hash.new
|
|
36
|
+
@runner_factory = runner_factory || lambda do |path, mode, options|
|
|
37
|
+
Aidp::Harness::Runner.new(path, mode, options)
|
|
38
|
+
end
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
# Execute multiple workstreams in parallel
|
|
@@ -120,7 +126,7 @@ module Aidp
|
|
|
120
126
|
Dir.chdir(workstream[:path])
|
|
121
127
|
|
|
122
128
|
# Execute harness
|
|
123
|
-
runner =
|
|
129
|
+
runner = @runner_factory.call(
|
|
124
130
|
workstream[:path],
|
|
125
131
|
options[:mode] || :execute,
|
|
126
132
|
options
|