aidp 0.16.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/error_handler.rb +32 -13
- data/lib/aidp/analyze/kb_inspector.rb +2 -3
- data/lib/aidp/analyze/progress.rb +6 -11
- data/lib/aidp/cli/mcp_dashboard.rb +1 -1
- data/lib/aidp/cli.rb +300 -33
- data/lib/aidp/config.rb +1 -1
- data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
- data/lib/aidp/execute/checkpoint.rb +1 -1
- data/lib/aidp/execute/future_work_backlog.rb +1 -1
- data/lib/aidp/execute/progress.rb +6 -9
- data/lib/aidp/execute/repl_macros.rb +79 -10
- data/lib/aidp/harness/config_loader.rb +2 -2
- data/lib/aidp/harness/config_validator.rb +1 -1
- 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 +8 -2
- 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/registry.rb +31 -29
- data/lib/aidp/skills/router.rb +178 -0
- data/lib/aidp/skills/wizard/builder.rb +141 -0
- data/lib/aidp/skills/wizard/controller.rb +145 -0
- data/lib/aidp/skills/wizard/differ.rb +232 -0
- data/lib/aidp/skills/wizard/prompter.rb +317 -0
- data/lib/aidp/skills/wizard/template_library.rb +164 -0
- data/lib/aidp/skills/wizard/writer.rb +105 -0
- 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
- data/templates/skills/README.md +334 -0
- data/templates/skills/architecture_analyst/SKILL.md +173 -0
- data/templates/skills/product_strategist/SKILL.md +141 -0
- data/templates/skills/repository_analyst/SKILL.md +117 -0
- data/templates/skills/test_analyzer/SKILL.md +213 -0
- metadata +13 -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
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# AIDP Skills System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Skills define **WHO** the agent is (persona, expertise, capabilities), separate from templates/procedures which define **WHAT** task to execute.
|
|
6
|
+
|
|
7
|
+
This separation allows for:
|
|
8
|
+
|
|
9
|
+
- Reusable personas across multiple tasks
|
|
10
|
+
- Provider-agnostic skill definitions
|
|
11
|
+
- Clear distinction between agent identity and task execution
|
|
12
|
+
- Easier customization and overriding of agent behaviors
|
|
13
|
+
|
|
14
|
+
## Skill Structure
|
|
15
|
+
|
|
16
|
+
Each skill is a directory containing a `SKILL.md` file:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
skills/
|
|
20
|
+
āāā repository_analyst/
|
|
21
|
+
āāā SKILL.md
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### SKILL.md Format
|
|
25
|
+
|
|
26
|
+
Skills use YAML frontmatter for metadata and markdown for content:
|
|
27
|
+
|
|
28
|
+
```markdown
|
|
29
|
+
---
|
|
30
|
+
id: repository_analyst
|
|
31
|
+
name: Repository Analyst
|
|
32
|
+
description: Expert in version control analysis and code evolution patterns
|
|
33
|
+
version: 1.0.0
|
|
34
|
+
expertise:
|
|
35
|
+
- version control system analysis (Git, SVN, etc.)
|
|
36
|
+
- code churn analysis and hotspots identification
|
|
37
|
+
keywords:
|
|
38
|
+
- git
|
|
39
|
+
- metrics
|
|
40
|
+
- hotspots
|
|
41
|
+
when_to_use:
|
|
42
|
+
- Analyzing repository history
|
|
43
|
+
- Identifying technical debt through metrics
|
|
44
|
+
when_not_to_use:
|
|
45
|
+
- Writing new code
|
|
46
|
+
- Debugging runtime issues
|
|
47
|
+
compatible_providers:
|
|
48
|
+
- anthropic
|
|
49
|
+
- openai
|
|
50
|
+
- cursor
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Repository Analyst
|
|
54
|
+
|
|
55
|
+
You are a **Repository Analyst**, an expert in version control analysis...
|
|
56
|
+
|
|
57
|
+
## Your Core Capabilities
|
|
58
|
+
|
|
59
|
+
### Version Control Analysis
|
|
60
|
+
- Analyze commit history...
|
|
61
|
+
|
|
62
|
+
## Analysis Philosophy
|
|
63
|
+
|
|
64
|
+
**Data-Driven**: Base all recommendations on actual repository metrics...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Required Frontmatter Fields
|
|
68
|
+
|
|
69
|
+
| Field | Type | Description |
|
|
70
|
+
|-------|------|-------------|
|
|
71
|
+
| `id` | String | Unique identifier (lowercase, alphanumeric, underscores only) |
|
|
72
|
+
| `name` | String | Human-readable name |
|
|
73
|
+
| `description` | String | Brief one-line description |
|
|
74
|
+
| `version` | String | Semantic version (X.Y.Z format) |
|
|
75
|
+
|
|
76
|
+
## Optional Frontmatter Fields
|
|
77
|
+
|
|
78
|
+
| Field | Type | Description |
|
|
79
|
+
|-------|------|-------------|
|
|
80
|
+
| `expertise` | Array | List of expertise areas |
|
|
81
|
+
| `keywords` | Array | Search/filter keywords |
|
|
82
|
+
| `when_to_use` | Array | Guidance for when to use this skill |
|
|
83
|
+
| `when_not_to_use` | Array | Guidance for when NOT to use this skill |
|
|
84
|
+
| `compatible_providers` | Array | Compatible provider names (empty = all) |
|
|
85
|
+
|
|
86
|
+
## Skill Locations
|
|
87
|
+
|
|
88
|
+
### Template Skills
|
|
89
|
+
|
|
90
|
+
Located in `templates/skills/` in the AIDP gem. These are read-only templates installed with the AIDP gem and cover common use cases; they cannot be modified directly in your project.
|
|
91
|
+
|
|
92
|
+
- **repository_analyst**: Version control and code evolution analysis
|
|
93
|
+
- **product_strategist**: Product planning and requirements gathering
|
|
94
|
+
- **architecture_analyst**: Architecture analysis and pattern identification
|
|
95
|
+
- **test_analyzer**: Test suite analysis and quality assessment
|
|
96
|
+
|
|
97
|
+
### Project Skills
|
|
98
|
+
|
|
99
|
+
Located in `.aidp/skills/` for project-specific skills:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
.aidp/
|
|
103
|
+
āāā skills/
|
|
104
|
+
āāā my_custom_skill/
|
|
105
|
+
āāā SKILL.md
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Project skills with matching IDs override template skills.
|
|
109
|
+
|
|
110
|
+
## Using Skills
|
|
111
|
+
|
|
112
|
+
### In Step Specifications
|
|
113
|
+
|
|
114
|
+
Reference skills in step specs (e.g., [analyze/steps.rb](lib/aidp/analyze/steps.rb#L6-L60)):
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
SPEC = {
|
|
118
|
+
"01_REPOSITORY_ANALYSIS" => {
|
|
119
|
+
"skill" => "repository_analyst",
|
|
120
|
+
"templates" => ["analysis/analyze_repository.md"],
|
|
121
|
+
"description" => "Repository mining",
|
|
122
|
+
"outs" => ["docs/analysis/repository_analysis.md"],
|
|
123
|
+
"gate" => false
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Programmatic Access
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# Load skills registry
|
|
132
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
|
133
|
+
registry.load_skills
|
|
134
|
+
|
|
135
|
+
# Find a skill
|
|
136
|
+
skill = registry.find("repository_analyst")
|
|
137
|
+
|
|
138
|
+
# Search skills
|
|
139
|
+
matching_skills = registry.search("git")
|
|
140
|
+
|
|
141
|
+
# Filter by keyword
|
|
142
|
+
analysis_skills = registry.by_keyword("analysis")
|
|
143
|
+
|
|
144
|
+
# Check provider compatibility
|
|
145
|
+
compatible = registry.compatible_with("anthropic")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Composing with Templates
|
|
149
|
+
|
|
150
|
+
Skills are automatically composed with templates by the runner:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
# In runner
|
|
154
|
+
skill = skills_registry.find(step_spec["skill"])
|
|
155
|
+
template = File.read(template_path)
|
|
156
|
+
|
|
157
|
+
# Compose skill + template
|
|
158
|
+
composed_prompt = @skills_composer.compose(
|
|
159
|
+
skill: skill,
|
|
160
|
+
template: template,
|
|
161
|
+
options: { variable: "value" }
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The composition structure is:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
1. Skill content (persona, expertise, philosophy)
|
|
169
|
+
2. Separator (---)
|
|
170
|
+
3. "# Current Task" header
|
|
171
|
+
4. Template content (task-specific instructions)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Configuration
|
|
175
|
+
|
|
176
|
+
Configure skills in `.aidp/aidp.yml`:
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
skills:
|
|
180
|
+
search_paths: [] # Additional skill search paths (optional)
|
|
181
|
+
default_provider_filter: true # Filter by provider compatibility
|
|
182
|
+
enable_custom_skills: true # Enable custom skill overrides
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Provider Compatibility
|
|
186
|
+
|
|
187
|
+
Skills can declare compatible providers in frontmatter:
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
compatible_providers:
|
|
191
|
+
- anthropic
|
|
192
|
+
- openai
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- Empty list = compatible with all providers
|
|
196
|
+
- Registry filters skills by provider when initialized
|
|
197
|
+
- Incompatible skills are skipped during loading
|
|
198
|
+
|
|
199
|
+
## Best Practices
|
|
200
|
+
|
|
201
|
+
### Skill Design
|
|
202
|
+
|
|
203
|
+
1. **Focus on WHO, not WHAT**: Skills define agent identity, not task steps
|
|
204
|
+
2. **Be specific**: Clearly describe expertise areas and capabilities
|
|
205
|
+
3. **Provide guidance**: Use `when_to_use` and `when_not_to_use` to help with selection
|
|
206
|
+
4. **Version carefully**: Use semantic versioning for tracking changes
|
|
207
|
+
5. **Test compatibility**: Verify skills work with intended providers
|
|
208
|
+
|
|
209
|
+
### Naming Conventions
|
|
210
|
+
|
|
211
|
+
- **ID**: `lowercase_with_underscores` (e.g., `repository_analyst`)
|
|
212
|
+
- **Name**: `Title Case` (e.g., `Repository Analyst`)
|
|
213
|
+
- **Files**: Always name the file `SKILL.md` (uppercase)
|
|
214
|
+
|
|
215
|
+
### Content Guidelines
|
|
216
|
+
|
|
217
|
+
From the [LLM_STYLE_GUIDE](../docs/LLM_STYLE_GUIDE.md#L1-L202):
|
|
218
|
+
|
|
219
|
+
- Use clear, professional language
|
|
220
|
+
- Organize with markdown headers
|
|
221
|
+
- Bullet points for lists of capabilities
|
|
222
|
+
- Explain philosophy and approach
|
|
223
|
+
- Provide concrete examples when helpful
|
|
224
|
+
|
|
225
|
+
## Creating a New Skill
|
|
226
|
+
|
|
227
|
+
1. **Create directory structure**:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
mkdir -p skills/my_skill
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
2. **Create SKILL.md**:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
touch skills/my_skill/SKILL.md
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
3. **Add frontmatter and content**:
|
|
240
|
+
|
|
241
|
+
```markdown
|
|
242
|
+
---
|
|
243
|
+
id: my_skill
|
|
244
|
+
name: My Skill Name
|
|
245
|
+
description: Brief description
|
|
246
|
+
version: 1.0.0
|
|
247
|
+
expertise:
|
|
248
|
+
- Area 1
|
|
249
|
+
- Area 2
|
|
250
|
+
keywords:
|
|
251
|
+
- keyword1
|
|
252
|
+
when_to_use:
|
|
253
|
+
- Situation 1
|
|
254
|
+
when_not_to_use:
|
|
255
|
+
- Situation 2
|
|
256
|
+
compatible_providers:
|
|
257
|
+
- anthropic
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
# My Skill Name
|
|
261
|
+
|
|
262
|
+
You are a **My Skill Name**, an expert in...
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
4. **Reference in steps**:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
"MY_STEP" => {
|
|
269
|
+
"skill" => "my_skill",
|
|
270
|
+
"templates" => ["path/to/template.md"],
|
|
271
|
+
...
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Architecture
|
|
276
|
+
|
|
277
|
+
### Core Components
|
|
278
|
+
|
|
279
|
+
- **[Skill](lib/aidp/skills/skill.rb#L1-L187)**: Model representing a skill
|
|
280
|
+
- **[Loader](lib/aidp/skills/loader.rb#L1-L179)**: Parses SKILL.md files
|
|
281
|
+
- **[Registry](lib/aidp/skills/registry.rb#L1-L213)**: Manages available skills
|
|
282
|
+
- **[Composer](lib/aidp/skills/composer.rb#L1-L162)**: Combines skills with templates
|
|
283
|
+
|
|
284
|
+
### Integration Points
|
|
285
|
+
|
|
286
|
+
- **[Analyze Runner](lib/aidp/analyze/runner.rb#L198-L236)**: Uses skills in analysis mode
|
|
287
|
+
- **[Execute Runner](lib/aidp/execute/runner.rb#L320-L355)**: Uses skills in execution mode
|
|
288
|
+
- **[Config](lib/aidp/config.rb#L166-L170)**: Skills configuration support
|
|
289
|
+
|
|
290
|
+
## Future Enhancements
|
|
291
|
+
|
|
292
|
+
Planned for future versions (out of scope for v1):
|
|
293
|
+
|
|
294
|
+
- **Skill Inheritance**: Skills extending other skills
|
|
295
|
+
- **Skill Composition**: Combining multiple skills for complex tasks
|
|
296
|
+
- **AI-Powered Selection**: Automatically selecting best skill for a task
|
|
297
|
+
- **Skill Marketplace**: Sharing skills across teams/organizations
|
|
298
|
+
- **Dynamic Generation**: Creating skills from examples
|
|
299
|
+
- **Execution Validation**: Checking if output matches skill expectations
|
|
300
|
+
|
|
301
|
+
## Related Documentation
|
|
302
|
+
|
|
303
|
+
- [PRD: Skills System](../docs/prd_skills_system.md) - Product requirements and architecture
|
|
304
|
+
- [LLM Style Guide](../docs/LLM_STYLE_GUIDE.md) - Coding standards for skills content
|
|
305
|
+
- [Issue #148](https://github.com/viamin/aidp/issues/148) - Original feature request
|
|
306
|
+
|
|
307
|
+
## Troubleshooting
|
|
308
|
+
|
|
309
|
+
### Skill Not Found
|
|
310
|
+
|
|
311
|
+
If a skill is referenced but not found:
|
|
312
|
+
|
|
313
|
+
1. Check the skill ID matches exactly (case-sensitive in SPEC, but lowercase in file)
|
|
314
|
+
2. Verify the SKILL.md file exists in the correct directory
|
|
315
|
+
3. Check for YAML syntax errors in frontmatter
|
|
316
|
+
4. Review logs for loading errors
|
|
317
|
+
|
|
318
|
+
### Provider Compatibility Issues
|
|
319
|
+
|
|
320
|
+
If skills aren't loading for a provider:
|
|
321
|
+
|
|
322
|
+
1. Check `compatible_providers` in frontmatter
|
|
323
|
+
2. Verify provider name matches exactly
|
|
324
|
+
3. Check `default_provider_filter` in config
|
|
325
|
+
4. Review registry initialization logs
|
|
326
|
+
|
|
327
|
+
### Validation Errors
|
|
328
|
+
|
|
329
|
+
Common validation errors:
|
|
330
|
+
|
|
331
|
+
- **"id must be lowercase"**: Use only lowercase letters, numbers, underscores
|
|
332
|
+
- **"version must be in format X.Y.Z"**: Use semantic versioning (e.g., "1.0.0")
|
|
333
|
+
- **"YAML frontmatter missing"**: Ensure `---` delimiters are present
|
|
334
|
+
- **Missing required field**: Add required frontmatter fields (id, name, description, version)
|