aidp 0.11.0 ā 0.12.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/cli/first_run_wizard.rb +11 -7
- data/lib/aidp/cli/mcp_dashboard.rb +205 -0
- data/lib/aidp/cli.rb +206 -6
- data/lib/aidp/config/paths.rb +131 -0
- data/lib/aidp/config.rb +18 -4
- data/lib/aidp/harness/config_validator.rb +4 -3
- data/lib/aidp/harness/configuration.rb +2 -1
- data/lib/aidp/harness/provider_info.rb +366 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -1
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/workflows/guided_agent.rb +400 -0
- metadata +5 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../harness/provider_manager"
|
|
4
|
+
require_relative "../harness/provider_factory"
|
|
5
|
+
require_relative "../harness/configuration"
|
|
6
|
+
require_relative "definitions"
|
|
7
|
+
require_relative "../message_display"
|
|
8
|
+
|
|
9
|
+
module Aidp
|
|
10
|
+
module Workflows
|
|
11
|
+
# Guided workflow agent that uses AI to help users select appropriate workflows
|
|
12
|
+
# Acts as a copilot to match user intent to AIDP capabilities
|
|
13
|
+
class GuidedAgent
|
|
14
|
+
include Aidp::MessageDisplay
|
|
15
|
+
|
|
16
|
+
class ConversationError < StandardError; end
|
|
17
|
+
|
|
18
|
+
def initialize(project_dir, prompt: nil)
|
|
19
|
+
@project_dir = project_dir
|
|
20
|
+
@prompt = prompt || TTY::Prompt.new
|
|
21
|
+
@configuration = Aidp::Harness::Configuration.new(project_dir)
|
|
22
|
+
@provider_manager = Aidp::Harness::ProviderManager.new(@configuration, prompt: @prompt)
|
|
23
|
+
@conversation_history = []
|
|
24
|
+
@user_input = {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Main entry point for guided workflow selection
|
|
28
|
+
def select_workflow
|
|
29
|
+
display_message("\nš¤ Welcome to AIDP Guided Workflow!", type: :highlight)
|
|
30
|
+
display_message("I'll help you choose the right workflow for your needs.\n", type: :info)
|
|
31
|
+
|
|
32
|
+
# Step 1: Get user's high-level goal
|
|
33
|
+
user_goal = get_user_goal
|
|
34
|
+
|
|
35
|
+
# Step 2: Use AI to analyze intent and recommend workflow
|
|
36
|
+
recommendation = analyze_user_intent(user_goal)
|
|
37
|
+
|
|
38
|
+
# Step 3: Present recommendation and get confirmation
|
|
39
|
+
workflow_selection = present_recommendation(recommendation)
|
|
40
|
+
|
|
41
|
+
# Step 4: Collect any additional required information
|
|
42
|
+
collect_workflow_details(workflow_selection)
|
|
43
|
+
|
|
44
|
+
workflow_selection
|
|
45
|
+
rescue => e
|
|
46
|
+
raise ConversationError, "Failed to guide workflow selection: #{e.message}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def get_user_goal
|
|
52
|
+
display_message("What would you like to do?", type: :highlight)
|
|
53
|
+
display_message("Examples:", type: :muted)
|
|
54
|
+
display_message(" ⢠Build a new feature for user authentication", type: :muted)
|
|
55
|
+
display_message(" ⢠Understand how this codebase handles payments", type: :muted)
|
|
56
|
+
display_message(" ⢠Improve test coverage in my API layer", type: :muted)
|
|
57
|
+
display_message(" ⢠Create a quick prototype for data export\n", type: :muted)
|
|
58
|
+
|
|
59
|
+
goal = @prompt.ask("Your goal:", required: true)
|
|
60
|
+
@user_input[:original_goal] = goal
|
|
61
|
+
goal
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def analyze_user_intent(user_goal)
|
|
65
|
+
display_message("\nš Analyzing your request...", type: :info)
|
|
66
|
+
|
|
67
|
+
# Build the system prompt with AIDP capabilities
|
|
68
|
+
system_prompt = build_system_prompt
|
|
69
|
+
|
|
70
|
+
# Build the user prompt
|
|
71
|
+
user_prompt = build_analysis_prompt(user_goal)
|
|
72
|
+
|
|
73
|
+
# Call provider to analyze intent
|
|
74
|
+
response = call_provider_for_analysis(system_prompt, user_prompt)
|
|
75
|
+
|
|
76
|
+
# Parse the response
|
|
77
|
+
parse_recommendation(response)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def build_system_prompt
|
|
81
|
+
# Try to load from project dir first, fall back to gem's docs
|
|
82
|
+
capabilities_path = File.join(@project_dir, "docs", "AIDP_CAPABILITIES.md")
|
|
83
|
+
unless File.exist?(capabilities_path)
|
|
84
|
+
# Use the gem's copy
|
|
85
|
+
gem_root = File.expand_path("../../..", __dir__)
|
|
86
|
+
capabilities_path = File.join(gem_root, "docs", "AIDP_CAPABILITIES.md")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
capabilities_doc = File.read(capabilities_path)
|
|
90
|
+
|
|
91
|
+
<<~PROMPT
|
|
92
|
+
You are an expert AI assistant helping users select the right AIDP workflow for their needs.
|
|
93
|
+
|
|
94
|
+
Your role:
|
|
95
|
+
1. Understand what the user wants to accomplish
|
|
96
|
+
2. Match their intent to AIDP's capabilities
|
|
97
|
+
3. Recommend the most appropriate workflow
|
|
98
|
+
4. Explain why this workflow fits their needs
|
|
99
|
+
5. Identify if custom steps or templates are needed
|
|
100
|
+
|
|
101
|
+
AIDP Capabilities Reference:
|
|
102
|
+
#{capabilities_doc}
|
|
103
|
+
|
|
104
|
+
Response Format:
|
|
105
|
+
Provide a JSON response with:
|
|
106
|
+
{
|
|
107
|
+
"mode": "analyze|execute|hybrid",
|
|
108
|
+
"workflow_key": "specific_workflow_key",
|
|
109
|
+
"reasoning": "brief explanation of why this fits",
|
|
110
|
+
"additional_steps": ["any", "custom", "steps", "if", "needed"],
|
|
111
|
+
"questions": ["any", "clarifying", "questions"],
|
|
112
|
+
"confidence": "high|medium|low"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
Be concise to preserve the user's context window.
|
|
116
|
+
PROMPT
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def build_analysis_prompt(user_goal)
|
|
120
|
+
<<~PROMPT
|
|
121
|
+
User Goal: #{user_goal}
|
|
122
|
+
|
|
123
|
+
Analyze this goal and recommend the most appropriate AIDP workflow.
|
|
124
|
+
Consider:
|
|
125
|
+
- Is this analysis or development?
|
|
126
|
+
- What level of rigor is needed?
|
|
127
|
+
- Are there any gaps in existing workflows?
|
|
128
|
+
|
|
129
|
+
Provide your recommendation in JSON format.
|
|
130
|
+
PROMPT
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def call_provider_for_analysis(system_prompt, user_prompt)
|
|
134
|
+
# Get current provider from provider manager
|
|
135
|
+
provider_name = @provider_manager.current_provider
|
|
136
|
+
|
|
137
|
+
unless provider_name
|
|
138
|
+
raise ConversationError, "No provider configured for guided workflow"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Create provider instance using ProviderFactory
|
|
142
|
+
provider_factory = Aidp::Harness::ProviderFactory.new(@configuration)
|
|
143
|
+
provider = provider_factory.create_provider(provider_name, prompt: @prompt)
|
|
144
|
+
|
|
145
|
+
unless provider
|
|
146
|
+
raise ConversationError, "Failed to create provider instance for #{provider_name}"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Make the request - combine system and user prompts
|
|
150
|
+
combined_prompt = "#{system_prompt}\n\n#{user_prompt}"
|
|
151
|
+
|
|
152
|
+
result = provider.send(prompt: combined_prompt)
|
|
153
|
+
|
|
154
|
+
unless result[:status] == :success
|
|
155
|
+
raise ConversationError, "Provider request failed: #{result[:error]}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
result[:content]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def parse_recommendation(response_text)
|
|
162
|
+
# Extract JSON from response (might be wrapped in markdown code blocks)
|
|
163
|
+
json_match = response_text.match(/```json\s*(\{.*?\})\s*```/m) ||
|
|
164
|
+
response_text.match(/(\{.*\})/m)
|
|
165
|
+
|
|
166
|
+
unless json_match
|
|
167
|
+
raise ConversationError, "Could not parse recommendation from response"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
JSON.parse(json_match[1], symbolize_names: true)
|
|
171
|
+
rescue JSON::ParserError => e
|
|
172
|
+
raise ConversationError, "Invalid JSON in recommendation: #{e.message}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def present_recommendation(recommendation)
|
|
176
|
+
display_message("\n⨠Recommendation", type: :highlight)
|
|
177
|
+
display_message("ā" * 60, type: :muted)
|
|
178
|
+
|
|
179
|
+
mode = recommendation[:mode].to_sym
|
|
180
|
+
workflow_key = recommendation[:workflow_key].to_sym
|
|
181
|
+
|
|
182
|
+
# Get workflow details
|
|
183
|
+
workflow = Definitions.get_workflow(mode, workflow_key)
|
|
184
|
+
|
|
185
|
+
unless workflow
|
|
186
|
+
# Handle custom workflow or error
|
|
187
|
+
return handle_custom_workflow(recommendation)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Display the recommendation
|
|
191
|
+
display_message("Mode: #{mode.to_s.capitalize}", type: :info)
|
|
192
|
+
display_message("Workflow: #{workflow[:name]}", type: :info)
|
|
193
|
+
display_message("\n#{workflow[:description]}", type: :muted)
|
|
194
|
+
display_message("\nReasoning: #{recommendation[:reasoning]}\n", type: :info)
|
|
195
|
+
|
|
196
|
+
# Show what's included
|
|
197
|
+
display_message("This workflow includes:", type: :highlight)
|
|
198
|
+
workflow[:details].each do |detail|
|
|
199
|
+
display_message(" ⢠#{detail}", type: :info)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Handle additional steps if needed
|
|
203
|
+
steps = workflow[:steps]
|
|
204
|
+
if recommendation[:additional_steps]&.any?
|
|
205
|
+
display_message("\nAdditional custom steps recommended:", type: :highlight)
|
|
206
|
+
recommendation[:additional_steps].each do |step|
|
|
207
|
+
display_message(" ⢠#{step}", type: :info)
|
|
208
|
+
end
|
|
209
|
+
steps = workflow[:steps] + recommendation[:additional_steps]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Ask for confirmation
|
|
213
|
+
display_message("")
|
|
214
|
+
confirmed = @prompt.yes?("Does this workflow fit your needs?")
|
|
215
|
+
|
|
216
|
+
if confirmed
|
|
217
|
+
{
|
|
218
|
+
mode: mode,
|
|
219
|
+
workflow_key: workflow_key,
|
|
220
|
+
workflow_type: workflow_key,
|
|
221
|
+
steps: steps,
|
|
222
|
+
user_input: @user_input,
|
|
223
|
+
workflow: workflow
|
|
224
|
+
}
|
|
225
|
+
else
|
|
226
|
+
# Offer alternatives
|
|
227
|
+
offer_alternatives(mode)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def handle_custom_workflow(recommendation)
|
|
232
|
+
display_message("\nš” Custom Workflow Needed", type: :highlight)
|
|
233
|
+
display_message("The AI recommends creating a custom workflow:\n", type: :info)
|
|
234
|
+
display_message(recommendation[:reasoning], type: :muted)
|
|
235
|
+
|
|
236
|
+
if recommendation[:questions]&.any?
|
|
237
|
+
display_message("\nLet me gather some more information:\n", type: :highlight)
|
|
238
|
+
recommendation[:questions].each do |question|
|
|
239
|
+
answer = @prompt.ask(question)
|
|
240
|
+
@user_input[question.downcase.gsub(/\s+/, "_").to_sym] = answer
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Let user select from available steps
|
|
245
|
+
mode = recommendation[:mode].to_sym
|
|
246
|
+
display_message("\nLet's build a custom workflow by selecting specific steps:", type: :info)
|
|
247
|
+
|
|
248
|
+
steps = select_custom_steps(mode, recommendation[:additional_steps])
|
|
249
|
+
|
|
250
|
+
{
|
|
251
|
+
mode: mode,
|
|
252
|
+
workflow_key: :custom,
|
|
253
|
+
workflow_type: :custom,
|
|
254
|
+
steps: steps,
|
|
255
|
+
user_input: @user_input,
|
|
256
|
+
workflow: {
|
|
257
|
+
name: "Custom Workflow",
|
|
258
|
+
description: recommendation[:reasoning],
|
|
259
|
+
details: steps
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def select_custom_steps(mode, suggested_steps = [])
|
|
265
|
+
# Get available steps for the mode
|
|
266
|
+
spec = case mode
|
|
267
|
+
when :analyze
|
|
268
|
+
Aidp::Analyze::Steps::SPEC
|
|
269
|
+
when :execute
|
|
270
|
+
Aidp::Execute::Steps::SPEC
|
|
271
|
+
when :hybrid
|
|
272
|
+
# Combine both
|
|
273
|
+
Aidp::Analyze::Steps::SPEC.merge(Aidp::Execute::Steps::SPEC)
|
|
274
|
+
else
|
|
275
|
+
{}
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
step_choices = spec.map do |step_key, step_spec|
|
|
279
|
+
{
|
|
280
|
+
name: "#{step_key} - #{step_spec["description"]}",
|
|
281
|
+
value: step_key
|
|
282
|
+
}
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Pre-select suggested steps
|
|
286
|
+
default_indices = suggested_steps.map do |step|
|
|
287
|
+
step_choices.index { |choice| choice[:value].to_s == step.to_s }
|
|
288
|
+
end.compact
|
|
289
|
+
|
|
290
|
+
selected_steps = @prompt.multi_select(
|
|
291
|
+
"Select steps for your custom workflow:",
|
|
292
|
+
step_choices,
|
|
293
|
+
default: default_indices,
|
|
294
|
+
per_page: 20
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
selected_steps.empty? ? suggested_steps : selected_steps
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def offer_alternatives(current_mode)
|
|
301
|
+
display_message("\nš Let's find a better fit", type: :highlight)
|
|
302
|
+
|
|
303
|
+
choices = [
|
|
304
|
+
{name: "Try a different #{current_mode} workflow", value: :different_workflow},
|
|
305
|
+
{name: "Switch to a different mode", value: :different_mode},
|
|
306
|
+
{name: "Build a custom workflow", value: :custom},
|
|
307
|
+
{name: "Start over", value: :restart}
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
choice = @prompt.select("What would you like to do?", choices)
|
|
311
|
+
|
|
312
|
+
case choice
|
|
313
|
+
when :different_workflow
|
|
314
|
+
select_manual_workflow(current_mode)
|
|
315
|
+
when :different_mode
|
|
316
|
+
# Let user pick mode manually then workflow
|
|
317
|
+
new_mode = @prompt.select(
|
|
318
|
+
"Select mode:",
|
|
319
|
+
{
|
|
320
|
+
"š¬ Analyze Mode" => :analyze,
|
|
321
|
+
"šļø Execute Mode" => :execute,
|
|
322
|
+
"š Hybrid Mode" => :hybrid
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
select_manual_workflow(new_mode)
|
|
326
|
+
when :custom
|
|
327
|
+
select_custom_steps(current_mode)
|
|
328
|
+
when :restart
|
|
329
|
+
select_workflow # Recursive call to start over
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def select_manual_workflow(mode)
|
|
334
|
+
workflows = Definitions.workflows_for_mode(mode)
|
|
335
|
+
|
|
336
|
+
choices = workflows.map do |key, workflow|
|
|
337
|
+
{
|
|
338
|
+
name: "#{workflow[:icon]} #{workflow[:name]} - #{workflow[:description]}",
|
|
339
|
+
value: key
|
|
340
|
+
}
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
selected_key = @prompt.select("Choose a workflow:", choices, per_page: 15)
|
|
344
|
+
workflow = workflows[selected_key]
|
|
345
|
+
|
|
346
|
+
{
|
|
347
|
+
mode: mode,
|
|
348
|
+
workflow_key: selected_key,
|
|
349
|
+
workflow_type: selected_key,
|
|
350
|
+
steps: workflow[:steps],
|
|
351
|
+
user_input: @user_input,
|
|
352
|
+
workflow: workflow
|
|
353
|
+
}
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def collect_workflow_details(workflow_selection)
|
|
357
|
+
# Collect additional information based on mode
|
|
358
|
+
case workflow_selection[:mode]
|
|
359
|
+
when :execute
|
|
360
|
+
collect_execute_details
|
|
361
|
+
when :analyze
|
|
362
|
+
# Analyze mode typically doesn't need much user input
|
|
363
|
+
@user_input[:analysis_goal] = @user_input[:original_goal]
|
|
364
|
+
when :hybrid
|
|
365
|
+
collect_execute_details # Hybrid often needs project details
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Update the workflow selection with collected input
|
|
369
|
+
workflow_selection[:user_input] = @user_input
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def collect_execute_details
|
|
373
|
+
return if @user_input[:project_description] # Already collected
|
|
374
|
+
|
|
375
|
+
display_message("\nš Project Information", type: :highlight)
|
|
376
|
+
display_message("Let me gather some details for the PRD:\n", type: :info)
|
|
377
|
+
|
|
378
|
+
@user_input[:project_description] = @prompt.ask(
|
|
379
|
+
"Describe what you're building (can reference your original goal):",
|
|
380
|
+
default: @user_input[:original_goal]
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
@user_input[:tech_stack] = @prompt.ask(
|
|
384
|
+
"Tech stack (e.g., Ruby/Rails, Node.js, Python)? [optional]",
|
|
385
|
+
required: false
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
@user_input[:target_users] = @prompt.ask(
|
|
389
|
+
"Who will use this? [optional]",
|
|
390
|
+
required: false
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
@user_input[:success_criteria] = @prompt.ask(
|
|
394
|
+
"How will you measure success? [optional]",
|
|
395
|
+
required: false
|
|
396
|
+
)
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aidp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bart Agapinan
|
|
@@ -250,8 +250,10 @@ files:
|
|
|
250
250
|
- lib/aidp/cli/checkpoint_command.rb
|
|
251
251
|
- lib/aidp/cli/first_run_wizard.rb
|
|
252
252
|
- lib/aidp/cli/jobs_command.rb
|
|
253
|
+
- lib/aidp/cli/mcp_dashboard.rb
|
|
253
254
|
- lib/aidp/cli/terminal_io.rb
|
|
254
255
|
- lib/aidp/config.rb
|
|
256
|
+
- lib/aidp/config/paths.rb
|
|
255
257
|
- lib/aidp/core_ext/class_attribute.rb
|
|
256
258
|
- lib/aidp/debug_logger.rb
|
|
257
259
|
- lib/aidp/debug_mixin.rb
|
|
@@ -274,6 +276,7 @@ files:
|
|
|
274
276
|
- lib/aidp/harness/error_handler.rb
|
|
275
277
|
- lib/aidp/harness/provider_config.rb
|
|
276
278
|
- lib/aidp/harness/provider_factory.rb
|
|
279
|
+
- lib/aidp/harness/provider_info.rb
|
|
277
280
|
- lib/aidp/harness/provider_manager.rb
|
|
278
281
|
- lib/aidp/harness/provider_type_checker.rb
|
|
279
282
|
- lib/aidp/harness/runner.rb
|
|
@@ -324,6 +327,7 @@ files:
|
|
|
324
327
|
- lib/aidp/util.rb
|
|
325
328
|
- lib/aidp/version.rb
|
|
326
329
|
- lib/aidp/workflows/definitions.rb
|
|
330
|
+
- lib/aidp/workflows/guided_agent.rb
|
|
327
331
|
- lib/aidp/workflows/selector.rb
|
|
328
332
|
- templates/COMMON/AGENT_BASE.md
|
|
329
333
|
- templates/COMMON/CONVENTIONS.md
|