aidp 0.10.0 → 0.12.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 +194 -25
- data/lib/aidp/analyze/kb_inspector.rb +2 -15
- data/lib/aidp/analyze/progress.rb +2 -1
- data/lib/aidp/analyze/ruby_maat_integration.rb +2 -15
- data/lib/aidp/analyze/runner.rb +64 -20
- data/lib/aidp/analyze/steps.rb +10 -8
- data/lib/aidp/analyze/tree_sitter_grammar_loader.rb +2 -13
- data/lib/aidp/analyze/tree_sitter_scan.rb +2 -13
- data/lib/aidp/cli/checkpoint_command.rb +98 -0
- data/lib/aidp/cli/first_run_wizard.rb +65 -94
- data/lib/aidp/cli/jobs_command.rb +249 -34
- data/lib/aidp/cli/mcp_dashboard.rb +205 -0
- data/lib/aidp/cli.rb +517 -43
- data/lib/aidp/config.rb +5 -8
- data/lib/aidp/debug_logger.rb +4 -4
- data/lib/aidp/debug_mixin.rb +11 -4
- data/lib/aidp/execute/checkpoint.rb +282 -0
- data/lib/aidp/execute/checkpoint_display.rb +221 -0
- data/lib/aidp/execute/progress.rb +2 -1
- data/lib/aidp/execute/prompt_manager.rb +62 -0
- data/lib/aidp/execute/runner.rb +53 -24
- data/lib/aidp/execute/steps.rb +36 -27
- data/lib/aidp/execute/work_loop_runner.rb +308 -0
- data/lib/aidp/execute/workflow_selector.rb +26 -17
- data/lib/aidp/harness/condition_detector.rb +4 -4
- data/lib/aidp/harness/config_schema.rb +40 -0
- data/lib/aidp/harness/config_validator.rb +3 -6
- data/lib/aidp/harness/configuration.rb +35 -1
- data/lib/aidp/harness/enhanced_runner.rb +22 -1
- data/lib/aidp/harness/error_handler.rb +103 -28
- data/lib/aidp/harness/provider_factory.rb +4 -1
- data/lib/aidp/harness/provider_info.rb +366 -0
- data/lib/aidp/harness/provider_manager.rb +250 -15
- data/lib/aidp/harness/runner.rb +3 -14
- data/lib/aidp/harness/simple_user_interface.rb +2 -15
- data/lib/aidp/harness/status_display.rb +12 -17
- data/lib/aidp/harness/test_runner.rb +83 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +2 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +44 -5
- data/lib/aidp/harness/ui/error_handler.rb +4 -0
- data/lib/aidp/harness/ui/frame_manager.rb +10 -8
- data/lib/aidp/harness/ui/job_monitor.rb +2 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +4 -2
- data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
- data/lib/aidp/harness/ui/progress_display.rb +8 -12
- data/lib/aidp/harness/ui/question_collector.rb +2 -0
- data/lib/aidp/harness/ui/spinner_group.rb +2 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
- data/lib/aidp/harness/ui/status_manager.rb +4 -2
- data/lib/aidp/harness/ui/status_widget.rb +3 -1
- data/lib/aidp/harness/ui/workflow_controller.rb +2 -0
- data/lib/aidp/harness/user_interface.rb +12 -17
- data/lib/aidp/jobs/background_runner.rb +278 -0
- data/lib/aidp/message_display.rb +48 -0
- data/lib/aidp/provider_manager.rb +3 -1
- data/lib/aidp/providers/anthropic.rb +100 -17
- data/lib/aidp/providers/base.rb +42 -11
- data/lib/aidp/providers/codex.rb +248 -0
- data/lib/aidp/providers/cursor.rb +35 -42
- data/lib/aidp/providers/gemini.rb +25 -15
- data/lib/aidp/providers/github_copilot.rb +41 -42
- data/lib/aidp/providers/opencode.rb +34 -41
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/workflows/definitions.rb +357 -0
- data/lib/aidp/workflows/guided_agent.rb +400 -0
- data/lib/aidp/workflows/selector.rb +171 -0
- data/lib/aidp.rb +12 -0
- data/templates/planning/generate_llm_style_guide.md +119 -0
- metadata +41 -26
- /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
- /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
- /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
- /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
- /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
- /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
- /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
- /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
- /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
- /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
- /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
- /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
- /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
- /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
- /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
- /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
- /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
- /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
- /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
- /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
- /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
- /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
- /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
- /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
- /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
data/lib/aidp/cli.rb
CHANGED
|
@@ -12,6 +12,8 @@ require_relative "cli/first_run_wizard"
|
|
|
12
12
|
module Aidp
|
|
13
13
|
# CLI interface for AIDP
|
|
14
14
|
class CLI
|
|
15
|
+
include Aidp::MessageDisplay
|
|
16
|
+
|
|
15
17
|
# Simple options holder for instance methods (used in specs)
|
|
16
18
|
attr_accessor :options
|
|
17
19
|
|
|
@@ -20,21 +22,6 @@ module Aidp
|
|
|
20
22
|
@prompt = prompt
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
# Helper method for consistent message display using TTY::Prompt
|
|
24
|
-
def display_message(message, type: :info)
|
|
25
|
-
color = case type
|
|
26
|
-
when :error then :red
|
|
27
|
-
when :success then :green
|
|
28
|
-
when :warning then :yellow
|
|
29
|
-
when :info then :blue
|
|
30
|
-
when :highlight then :cyan
|
|
31
|
-
when :muted then :bright_black
|
|
32
|
-
else :white
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
@prompt.say(message, color: color)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
25
|
# Instance version of harness status (used by specs; non-interactive)
|
|
39
26
|
def harness_status
|
|
40
27
|
modes = %i[analyze execute]
|
|
@@ -68,9 +55,9 @@ module Aidp
|
|
|
68
55
|
status = if options[:expect_error] == true
|
|
69
56
|
"error"
|
|
70
57
|
elsif step.nil?
|
|
71
|
-
"success"
|
|
58
|
+
"success" # Initial call without step
|
|
72
59
|
else
|
|
73
|
-
"completed"
|
|
60
|
+
"completed" # Subsequent calls with specific step
|
|
74
61
|
end
|
|
75
62
|
|
|
76
63
|
{
|
|
@@ -154,20 +141,7 @@ module Aidp
|
|
|
154
141
|
end
|
|
155
142
|
|
|
156
143
|
class << self
|
|
157
|
-
|
|
158
|
-
def display_message(message, type: :info)
|
|
159
|
-
prompt = TTY::Prompt.new
|
|
160
|
-
color = case type
|
|
161
|
-
when :error then :red
|
|
162
|
-
when :success then :green
|
|
163
|
-
when :warning then :yellow
|
|
164
|
-
when :info then :blue
|
|
165
|
-
when :highlight then :cyan
|
|
166
|
-
when :muted then :bright_black
|
|
167
|
-
else :white
|
|
168
|
-
end
|
|
169
|
-
prompt.say(message, color: color)
|
|
170
|
-
end
|
|
144
|
+
extend Aidp::MessageDisplay::ClassMethods
|
|
171
145
|
|
|
172
146
|
def run(args = ARGV)
|
|
173
147
|
# Handle subcommands first (status, jobs, kb, harness)
|
|
@@ -209,7 +183,7 @@ module Aidp
|
|
|
209
183
|
|
|
210
184
|
# Initialize the enhanced TUI
|
|
211
185
|
tui = Aidp::Harness::UI::EnhancedTUI.new
|
|
212
|
-
workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui)
|
|
186
|
+
workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
|
|
213
187
|
|
|
214
188
|
# Start TUI display loop
|
|
215
189
|
tui.start_display_loop
|
|
@@ -251,15 +225,67 @@ module Aidp
|
|
|
251
225
|
options = {}
|
|
252
226
|
|
|
253
227
|
parser = OptionParser.new do |opts|
|
|
254
|
-
opts.banner = "Usage: aidp [options]"
|
|
228
|
+
opts.banner = "Usage: aidp [COMMAND] [options]"
|
|
229
|
+
opts.separator ""
|
|
230
|
+
opts.separator "AI Development Pipeline - Autonomous development workflow automation"
|
|
255
231
|
opts.separator ""
|
|
256
|
-
opts.separator "
|
|
232
|
+
opts.separator "Commands:"
|
|
233
|
+
opts.separator " analyze [--background] Start analyze mode workflow"
|
|
234
|
+
opts.separator " execute [--background] Start execute mode workflow"
|
|
235
|
+
opts.separator " status Show current system status"
|
|
236
|
+
opts.separator " jobs Manage background jobs"
|
|
237
|
+
opts.separator " list - List all jobs"
|
|
238
|
+
opts.separator " status <id> [--follow] - Show job status"
|
|
239
|
+
opts.separator " logs <id> [--tail] - Show job logs"
|
|
240
|
+
opts.separator " stop <id> - Stop a running job"
|
|
241
|
+
opts.separator " checkpoint View progress checkpoints and metrics"
|
|
242
|
+
opts.separator " show - Show latest checkpoint"
|
|
243
|
+
opts.separator " summary [--watch] - Show progress summary with trends"
|
|
244
|
+
opts.separator " history [N] - Show last N checkpoints"
|
|
245
|
+
opts.separator " metrics - Show detailed metrics"
|
|
246
|
+
opts.separator " clear [--force] - Clear checkpoint data"
|
|
247
|
+
opts.separator " providers Show provider health dashboard"
|
|
248
|
+
opts.separator " info <name> - Show detailed provider information"
|
|
249
|
+
opts.separator " refresh [name] - Refresh provider capabilities info"
|
|
250
|
+
opts.separator " mcp MCP server dashboard and management"
|
|
251
|
+
opts.separator " dashboard - Show all MCP servers across providers"
|
|
252
|
+
opts.separator " check <servers...> - Check provider eligibility for servers"
|
|
253
|
+
opts.separator " harness Manage harness state"
|
|
254
|
+
opts.separator " status - Show harness status"
|
|
255
|
+
opts.separator " reset - Reset harness state"
|
|
256
|
+
opts.separator " kb Knowledge base commands"
|
|
257
|
+
opts.separator " show <topic> - Show knowledge base topic"
|
|
257
258
|
opts.separator ""
|
|
258
259
|
opts.separator "Options:"
|
|
259
260
|
|
|
260
261
|
opts.on("-h", "--help", "Show this help message") { options[:help] = true }
|
|
261
262
|
opts.on("-v", "--version", "Show version information") { options[:version] = true }
|
|
262
|
-
opts.on("--setup-config", "Setup or reconfigure config file
|
|
263
|
+
opts.on("--setup-config", "Setup or reconfigure config file") { options[:setup_config] = true }
|
|
264
|
+
|
|
265
|
+
opts.separator ""
|
|
266
|
+
opts.separator "Examples:"
|
|
267
|
+
opts.separator " # Start background execution"
|
|
268
|
+
opts.separator " aidp execute --background"
|
|
269
|
+
opts.separator " aidp execute --background --follow # Start and follow logs"
|
|
270
|
+
opts.separator ""
|
|
271
|
+
opts.separator " # Monitor background jobs"
|
|
272
|
+
opts.separator " aidp jobs list # List all jobs"
|
|
273
|
+
opts.separator " aidp jobs status <job_id> # Show job status"
|
|
274
|
+
opts.separator " aidp jobs logs <job_id> --tail # Tail job logs"
|
|
275
|
+
opts.separator ""
|
|
276
|
+
opts.separator " # Watch progress in real-time"
|
|
277
|
+
opts.separator " aidp checkpoint summary --watch # Auto-refresh every 5s"
|
|
278
|
+
opts.separator " aidp checkpoint summary --watch --interval 10"
|
|
279
|
+
opts.separator ""
|
|
280
|
+
opts.separator " # Other commands"
|
|
281
|
+
opts.separator " aidp providers # Check provider health"
|
|
282
|
+
opts.separator " aidp providers info claude # Show detailed provider info"
|
|
283
|
+
opts.separator " aidp providers refresh # Refresh all provider info"
|
|
284
|
+
opts.separator " aidp mcp # Show MCP server dashboard"
|
|
285
|
+
opts.separator " aidp mcp check dash-api filesystem # Check provider eligibility"
|
|
286
|
+
opts.separator " aidp checkpoint history 20 # Show last 20 checkpoints"
|
|
287
|
+
opts.separator ""
|
|
288
|
+
opts.separator "For more information, visit: https://github.com/viamin/aidp"
|
|
263
289
|
end
|
|
264
290
|
|
|
265
291
|
parser.parse!(args)
|
|
@@ -270,18 +296,21 @@ module Aidp
|
|
|
270
296
|
# Determine if the invocation is a subcommand style call
|
|
271
297
|
def subcommand?(args)
|
|
272
298
|
return false if args.nil? || args.empty?
|
|
273
|
-
%w[status jobs kb harness execute analyze].include?(args.first)
|
|
299
|
+
%w[status jobs kb harness execute analyze providers checkpoint mcp].include?(args.first)
|
|
274
300
|
end
|
|
275
301
|
|
|
276
302
|
def run_subcommand(args)
|
|
277
303
|
cmd = args.shift
|
|
278
304
|
case cmd
|
|
279
305
|
when "status" then run_status_command
|
|
280
|
-
when "jobs" then run_jobs_command
|
|
306
|
+
when "jobs" then run_jobs_command(args)
|
|
281
307
|
when "kb" then run_kb_command(args)
|
|
282
308
|
when "harness" then run_harness_command(args)
|
|
283
309
|
when "execute" then run_execute_command(args)
|
|
284
310
|
when "analyze" then run_execute_command(args, mode: :analyze) # symmetry
|
|
311
|
+
when "providers" then run_providers_command(args)
|
|
312
|
+
when "checkpoint" then run_checkpoint_command(args)
|
|
313
|
+
when "mcp" then run_mcp_command(args)
|
|
285
314
|
else
|
|
286
315
|
display_message("Unknown command: #{cmd}", type: :info)
|
|
287
316
|
return 1
|
|
@@ -298,10 +327,11 @@ module Aidp
|
|
|
298
327
|
display_message("Use 'aidp analyze' or 'aidp execute' to start a workflow", type: :info)
|
|
299
328
|
end
|
|
300
329
|
|
|
301
|
-
def run_jobs_command
|
|
330
|
+
def run_jobs_command(args = [])
|
|
302
331
|
require_relative "cli/jobs_command"
|
|
303
332
|
jobs_cmd = Aidp::CLI::JobsCommand.new(prompt: TTY::Prompt.new)
|
|
304
|
-
|
|
333
|
+
subcommand = args.shift
|
|
334
|
+
jobs_cmd.run(subcommand, args)
|
|
305
335
|
end
|
|
306
336
|
|
|
307
337
|
def run_kb_command(args)
|
|
@@ -336,6 +366,8 @@ module Aidp
|
|
|
336
366
|
approve_step = nil
|
|
337
367
|
reset = false
|
|
338
368
|
no_harness = false
|
|
369
|
+
background = false
|
|
370
|
+
follow = false
|
|
339
371
|
|
|
340
372
|
until flags.empty?
|
|
341
373
|
token = flags.shift
|
|
@@ -343,6 +375,8 @@ module Aidp
|
|
|
343
375
|
when "--no-harness" then no_harness = true
|
|
344
376
|
when "--reset" then reset = true
|
|
345
377
|
when "--approve" then approve_step = flags.shift
|
|
378
|
+
when "--background" then background = true
|
|
379
|
+
when "--follow" then follow = true
|
|
346
380
|
else
|
|
347
381
|
step ||= token unless token.start_with?("--")
|
|
348
382
|
end
|
|
@@ -361,6 +395,35 @@ module Aidp
|
|
|
361
395
|
display_message("Use 'aidp #{mode}' without arguments", type: :info)
|
|
362
396
|
return
|
|
363
397
|
end
|
|
398
|
+
|
|
399
|
+
# Handle background execution
|
|
400
|
+
if background
|
|
401
|
+
require_relative "jobs/background_runner"
|
|
402
|
+
runner = Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
|
|
403
|
+
|
|
404
|
+
display_message("Starting #{mode} mode in background...", type: :info)
|
|
405
|
+
job_id = runner.start(mode, {})
|
|
406
|
+
|
|
407
|
+
display_message("✓ Started background job: #{job_id}", type: :success)
|
|
408
|
+
display_message("", type: :info)
|
|
409
|
+
display_message("Monitor progress:", type: :info)
|
|
410
|
+
display_message(" aidp jobs status #{job_id}", type: :info)
|
|
411
|
+
display_message(" aidp jobs logs #{job_id} --tail", type: :info)
|
|
412
|
+
display_message(" aidp checkpoint summary", type: :info)
|
|
413
|
+
display_message("", type: :info)
|
|
414
|
+
display_message("Stop the job:", type: :info)
|
|
415
|
+
display_message(" aidp jobs stop #{job_id}", type: :info)
|
|
416
|
+
|
|
417
|
+
if follow
|
|
418
|
+
display_message("", type: :info)
|
|
419
|
+
display_message("Following logs (Ctrl+C to stop following)...", type: :info)
|
|
420
|
+
sleep 1 # Give daemon time to start writing logs
|
|
421
|
+
runner.follow_job_logs(job_id)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
return
|
|
425
|
+
end
|
|
426
|
+
|
|
364
427
|
if step
|
|
365
428
|
display_message("Running #{mode} step '#{step}' with enhanced TUI harness", type: :highlight)
|
|
366
429
|
display_message("progress indicators", type: :info)
|
|
@@ -386,6 +449,414 @@ module Aidp
|
|
|
386
449
|
display_message("workflow selection options", type: :info)
|
|
387
450
|
end
|
|
388
451
|
|
|
452
|
+
def run_checkpoint_command(args)
|
|
453
|
+
require_relative "execute/checkpoint"
|
|
454
|
+
require_relative "execute/checkpoint_display"
|
|
455
|
+
|
|
456
|
+
sub = args.shift || "summary"
|
|
457
|
+
checkpoint = Aidp::Execute::Checkpoint.new(Dir.pwd)
|
|
458
|
+
display = Aidp::Execute::CheckpointDisplay.new
|
|
459
|
+
|
|
460
|
+
case sub
|
|
461
|
+
when "show"
|
|
462
|
+
latest = checkpoint.latest_checkpoint
|
|
463
|
+
if latest
|
|
464
|
+
display.display_checkpoint(latest, show_details: true)
|
|
465
|
+
else
|
|
466
|
+
display_message("No checkpoint data found.", type: :info)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
when "summary"
|
|
470
|
+
watch = args.include?("--watch")
|
|
471
|
+
interval = extract_interval_option(args) || 5
|
|
472
|
+
|
|
473
|
+
if watch
|
|
474
|
+
watch_checkpoint_summary(checkpoint, display, interval)
|
|
475
|
+
else
|
|
476
|
+
summary = checkpoint.progress_summary
|
|
477
|
+
if summary
|
|
478
|
+
display.display_progress_summary(summary)
|
|
479
|
+
else
|
|
480
|
+
display_message("No checkpoint data found.", type: :info)
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
when "history"
|
|
485
|
+
limit = args.shift || "10"
|
|
486
|
+
history = checkpoint.checkpoint_history(limit: limit.to_i)
|
|
487
|
+
if history.any?
|
|
488
|
+
display.display_checkpoint_history(history, limit: limit.to_i)
|
|
489
|
+
else
|
|
490
|
+
display_message("No checkpoint history found.", type: :info)
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
when "metrics"
|
|
494
|
+
latest = checkpoint.latest_checkpoint
|
|
495
|
+
unless latest
|
|
496
|
+
display_message("No checkpoint data found.", type: :info)
|
|
497
|
+
return
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
display_message("", type: :info)
|
|
501
|
+
display_message("📊 Detailed Metrics", type: :info)
|
|
502
|
+
display_message("=" * 60, type: :muted)
|
|
503
|
+
|
|
504
|
+
metrics = latest[:metrics]
|
|
505
|
+
display_message("Lines of Code: #{metrics[:lines_of_code]}", type: :info)
|
|
506
|
+
display_message("File Count: #{metrics[:file_count]}", type: :info)
|
|
507
|
+
display_message("Test Coverage: #{metrics[:test_coverage]}%", type: :info)
|
|
508
|
+
display_message("Code Quality: #{metrics[:code_quality]}%", type: :info)
|
|
509
|
+
display_message("PRD Task Progress: #{metrics[:prd_task_progress]}%", type: :info)
|
|
510
|
+
|
|
511
|
+
if metrics[:tests_passing]
|
|
512
|
+
status = metrics[:tests_passing] ? "✓ Passing" : "✗ Failing"
|
|
513
|
+
display_message("Tests: #{status}", type: :info)
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
if metrics[:linters_passing]
|
|
517
|
+
status = metrics[:linters_passing] ? "✓ Passing" : "✗ Failing"
|
|
518
|
+
display_message("Linters: #{status}", type: :info)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
display_message("=" * 60, type: :muted)
|
|
522
|
+
display_message("", type: :info)
|
|
523
|
+
|
|
524
|
+
when "clear"
|
|
525
|
+
force = args.include?("--force")
|
|
526
|
+
unless force
|
|
527
|
+
prompt = TTY::Prompt.new
|
|
528
|
+
confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
|
|
529
|
+
return unless confirm
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
checkpoint.clear
|
|
533
|
+
display_message("✓ Checkpoint data cleared.", type: :success)
|
|
534
|
+
|
|
535
|
+
else
|
|
536
|
+
display_message("Usage: aidp checkpoint <show|summary|history|metrics|clear>", type: :info)
|
|
537
|
+
display_message(" show - Show the latest checkpoint data", type: :info)
|
|
538
|
+
display_message(" summary [--watch] - Show progress summary with trends", type: :info)
|
|
539
|
+
display_message(" history [N] - Show last N checkpoints", type: :info)
|
|
540
|
+
display_message(" metrics - Show detailed metrics", type: :info)
|
|
541
|
+
display_message(" clear [--force] - Clear all checkpoint data", type: :info)
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def watch_checkpoint_summary(checkpoint, display, interval)
|
|
546
|
+
display_message("Watching checkpoint summary (refresh: #{interval}s, Ctrl+C to exit)...", type: :info)
|
|
547
|
+
display_message("", type: :info)
|
|
548
|
+
|
|
549
|
+
begin
|
|
550
|
+
loop do
|
|
551
|
+
# Clear screen
|
|
552
|
+
print "\e[2J\e[H"
|
|
553
|
+
|
|
554
|
+
summary = checkpoint.progress_summary
|
|
555
|
+
if summary
|
|
556
|
+
display.display_progress_summary(summary)
|
|
557
|
+
|
|
558
|
+
# Show last update time
|
|
559
|
+
if summary[:current] && summary[:current][:timestamp]
|
|
560
|
+
last_update = Time.parse(summary[:current][:timestamp])
|
|
561
|
+
age = Time.now - last_update
|
|
562
|
+
display_message("", type: :info)
|
|
563
|
+
display_message("Last update: #{format_time_ago_simple(age)} | Refreshing in #{interval}s...", type: :muted)
|
|
564
|
+
end
|
|
565
|
+
else
|
|
566
|
+
display_message("No checkpoint data found. Waiting for data...", type: :info)
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
sleep interval
|
|
570
|
+
end
|
|
571
|
+
rescue Interrupt
|
|
572
|
+
display_message("\nStopped watching checkpoint summary", type: :info)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def extract_interval_option(args)
|
|
577
|
+
args.each_with_index do |arg, i|
|
|
578
|
+
if arg == "--interval" && args[i + 1]
|
|
579
|
+
return args[i + 1].to_i
|
|
580
|
+
elsif arg.start_with?("--interval=")
|
|
581
|
+
return arg.split("=")[1].to_i
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
nil
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def format_time_ago_simple(seconds)
|
|
588
|
+
if seconds < 60
|
|
589
|
+
"#{seconds.to_i}s ago"
|
|
590
|
+
elsif seconds < 3600
|
|
591
|
+
"#{(seconds / 60).to_i}m ago"
|
|
592
|
+
else
|
|
593
|
+
"#{(seconds / 3600).to_i}h ago"
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def run_providers_command(args)
|
|
598
|
+
subcommand = args.first if args.first && !args.first.start_with?("--")
|
|
599
|
+
|
|
600
|
+
case subcommand
|
|
601
|
+
when "info"
|
|
602
|
+
args.shift # Remove 'info'
|
|
603
|
+
run_providers_info_command(args)
|
|
604
|
+
return
|
|
605
|
+
when "refresh"
|
|
606
|
+
args.shift # Remove 'refresh'
|
|
607
|
+
run_providers_refresh_command(args)
|
|
608
|
+
return
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# Accept flags directly on `aidp providers` now (health is implicit)
|
|
612
|
+
no_color = false
|
|
613
|
+
args.reject! do |a|
|
|
614
|
+
if a == "--no-color"
|
|
615
|
+
no_color = true
|
|
616
|
+
true
|
|
617
|
+
else
|
|
618
|
+
false
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
configuration = Aidp::Harness::Configuration.new(Dir.pwd)
|
|
622
|
+
pm = Aidp::Harness::ProviderManager.new(configuration, prompt: TTY::Prompt.new)
|
|
623
|
+
|
|
624
|
+
# Use TTY::Spinner for progress indication
|
|
625
|
+
require "tty-spinner"
|
|
626
|
+
start_time = Time.now
|
|
627
|
+
spinner = TTY::Spinner.new(":spinner Gathering provider health...", format: :dots)
|
|
628
|
+
spinner.auto_spin
|
|
629
|
+
|
|
630
|
+
begin
|
|
631
|
+
rows = pm.health_dashboard
|
|
632
|
+
ensure
|
|
633
|
+
spinner.stop
|
|
634
|
+
elapsed = (Time.now - start_time).round(2)
|
|
635
|
+
display_message("Provider Health Dashboard (#{elapsed}s)", type: :highlight)
|
|
636
|
+
end
|
|
637
|
+
require "tty-table"
|
|
638
|
+
color = ->(text, code) { "\e[#{code}m#{text}\e[0m" }
|
|
639
|
+
status_color = lambda do |status|
|
|
640
|
+
case status
|
|
641
|
+
when /healthy/ then 32
|
|
642
|
+
when /unhealthy_auth/ then 31
|
|
643
|
+
when /unhealthy/ then 33
|
|
644
|
+
when /circuit/ then 35
|
|
645
|
+
else 37
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
availability_color = ->(avail) { (avail == "yes") ? 32 : 31 }
|
|
649
|
+
rate_color = ->(rl) { rl.start_with?("yes") ? 33 : 36 }
|
|
650
|
+
circuit_color = lambda do |c|
|
|
651
|
+
c.start_with?("open") ? 31 : 32
|
|
652
|
+
end
|
|
653
|
+
table_rows = rows.map do |r|
|
|
654
|
+
last_used = r[:last_used] ? r[:last_used].strftime("%H:%M:%S") : "-"
|
|
655
|
+
cb = r[:circuit_breaker]
|
|
656
|
+
cb += " (#{r[:circuit_breaker_remaining]}s)" if r[:circuit_breaker_remaining]
|
|
657
|
+
rl = if r[:rate_limited]
|
|
658
|
+
r[:rate_limit_reset_in] ? "yes (#{r[:rate_limit_reset_in]}s)" : "yes"
|
|
659
|
+
else
|
|
660
|
+
"no"
|
|
661
|
+
end
|
|
662
|
+
tokens = (r[:total_tokens].to_i > 0) ? r[:total_tokens].to_s : "0"
|
|
663
|
+
reason = r[:unhealthy_reason] || "-"
|
|
664
|
+
if no_color || !$stdout.tty?
|
|
665
|
+
[r[:provider], r[:status], (r[:available] ? "yes" : "no"), cb, rl, tokens, last_used, reason]
|
|
666
|
+
else
|
|
667
|
+
[
|
|
668
|
+
color.call(r[:provider], "1;97"),
|
|
669
|
+
color.call(r[:status], status_color.call(r[:status])),
|
|
670
|
+
color.call(r[:available] ? "yes" : "no", availability_color.call(r[:available] ? "yes" : "no")),
|
|
671
|
+
color.call(cb, circuit_color.call(cb)),
|
|
672
|
+
color.call(rl, rate_color.call(rl)),
|
|
673
|
+
color.call(tokens, 37),
|
|
674
|
+
color.call(last_used, 90),
|
|
675
|
+
((reason == "-") ? reason : color.call(reason, 33))
|
|
676
|
+
]
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
header = ["Provider", "Status", "Avail", "Circuit", "RateLimited", "Tokens", "LastUsed", "Reason"]
|
|
680
|
+
table = TTY::Table.new header, table_rows
|
|
681
|
+
display_message(table.render(:basic), type: :info)
|
|
682
|
+
rescue => e
|
|
683
|
+
display_message("Failed to display provider health: #{e.message}", type: :error)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def run_providers_info_command(args)
|
|
687
|
+
require_relative "harness/provider_info"
|
|
688
|
+
|
|
689
|
+
provider_name = args.shift
|
|
690
|
+
unless provider_name
|
|
691
|
+
display_message("Usage: aidp providers info <provider_name>", type: :info)
|
|
692
|
+
display_message("Example: aidp providers info claude", type: :info)
|
|
693
|
+
return
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
force_refresh = args.include?("--refresh")
|
|
697
|
+
|
|
698
|
+
display_message("Provider Information: #{provider_name}", type: :highlight)
|
|
699
|
+
display_message("=" * 60, type: :muted)
|
|
700
|
+
|
|
701
|
+
provider_info = Aidp::Harness::ProviderInfo.new(provider_name, Dir.pwd)
|
|
702
|
+
info = provider_info.get_info(force_refresh: force_refresh)
|
|
703
|
+
|
|
704
|
+
if info.nil?
|
|
705
|
+
display_message("No information available for provider: #{provider_name}", type: :error)
|
|
706
|
+
return
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
# Display basic info
|
|
710
|
+
display_message("Last Checked: #{info[:last_checked]}", type: :info)
|
|
711
|
+
display_message("CLI Available: #{info[:cli_available] ? "Yes" : "No"}", type: info[:cli_available] ? :success : :error)
|
|
712
|
+
|
|
713
|
+
# Display authentication
|
|
714
|
+
if info[:auth_method]
|
|
715
|
+
display_message("\nAuthentication Method: #{info[:auth_method]}", type: :info)
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
# Display MCP support
|
|
719
|
+
display_message("\nMCP Support: #{info[:mcp_support] ? "Yes" : "No"}", type: info[:mcp_support] ? :success : :info)
|
|
720
|
+
|
|
721
|
+
# Display MCP servers if available
|
|
722
|
+
if info[:mcp_servers]&.any?
|
|
723
|
+
display_message("\nMCP Servers: (#{info[:mcp_servers].size} configured)", type: :highlight)
|
|
724
|
+
info[:mcp_servers].each do |server|
|
|
725
|
+
status_symbol = server[:enabled] ? "✓" : "○"
|
|
726
|
+
display_message(" #{status_symbol} #{server[:name]} (#{server[:status]})", type: server[:enabled] ? :success : :muted)
|
|
727
|
+
display_message(" #{server[:description]}", type: :muted) if server[:description]
|
|
728
|
+
end
|
|
729
|
+
elsif info[:mcp_support]
|
|
730
|
+
display_message("\nMCP Servers: None configured", type: :muted)
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
# Display permission modes
|
|
734
|
+
if info[:permission_modes]&.any?
|
|
735
|
+
display_message("\nPermission Modes:", type: :highlight)
|
|
736
|
+
info[:permission_modes].each do |mode|
|
|
737
|
+
display_message(" - #{mode}", type: :info)
|
|
738
|
+
end
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# Display capabilities
|
|
742
|
+
if info[:capabilities]&.any?
|
|
743
|
+
display_message("\nCapabilities:", type: :highlight)
|
|
744
|
+
info[:capabilities].each do |cap, value|
|
|
745
|
+
next unless value
|
|
746
|
+
|
|
747
|
+
display_message(" ✓ #{cap.to_s.split("_").map(&:capitalize).join(" ")}", type: :success)
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Display notable flags
|
|
752
|
+
if info[:flags]&.any?
|
|
753
|
+
display_message("\nNotable Flags: (#{info[:flags].size} total)", type: :highlight)
|
|
754
|
+
# Show first 10 flags
|
|
755
|
+
info[:flags].take(10).each do |name, flag_info|
|
|
756
|
+
display_message(" #{flag_info[:flag]}", type: :info)
|
|
757
|
+
display_message(" #{flag_info[:description][0..80]}...", type: :muted) if flag_info[:description]
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
if info[:flags].size > 10
|
|
761
|
+
display_message("\n ... and #{info[:flags].size - 10} more flags", type: :muted)
|
|
762
|
+
display_message(" Run '#{get_binary_name(provider_name)} --help' for full details", type: :muted)
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
display_message("\n" + "=" * 60, type: :muted)
|
|
767
|
+
display_message("Tip: Use --refresh to update this information", type: :muted)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def run_providers_refresh_command(args)
|
|
771
|
+
require_relative "harness/provider_info"
|
|
772
|
+
require "tty-spinner"
|
|
773
|
+
|
|
774
|
+
provider_name = args.shift
|
|
775
|
+
configuration = Aidp::Harness::Configuration.new(Dir.pwd)
|
|
776
|
+
providers_to_refresh = if provider_name
|
|
777
|
+
[provider_name]
|
|
778
|
+
else
|
|
779
|
+
configuration.configured_providers
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
display_message("Refreshing provider information...", type: :info)
|
|
783
|
+
display_message("", type: :info)
|
|
784
|
+
|
|
785
|
+
providers_to_refresh.each do |prov|
|
|
786
|
+
spinner = TTY::Spinner.new("[:spinner] #{prov}...", format: :dots)
|
|
787
|
+
spinner.auto_spin
|
|
788
|
+
|
|
789
|
+
provider_info = Aidp::Harness::ProviderInfo.new(prov, Dir.pwd)
|
|
790
|
+
info = provider_info.gather_info
|
|
791
|
+
|
|
792
|
+
if info[:cli_available]
|
|
793
|
+
spinner.success("(available)")
|
|
794
|
+
else
|
|
795
|
+
spinner.error("(unavailable)")
|
|
796
|
+
end
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
display_message("\n✓ Provider information refreshed", type: :success)
|
|
800
|
+
display_message("Use 'aidp providers info <name>' to view details", type: :muted)
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def run_mcp_command(args)
|
|
804
|
+
require_relative "cli/mcp_dashboard"
|
|
805
|
+
|
|
806
|
+
subcommand = args.shift
|
|
807
|
+
|
|
808
|
+
dashboard = Aidp::CLI::McpDashboard.new(Dir.pwd)
|
|
809
|
+
|
|
810
|
+
case subcommand
|
|
811
|
+
when "dashboard", "list", nil
|
|
812
|
+
# Extract flags
|
|
813
|
+
no_color = args.include?("--no-color")
|
|
814
|
+
dashboard.display_dashboard(no_color: no_color)
|
|
815
|
+
|
|
816
|
+
when "check"
|
|
817
|
+
# Check eligibility for specific servers
|
|
818
|
+
required_servers = args
|
|
819
|
+
if required_servers.empty?
|
|
820
|
+
display_message("Usage: aidp mcp check <server1> [server2] ...", type: :info)
|
|
821
|
+
display_message("Example: aidp mcp check filesystem brave-search", type: :info)
|
|
822
|
+
return
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
dashboard.display_task_eligibility(required_servers)
|
|
826
|
+
|
|
827
|
+
else
|
|
828
|
+
display_message("Usage: aidp mcp <command>", type: :info)
|
|
829
|
+
display_message("", type: :info)
|
|
830
|
+
display_message("Commands:", type: :info)
|
|
831
|
+
display_message(" dashboard, list Show MCP servers across all providers (default)", type: :info)
|
|
832
|
+
display_message(" check <servers...> Check which providers have required MCP servers", type: :info)
|
|
833
|
+
display_message("", type: :info)
|
|
834
|
+
display_message("Examples:", type: :info)
|
|
835
|
+
display_message(" aidp mcp # Show dashboard", type: :info)
|
|
836
|
+
display_message(" aidp mcp dashboard --no-color # Show without colors", type: :info)
|
|
837
|
+
display_message(" aidp mcp check filesystem dash-api # Check provider eligibility", type: :info)
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def get_binary_name(provider_name)
|
|
842
|
+
case provider_name
|
|
843
|
+
when "claude", "anthropic"
|
|
844
|
+
"claude"
|
|
845
|
+
when "cursor"
|
|
846
|
+
"cursor"
|
|
847
|
+
when "gemini"
|
|
848
|
+
"gemini"
|
|
849
|
+
when "codex"
|
|
850
|
+
"codex"
|
|
851
|
+
when "github_copilot"
|
|
852
|
+
"gh"
|
|
853
|
+
when "opencode"
|
|
854
|
+
"opencode"
|
|
855
|
+
else
|
|
856
|
+
provider_name
|
|
857
|
+
end
|
|
858
|
+
end
|
|
859
|
+
|
|
389
860
|
def extract_mode_option(args)
|
|
390
861
|
mode = nil
|
|
391
862
|
args.each do |arg|
|
|
@@ -403,17 +874,20 @@ module Aidp
|
|
|
403
874
|
|
|
404
875
|
def select_mode_interactive(tui)
|
|
405
876
|
mode_options = [
|
|
877
|
+
"🤖 Guided Workflow (Copilot) - AI helps you choose the right workflow",
|
|
406
878
|
"🔬 Analyze Mode - Analyze your codebase for insights and recommendations",
|
|
407
879
|
"🏗️ Execute Mode - Build new features with guided development workflow"
|
|
408
880
|
]
|
|
409
881
|
selected = tui.single_select("Welcome to AI Dev Pipeline! Choose your mode", mode_options, default: 1)
|
|
410
882
|
# Announce mode explicitly in headless contexts (handled internally otherwise)
|
|
411
883
|
if (defined?(RSpec) || ENV["RSPEC_RUNNING"]) && tui.respond_to?(:announce_mode)
|
|
412
|
-
tui.announce_mode(:
|
|
413
|
-
tui.announce_mode(:
|
|
884
|
+
tui.announce_mode(:guided) if selected == mode_options[0]
|
|
885
|
+
tui.announce_mode(:analyze) if selected == mode_options[1]
|
|
886
|
+
tui.announce_mode(:execute) if selected == mode_options[2]
|
|
414
887
|
end
|
|
415
|
-
return :
|
|
416
|
-
return :
|
|
888
|
+
return :guided if selected == mode_options[0]
|
|
889
|
+
return :analyze if selected == mode_options[1]
|
|
890
|
+
return :execute if selected == mode_options[2]
|
|
417
891
|
:analyze
|
|
418
892
|
end
|
|
419
893
|
|