aidp 0.15.2 → 0.16.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 +47 -0
- data/lib/aidp/analyze/error_handler.rb +14 -15
- data/lib/aidp/analyze/runner.rb +27 -5
- data/lib/aidp/analyze/steps.rb +4 -0
- data/lib/aidp/cli/jobs_command.rb +2 -1
- data/lib/aidp/cli.rb +812 -3
- data/lib/aidp/concurrency/backoff.rb +148 -0
- data/lib/aidp/concurrency/exec.rb +192 -0
- data/lib/aidp/concurrency/wait.rb +148 -0
- data/lib/aidp/concurrency.rb +71 -0
- data/lib/aidp/config.rb +20 -0
- data/lib/aidp/daemon/runner.rb +9 -8
- data/lib/aidp/debug_mixin.rb +1 -0
- data/lib/aidp/errors.rb +12 -0
- data/lib/aidp/execute/interactive_repl.rb +102 -11
- data/lib/aidp/execute/repl_macros.rb +776 -2
- data/lib/aidp/execute/runner.rb +27 -5
- data/lib/aidp/execute/steps.rb +2 -0
- data/lib/aidp/harness/config_loader.rb +24 -2
- data/lib/aidp/harness/enhanced_runner.rb +16 -2
- data/lib/aidp/harness/error_handler.rb +1 -1
- data/lib/aidp/harness/provider_info.rb +19 -15
- data/lib/aidp/harness/provider_manager.rb +47 -41
- data/lib/aidp/harness/runner.rb +3 -11
- data/lib/aidp/harness/state/persistence.rb +1 -6
- data/lib/aidp/harness/state_manager.rb +115 -7
- data/lib/aidp/harness/status_display.rb +11 -18
- data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +1 -1
- data/lib/aidp/harness/user_interface.rb +12 -15
- data/lib/aidp/jobs/background_runner.rb +15 -5
- data/lib/aidp/providers/codex.rb +0 -1
- data/lib/aidp/providers/cursor.rb +0 -1
- data/lib/aidp/providers/github_copilot.rb +0 -1
- data/lib/aidp/providers/opencode.rb +0 -1
- data/lib/aidp/skills/composer.rb +178 -0
- data/lib/aidp/skills/loader.rb +205 -0
- data/lib/aidp/skills/registry.rb +220 -0
- data/lib/aidp/skills/skill.rb +174 -0
- data/lib/aidp/skills.rb +30 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +93 -28
- data/lib/aidp/watch/runner.rb +3 -2
- data/lib/aidp/workstream_executor.rb +244 -0
- data/lib/aidp/workstream_state.rb +212 -0
- data/lib/aidp/worktree.rb +208 -0
- data/lib/aidp.rb +6 -0
- metadata +17 -4
data/lib/aidp/cli.rb
CHANGED
@@ -9,6 +9,7 @@ require_relative "harness/ui/enhanced_workflow_selector"
|
|
9
9
|
require_relative "harness/enhanced_runner"
|
10
10
|
require_relative "cli/first_run_wizard"
|
11
11
|
require_relative "rescue_logging"
|
12
|
+
require_relative "concurrency"
|
12
13
|
|
13
14
|
module Aidp
|
14
15
|
# CLI interface for AIDP
|
@@ -286,6 +287,27 @@ module Aidp
|
|
286
287
|
opts.separator " mcp MCP server dashboard and management"
|
287
288
|
opts.separator " dashboard - Show all MCP servers across providers"
|
288
289
|
opts.separator " check <servers...> - Check provider eligibility for servers"
|
290
|
+
opts.separator " ws Manage parallel workstreams (git worktrees)"
|
291
|
+
opts.separator " list - List all workstreams"
|
292
|
+
opts.separator " new <slug> [task] - Create new workstream"
|
293
|
+
opts.separator " rm <slug> - Remove workstream"
|
294
|
+
opts.separator " status <slug> - Show workstream status"
|
295
|
+
opts.separator " pause <slug> - Pause workstream"
|
296
|
+
opts.separator " resume <slug> - Resume workstream"
|
297
|
+
opts.separator " complete <slug> - Mark workstream as completed"
|
298
|
+
opts.separator " dashboard - Show multi-workstream overview"
|
299
|
+
opts.separator " pause-all - Pause all active workstreams"
|
300
|
+
opts.separator " resume-all - Resume all paused workstreams"
|
301
|
+
opts.separator " stop-all - Stop all active workstreams"
|
302
|
+
opts.separator " work Execute workflow in workstream context"
|
303
|
+
opts.separator " --workstream <slug> - Required: workstream to run in"
|
304
|
+
opts.separator " --mode <mode> - analyze or execute (default: execute)"
|
305
|
+
opts.separator " --background - Run in background job"
|
306
|
+
opts.separator " skill Manage skills (agent personas)"
|
307
|
+
opts.separator " list - List all available skills"
|
308
|
+
opts.separator " show <id> - Show detailed skill information"
|
309
|
+
opts.separator " search <query> - Search skills by keyword"
|
310
|
+
opts.separator " validate [path] - Validate skill file format"
|
289
311
|
opts.separator " harness Manage harness state"
|
290
312
|
opts.separator " config Manage configuration"
|
291
313
|
opts.separator " status - Show harness status"
|
@@ -338,7 +360,7 @@ module Aidp
|
|
338
360
|
# Determine if the invocation is a subcommand style call
|
339
361
|
def subcommand?(args)
|
340
362
|
return false if args.nil? || args.empty?
|
341
|
-
%w[status jobs kb harness providers checkpoint mcp issue config init watch].include?(args.first)
|
363
|
+
%w[status jobs kb harness providers checkpoint mcp issue config init watch ws work skill].include?(args.first)
|
342
364
|
end
|
343
365
|
|
344
366
|
def run_subcommand(args)
|
@@ -355,6 +377,9 @@ module Aidp
|
|
355
377
|
when "config" then run_config_command(args)
|
356
378
|
when "init" then run_init_command(args)
|
357
379
|
when "watch" then run_watch_command(args)
|
380
|
+
when "ws" then run_ws_command(args)
|
381
|
+
when "work" then run_work_command(args)
|
382
|
+
when "skill" then run_skill_command(args)
|
358
383
|
else
|
359
384
|
display_message("Unknown command: #{cmd}", type: :info)
|
360
385
|
return 1
|
@@ -461,7 +486,15 @@ module Aidp
|
|
461
486
|
if follow
|
462
487
|
display_message("", type: :info)
|
463
488
|
display_message("Following logs (Ctrl+C to stop following)...", type: :info)
|
464
|
-
|
489
|
+
|
490
|
+
# Wait for log file to be created before following
|
491
|
+
log_file = File.join(runner.instance_variable_get(:@jobs_dir), job_id, "output.log")
|
492
|
+
begin
|
493
|
+
Aidp::Concurrency::Wait.for_file(log_file, timeout: 10, interval: 0.2)
|
494
|
+
rescue Aidp::Concurrency::TimeoutError
|
495
|
+
display_message("Warning: Log file not found after 10s", type: :warning)
|
496
|
+
end
|
497
|
+
|
465
498
|
runner.follow_job_logs(job_id)
|
466
499
|
end
|
467
500
|
|
@@ -1060,7 +1093,7 @@ module Aidp
|
|
1060
1093
|
|
1061
1094
|
def run_watch_command(args)
|
1062
1095
|
if args.empty?
|
1063
|
-
display_message("Usage: aidp watch <issues_url> [--interval SECONDS] [--provider NAME] [--once]", type: :info)
|
1096
|
+
display_message("Usage: aidp watch <issues_url> [--interval SECONDS] [--provider NAME] [--once] [--no-workstreams]", type: :info)
|
1064
1097
|
return
|
1065
1098
|
end
|
1066
1099
|
|
@@ -1068,6 +1101,7 @@ module Aidp
|
|
1068
1101
|
interval = Aidp::Watch::Runner::DEFAULT_INTERVAL
|
1069
1102
|
provider_name = nil
|
1070
1103
|
once = false
|
1104
|
+
use_workstreams = true # Default to using workstreams
|
1071
1105
|
|
1072
1106
|
until args.empty?
|
1073
1107
|
token = args.shift
|
@@ -1079,6 +1113,8 @@ module Aidp
|
|
1079
1113
|
provider_name = args.shift
|
1080
1114
|
when "--once"
|
1081
1115
|
once = true
|
1116
|
+
when "--no-workstreams"
|
1117
|
+
use_workstreams = false
|
1082
1118
|
else
|
1083
1119
|
display_message("⚠️ Unknown watch option: #{token}", type: :warn)
|
1084
1120
|
end
|
@@ -1090,6 +1126,7 @@ module Aidp
|
|
1090
1126
|
provider_name: provider_name,
|
1091
1127
|
project_dir: Dir.pwd,
|
1092
1128
|
once: once,
|
1129
|
+
use_workstreams: use_workstreams,
|
1093
1130
|
prompt: TTY::Prompt.new
|
1094
1131
|
)
|
1095
1132
|
runner.start
|
@@ -1098,9 +1135,781 @@ module Aidp
|
|
1098
1135
|
display_message("❌ #{e.message}", type: :error)
|
1099
1136
|
end
|
1100
1137
|
|
1138
|
+
def run_ws_command(args)
|
1139
|
+
require_relative "worktree"
|
1140
|
+
require_relative "workstream_state"
|
1141
|
+
require "tty-table"
|
1142
|
+
|
1143
|
+
subcommand = args.shift
|
1144
|
+
|
1145
|
+
case subcommand
|
1146
|
+
when "list", nil
|
1147
|
+
# List all workstreams
|
1148
|
+
workstreams = Aidp::Worktree.list(project_dir: Dir.pwd)
|
1149
|
+
|
1150
|
+
if workstreams.empty?
|
1151
|
+
display_message("No workstreams found.", type: :info)
|
1152
|
+
display_message("Create one with: aidp ws new <slug> [task]", type: :muted)
|
1153
|
+
return
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
display_message("Workstreams", type: :highlight)
|
1157
|
+
display_message("=" * 80, type: :muted)
|
1158
|
+
|
1159
|
+
table_rows = workstreams.map do |ws|
|
1160
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd) || {}
|
1161
|
+
status_icon = ws[:active] ? "✓" : "✗"
|
1162
|
+
created = Time.parse(ws[:created_at]).strftime("%Y-%m-%d %H:%M")
|
1163
|
+
iterations = state[:iterations] || 0
|
1164
|
+
task = state[:task] && state[:task].to_s[0, 40]
|
1165
|
+
[
|
1166
|
+
status_icon,
|
1167
|
+
ws[:slug],
|
1168
|
+
ws[:branch],
|
1169
|
+
created,
|
1170
|
+
ws[:active] ? "active" : "inactive",
|
1171
|
+
iterations,
|
1172
|
+
task
|
1173
|
+
]
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
header = ["", "Slug", "Branch", "Created", "Status", "Iter", "Task"]
|
1177
|
+
table = TTY::Table.new(header, table_rows)
|
1178
|
+
# Render with explicit width for non-TTY environments (e.g., tests)
|
1179
|
+
renderer = if $stdout.tty?
|
1180
|
+
table.render(:basic)
|
1181
|
+
else
|
1182
|
+
table.render(:basic, width: 120)
|
1183
|
+
end
|
1184
|
+
display_message(renderer, type: :info)
|
1185
|
+
|
1186
|
+
when "new"
|
1187
|
+
# Create new workstream
|
1188
|
+
slug = args.shift
|
1189
|
+
unless slug
|
1190
|
+
display_message("❌ Missing slug", type: :error)
|
1191
|
+
display_message("Usage: aidp ws new <slug> [task]", type: :info)
|
1192
|
+
return
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
# Validate slug format (lowercase, hyphens, no special chars)
|
1196
|
+
unless slug.match?(/^[a-z0-9]+(-[a-z0-9]+)*$/)
|
1197
|
+
display_message("❌ Invalid slug format", type: :error)
|
1198
|
+
display_message(" Slug must be lowercase with hyphens (e.g., 'issue-123-fix-auth')", type: :info)
|
1199
|
+
return
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
task_parts = []
|
1203
|
+
base_branch = nil
|
1204
|
+
until args.empty?
|
1205
|
+
token = args.shift
|
1206
|
+
if token == "--base-branch"
|
1207
|
+
base_branch = args.shift
|
1208
|
+
else
|
1209
|
+
task_parts << token
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
task = task_parts.join(" ")
|
1213
|
+
|
1214
|
+
begin
|
1215
|
+
result = Aidp::Worktree.create(
|
1216
|
+
slug: slug,
|
1217
|
+
project_dir: Dir.pwd,
|
1218
|
+
base_branch: base_branch,
|
1219
|
+
task: (task unless task.empty?)
|
1220
|
+
)
|
1221
|
+
|
1222
|
+
display_message("✓ Created workstream: #{slug}", type: :success)
|
1223
|
+
display_message(" Path: #{result[:path]}", type: :info)
|
1224
|
+
display_message(" Branch: #{result[:branch]}", type: :info)
|
1225
|
+
display_message(" Task: #{task}", type: :info) unless task.empty?
|
1226
|
+
display_message("", type: :info)
|
1227
|
+
display_message("Switch to this workstream:", type: :muted)
|
1228
|
+
display_message(" cd #{result[:path]}", type: :info)
|
1229
|
+
rescue Aidp::Worktree::Error => e
|
1230
|
+
display_message("❌ #{e.message}", type: :error)
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
when "rm"
|
1234
|
+
# Remove workstream
|
1235
|
+
slug = args.shift
|
1236
|
+
unless slug
|
1237
|
+
display_message("❌ Missing slug", type: :error)
|
1238
|
+
display_message("Usage: aidp ws rm <slug> [--delete-branch] [--force]", type: :info)
|
1239
|
+
return
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
delete_branch = args.include?("--delete-branch")
|
1243
|
+
force = args.include?("--force")
|
1244
|
+
|
1245
|
+
# Confirm removal unless --force
|
1246
|
+
unless force
|
1247
|
+
prompt = TTY::Prompt.new
|
1248
|
+
confirm = prompt.yes?("Remove workstream '#{slug}'?#{" (will also delete branch)" if delete_branch}")
|
1249
|
+
return unless confirm
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
begin
|
1253
|
+
Aidp::Worktree.remove(
|
1254
|
+
slug: slug,
|
1255
|
+
project_dir: Dir.pwd,
|
1256
|
+
delete_branch: delete_branch
|
1257
|
+
)
|
1258
|
+
|
1259
|
+
display_message("✓ Removed workstream: #{slug}", type: :success)
|
1260
|
+
display_message(" Branch deleted", type: :info) if delete_branch
|
1261
|
+
rescue Aidp::Worktree::Error => e
|
1262
|
+
display_message("❌ #{e.message}", type: :error)
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
when "status"
|
1266
|
+
# Show workstream status
|
1267
|
+
slug = args.shift
|
1268
|
+
unless slug
|
1269
|
+
display_message("❌ Missing slug", type: :error)
|
1270
|
+
display_message("Usage: aidp ws status <slug>", type: :info)
|
1271
|
+
return
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
begin
|
1275
|
+
ws = Aidp::Worktree.info(slug: slug, project_dir: Dir.pwd)
|
1276
|
+
unless ws
|
1277
|
+
display_message("❌ Workstream not found: #{slug}", type: :error)
|
1278
|
+
return
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
state = Aidp::WorkstreamState.read(slug: slug, project_dir: Dir.pwd) || {}
|
1282
|
+
iterations = state[:iterations] || 0
|
1283
|
+
elapsed = Aidp::WorkstreamState.elapsed_seconds(slug: slug, project_dir: Dir.pwd)
|
1284
|
+
task = state[:task]
|
1285
|
+
recent_events = Aidp::WorkstreamState.recent_events(slug: slug, project_dir: Dir.pwd, limit: 5)
|
1286
|
+
display_message("Workstream: #{slug}", type: :highlight)
|
1287
|
+
display_message("=" * 60, type: :muted)
|
1288
|
+
display_message("Path: #{ws[:path]}", type: :info)
|
1289
|
+
display_message("Branch: #{ws[:branch]}", type: :info)
|
1290
|
+
display_message("Created: #{Time.parse(ws[:created_at]).strftime("%Y-%m-%d %H:%M:%S")}", type: :info)
|
1291
|
+
display_message("Status: #{ws[:active] ? "Active" : "Inactive"}", type: ws[:active] ? :success : :error)
|
1292
|
+
display_message("Iterations: #{iterations}", type: :info)
|
1293
|
+
display_message("Elapsed: #{elapsed}s", type: :info)
|
1294
|
+
display_message("Task: #{task}", type: :info) if task
|
1295
|
+
if recent_events.any?
|
1296
|
+
display_message("", type: :info)
|
1297
|
+
display_message("Recent Events:", type: :highlight)
|
1298
|
+
recent_events.each do |ev|
|
1299
|
+
display_message(" #{ev[:timestamp]} #{ev[:type]} #{ev[:data].inspect if ev[:data]}", type: :muted)
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
# Show git status if active
|
1304
|
+
if ws[:active] && Dir.exist?(ws[:path])
|
1305
|
+
display_message("", type: :info)
|
1306
|
+
display_message("Git Status:", type: :highlight)
|
1307
|
+
Dir.chdir(ws[:path]) do
|
1308
|
+
system("git", "status", "--short")
|
1309
|
+
end
|
1310
|
+
end
|
1311
|
+
rescue Aidp::Worktree::Error => e
|
1312
|
+
display_message("❌ #{e.message}", type: :error)
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
when "pause"
|
1316
|
+
# Pause workstream
|
1317
|
+
slug = args.shift
|
1318
|
+
unless slug
|
1319
|
+
display_message("❌ Missing slug", type: :error)
|
1320
|
+
display_message("Usage: aidp ws pause <slug>", type: :info)
|
1321
|
+
return
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
result = Aidp::WorkstreamState.pause(slug: slug, project_dir: Dir.pwd)
|
1325
|
+
if result[:error]
|
1326
|
+
display_message("❌ #{result[:error]}", type: :error)
|
1327
|
+
else
|
1328
|
+
display_message("⏸️ Paused workstream: #{slug}", type: :success)
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
when "resume"
|
1332
|
+
# Resume workstream
|
1333
|
+
slug = args.shift
|
1334
|
+
unless slug
|
1335
|
+
display_message("❌ Missing slug", type: :error)
|
1336
|
+
display_message("Usage: aidp ws resume <slug>", type: :info)
|
1337
|
+
return
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
result = Aidp::WorkstreamState.resume(slug: slug, project_dir: Dir.pwd)
|
1341
|
+
if result[:error]
|
1342
|
+
display_message("❌ #{result[:error]}", type: :error)
|
1343
|
+
else
|
1344
|
+
display_message("▶️ Resumed workstream: #{slug}", type: :success)
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
when "complete"
|
1348
|
+
# Mark workstream as completed
|
1349
|
+
slug = args.shift
|
1350
|
+
unless slug
|
1351
|
+
display_message("❌ Missing slug", type: :error)
|
1352
|
+
display_message("Usage: aidp ws complete <slug>", type: :info)
|
1353
|
+
return
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
result = Aidp::WorkstreamState.complete(slug: slug, project_dir: Dir.pwd)
|
1357
|
+
if result[:error]
|
1358
|
+
display_message("❌ #{result[:error]}", type: :error)
|
1359
|
+
else
|
1360
|
+
display_message("✅ Completed workstream: #{slug}", type: :success)
|
1361
|
+
end
|
1362
|
+
|
1363
|
+
when "run"
|
1364
|
+
# Run one or more workstreams in parallel
|
1365
|
+
require_relative "workstream_executor"
|
1366
|
+
|
1367
|
+
slugs = []
|
1368
|
+
max_concurrent = 3
|
1369
|
+
mode = :execute
|
1370
|
+
selected_steps = nil
|
1371
|
+
|
1372
|
+
until args.empty?
|
1373
|
+
token = args.shift
|
1374
|
+
case token
|
1375
|
+
when "--max-concurrent"
|
1376
|
+
max_concurrent = args.shift.to_i
|
1377
|
+
when "--mode"
|
1378
|
+
mode = args.shift&.to_sym || :execute
|
1379
|
+
when "--steps"
|
1380
|
+
selected_steps = args.shift.split(",")
|
1381
|
+
else
|
1382
|
+
slugs << token
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
if slugs.empty?
|
1387
|
+
display_message("❌ Missing workstream slug(s)", type: :error)
|
1388
|
+
display_message("Usage: aidp ws run <slug1> [slug2...] [--max-concurrent N] [--mode analyze|execute] [--steps STEP1,STEP2]", type: :info)
|
1389
|
+
display_message("", type: :info)
|
1390
|
+
display_message("Examples:", type: :info)
|
1391
|
+
display_message(" aidp ws run issue-123 # Run single workstream", type: :info)
|
1392
|
+
display_message(" aidp ws run issue-123 issue-456 feature-x # Run multiple in parallel", type: :info)
|
1393
|
+
display_message(" aidp ws run issue-* --max-concurrent 5 # Run all matching (expand glob first)", type: :info)
|
1394
|
+
return
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
begin
|
1398
|
+
executor = Aidp::WorkstreamExecutor.new(project_dir: Dir.pwd, max_concurrent: max_concurrent)
|
1399
|
+
options = {mode: mode}
|
1400
|
+
options[:selected_steps] = selected_steps if selected_steps
|
1401
|
+
|
1402
|
+
results = executor.execute_parallel(slugs, options)
|
1403
|
+
|
1404
|
+
# Show results
|
1405
|
+
display_message("", type: :info)
|
1406
|
+
success_count = results.count { |r| r.status == "completed" }
|
1407
|
+
if success_count == results.size
|
1408
|
+
display_message("🎉 All workstreams completed successfully!", type: :success)
|
1409
|
+
else
|
1410
|
+
display_message("⚠️ Some workstreams failed", type: :warn)
|
1411
|
+
end
|
1412
|
+
rescue => e
|
1413
|
+
display_message("❌ Parallel execution error: #{e.message}", type: :error)
|
1414
|
+
end
|
1415
|
+
|
1416
|
+
when "run-all"
|
1417
|
+
# Run all active workstreams in parallel
|
1418
|
+
require_relative "workstream_executor"
|
1419
|
+
|
1420
|
+
max_concurrent = 3
|
1421
|
+
mode = :execute
|
1422
|
+
selected_steps = nil
|
1423
|
+
|
1424
|
+
until args.empty?
|
1425
|
+
token = args.shift
|
1426
|
+
case token
|
1427
|
+
when "--max-concurrent"
|
1428
|
+
max_concurrent = args.shift.to_i
|
1429
|
+
when "--mode"
|
1430
|
+
mode = args.shift&.to_sym || :execute
|
1431
|
+
when "--steps"
|
1432
|
+
selected_steps = args.shift.split(",")
|
1433
|
+
end
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
begin
|
1437
|
+
executor = Aidp::WorkstreamExecutor.new(project_dir: Dir.pwd, max_concurrent: max_concurrent)
|
1438
|
+
options = {mode: mode}
|
1439
|
+
options[:selected_steps] = selected_steps if selected_steps
|
1440
|
+
|
1441
|
+
results = executor.execute_all(options)
|
1442
|
+
|
1443
|
+
if results.empty?
|
1444
|
+
display_message("⚠️ No active workstreams to run", type: :warn)
|
1445
|
+
return
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
# Show results
|
1449
|
+
display_message("", type: :info)
|
1450
|
+
success_count = results.count { |r| r.status == "completed" }
|
1451
|
+
if success_count == results.size
|
1452
|
+
display_message("🎉 All workstreams completed successfully!", type: :success)
|
1453
|
+
else
|
1454
|
+
display_message("⚠️ Some workstreams failed", type: :warn)
|
1455
|
+
end
|
1456
|
+
rescue => e
|
1457
|
+
display_message("❌ Parallel execution error: #{e.message}", type: :error)
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
when "dashboard"
|
1461
|
+
# Show multi-workstream dashboard
|
1462
|
+
workstreams = Aidp::Worktree.list(project_dir: Dir.pwd)
|
1463
|
+
|
1464
|
+
if workstreams.empty?
|
1465
|
+
display_message("No workstreams found.", type: :info)
|
1466
|
+
display_message("Create one with: aidp ws new <slug> [task]", type: :muted)
|
1467
|
+
return
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
display_message("Workstreams Dashboard", type: :highlight)
|
1471
|
+
display_message("=" * 120, type: :muted)
|
1472
|
+
|
1473
|
+
# Aggregate state from all workstreams
|
1474
|
+
table_rows = workstreams.map do |ws|
|
1475
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd) || {}
|
1476
|
+
status = state[:status] || "active"
|
1477
|
+
iterations = state[:iterations] || 0
|
1478
|
+
elapsed = Aidp::WorkstreamState.elapsed_seconds(slug: ws[:slug], project_dir: Dir.pwd)
|
1479
|
+
task = state[:task] && state[:task].to_s[0, 30]
|
1480
|
+
recent_events = Aidp::WorkstreamState.recent_events(slug: ws[:slug], project_dir: Dir.pwd, limit: 1)
|
1481
|
+
recent_event = recent_events.first
|
1482
|
+
event_summary = if recent_event
|
1483
|
+
"#{recent_event[:type]} (#{Time.parse(recent_event[:timestamp]).strftime("%H:%M")})"
|
1484
|
+
else
|
1485
|
+
"—"
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
status_icon = case status
|
1489
|
+
when "active" then "▶️"
|
1490
|
+
when "paused" then "⏸️"
|
1491
|
+
when "completed" then "✅"
|
1492
|
+
when "removed" then "❌"
|
1493
|
+
else "?"
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
[
|
1497
|
+
status_icon,
|
1498
|
+
ws[:slug],
|
1499
|
+
status,
|
1500
|
+
iterations,
|
1501
|
+
"#{elapsed}s",
|
1502
|
+
task || "—",
|
1503
|
+
event_summary
|
1504
|
+
]
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
header = ["", "Slug", "Status", "Iter", "Elapsed", "Task", "Recent Event"]
|
1508
|
+
table = TTY::Table.new(header, table_rows)
|
1509
|
+
# Render with explicit width for non-TTY environments
|
1510
|
+
renderer = if $stdout.tty?
|
1511
|
+
table.render(:basic)
|
1512
|
+
else
|
1513
|
+
table.render(:basic, width: 120)
|
1514
|
+
end
|
1515
|
+
display_message(renderer, type: :info)
|
1516
|
+
|
1517
|
+
# Show summary counts
|
1518
|
+
display_message("", type: :info)
|
1519
|
+
status_counts = workstreams.group_by do |ws|
|
1520
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd) || {}
|
1521
|
+
state[:status] || "active"
|
1522
|
+
end
|
1523
|
+
summary_parts = status_counts.map { |status, ws_list| "#{status}: #{ws_list.size}" }
|
1524
|
+
display_message("Summary: #{summary_parts.join(", ")}", type: :muted)
|
1525
|
+
|
1526
|
+
when "pause-all"
|
1527
|
+
# Pause all active workstreams
|
1528
|
+
workstreams = Aidp::Worktree.list(project_dir: Dir.pwd)
|
1529
|
+
paused_count = 0
|
1530
|
+
workstreams.each do |ws|
|
1531
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd)
|
1532
|
+
next unless state && state[:status] == "active"
|
1533
|
+
result = Aidp::WorkstreamState.pause(slug: ws[:slug], project_dir: Dir.pwd)
|
1534
|
+
paused_count += 1 unless result[:error]
|
1535
|
+
end
|
1536
|
+
display_message("⏸️ Paused #{paused_count} workstream(s)", type: :success)
|
1537
|
+
|
1538
|
+
when "resume-all"
|
1539
|
+
# Resume all paused workstreams
|
1540
|
+
workstreams = Aidp::Worktree.list(project_dir: Dir.pwd)
|
1541
|
+
resumed_count = 0
|
1542
|
+
workstreams.each do |ws|
|
1543
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd)
|
1544
|
+
next unless state && state[:status] == "paused"
|
1545
|
+
result = Aidp::WorkstreamState.resume(slug: ws[:slug], project_dir: Dir.pwd)
|
1546
|
+
resumed_count += 1 unless result[:error]
|
1547
|
+
end
|
1548
|
+
display_message("▶️ Resumed #{resumed_count} workstream(s)", type: :success)
|
1549
|
+
|
1550
|
+
when "stop-all"
|
1551
|
+
# Complete all active workstreams
|
1552
|
+
workstreams = Aidp::Worktree.list(project_dir: Dir.pwd)
|
1553
|
+
stopped_count = 0
|
1554
|
+
workstreams.each do |ws|
|
1555
|
+
state = Aidp::WorkstreamState.read(slug: ws[:slug], project_dir: Dir.pwd)
|
1556
|
+
next unless state && state[:status] == "active"
|
1557
|
+
result = Aidp::WorkstreamState.complete(slug: ws[:slug], project_dir: Dir.pwd)
|
1558
|
+
stopped_count += 1 unless result[:error]
|
1559
|
+
end
|
1560
|
+
display_message("⏹️ Stopped #{stopped_count} workstream(s)", type: :success)
|
1561
|
+
|
1562
|
+
else
|
1563
|
+
display_message("Usage: aidp ws <command>", type: :info)
|
1564
|
+
display_message("", type: :info)
|
1565
|
+
display_message("Commands:", type: :info)
|
1566
|
+
display_message(" list List all workstreams (default)", type: :info)
|
1567
|
+
display_message(" new <slug> [task] Create new workstream", type: :info)
|
1568
|
+
display_message(" rm <slug> Remove workstream", type: :info)
|
1569
|
+
display_message(" status <slug> Show workstream status", type: :info)
|
1570
|
+
display_message(" run <slug...> Run workstream(s) in parallel", type: :info)
|
1571
|
+
display_message(" run-all Run all active workstreams in parallel", type: :info)
|
1572
|
+
display_message(" dashboard Show multi-workstream dashboard", type: :info)
|
1573
|
+
display_message(" pause <slug> Pause workstream execution", type: :info)
|
1574
|
+
display_message(" resume <slug> Resume paused workstream", type: :info)
|
1575
|
+
display_message(" complete <slug> Mark workstream as completed", type: :info)
|
1576
|
+
display_message("", type: :info)
|
1577
|
+
display_message("Options:", type: :info)
|
1578
|
+
display_message(" --base-branch <branch> Branch to create from (for 'new')", type: :info)
|
1579
|
+
display_message(" --delete-branch Also delete git branch (for 'rm')", type: :info)
|
1580
|
+
display_message(" --force Skip confirmation (for 'rm')", type: :info)
|
1581
|
+
display_message(" --max-concurrent N Max parallel workstreams (for 'run', 'run-all')", type: :info)
|
1582
|
+
display_message(" --mode analyze|execute Execution mode (for 'run', 'run-all')", type: :info)
|
1583
|
+
display_message(" --steps STEP1,STEP2 Specific steps to run (for 'run', 'run-all')", type: :info)
|
1584
|
+
display_message("", type: :info)
|
1585
|
+
display_message("Examples:", type: :info)
|
1586
|
+
display_message(" aidp ws list # List workstreams", type: :info)
|
1587
|
+
display_message(" aidp ws new issue-123 Fix authentication bug # Create workstream", type: :info)
|
1588
|
+
display_message(" aidp ws new feature-x --base-branch develop # Create from branch", type: :info)
|
1589
|
+
display_message(" aidp ws status issue-123 # Show status", type: :info)
|
1590
|
+
display_message(" aidp ws run issue-123 # Run single workstream", type: :info)
|
1591
|
+
display_message(" aidp ws run issue-123 feature-x --max-concurrent 5 # Run multiple in parallel", type: :info)
|
1592
|
+
display_message(" aidp ws run-all --max-concurrent 3 # Run all active workstreams", type: :info)
|
1593
|
+
display_message(" aidp ws dashboard # Monitor all workstreams", type: :info)
|
1594
|
+
display_message(" aidp ws rm issue-123 # Remove workstream", type: :info)
|
1595
|
+
display_message(" aidp ws rm issue-123 --delete-branch --force # Force remove with branch", type: :info)
|
1596
|
+
end
|
1597
|
+
end
|
1598
|
+
|
1599
|
+
def run_work_command(args)
|
1600
|
+
require_relative "worktree"
|
1601
|
+
require_relative "harness/state_manager"
|
1602
|
+
|
1603
|
+
# Parse options
|
1604
|
+
workstream_slug = nil
|
1605
|
+
mode = :execute
|
1606
|
+
background = false
|
1607
|
+
|
1608
|
+
until args.empty?
|
1609
|
+
token = args.shift
|
1610
|
+
case token
|
1611
|
+
when "--workstream"
|
1612
|
+
workstream_slug = args.shift
|
1613
|
+
when "--mode"
|
1614
|
+
mode = args.shift&.to_sym || :execute
|
1615
|
+
when "--background"
|
1616
|
+
background = true
|
1617
|
+
else
|
1618
|
+
display_message("⚠️ Unknown work option: #{token}", type: :warn)
|
1619
|
+
end
|
1620
|
+
end
|
1621
|
+
|
1622
|
+
unless workstream_slug
|
1623
|
+
display_message("❌ Missing required --workstream flag", type: :error)
|
1624
|
+
display_message("Usage: aidp work --workstream <slug> [--mode analyze|execute] [--background]", type: :info)
|
1625
|
+
return
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
# Verify workstream exists
|
1629
|
+
ws = Aidp::Worktree.info(slug: workstream_slug, project_dir: Dir.pwd)
|
1630
|
+
unless ws
|
1631
|
+
display_message("❌ Workstream not found: #{workstream_slug}", type: :error)
|
1632
|
+
return
|
1633
|
+
end
|
1634
|
+
|
1635
|
+
display_message("🚀 Starting #{mode} mode in workstream: #{workstream_slug}", type: :highlight)
|
1636
|
+
display_message(" Path: #{ws[:path]}", type: :info)
|
1637
|
+
display_message(" Branch: #{ws[:branch]}", type: :info)
|
1638
|
+
|
1639
|
+
if background
|
1640
|
+
require_relative "jobs/background_runner"
|
1641
|
+
runner = Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
|
1642
|
+
|
1643
|
+
display_message("Starting in background...", type: :info)
|
1644
|
+
job_id = runner.start(mode, {workstream: workstream_slug})
|
1645
|
+
|
1646
|
+
display_message("✓ Started background job: #{job_id}", type: :success)
|
1647
|
+
display_message("", type: :info)
|
1648
|
+
display_message("Monitor progress:", type: :info)
|
1649
|
+
display_message(" aidp jobs status #{job_id}", type: :info)
|
1650
|
+
display_message(" aidp jobs logs #{job_id} --tail", type: :info)
|
1651
|
+
display_message(" aidp ws status #{workstream_slug}", type: :info)
|
1652
|
+
else
|
1653
|
+
# Run harness inline with workstream context
|
1654
|
+
state_manager = Aidp::Harness::StateManager.new(Dir.pwd, mode)
|
1655
|
+
state_manager.set_workstream(workstream_slug)
|
1656
|
+
|
1657
|
+
# Launch harness (will cd into workstream path via enhanced_runner)
|
1658
|
+
display_message("Starting interactive harness...", type: :info)
|
1659
|
+
display_message("Press Ctrl+C to stop", type: :highlight)
|
1660
|
+
|
1661
|
+
# Re-use existing CLI.run harness launch logic but skip first-run wizard
|
1662
|
+
# Initialize the enhanced TUI
|
1663
|
+
require_relative "harness/ui/enhanced_tui"
|
1664
|
+
require_relative "harness/ui/enhanced_workflow_selector"
|
1665
|
+
require_relative "harness/enhanced_runner"
|
1666
|
+
|
1667
|
+
tui = Aidp::Harness::UI::EnhancedTUI.new
|
1668
|
+
workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
|
1669
|
+
|
1670
|
+
# Start TUI display loop
|
1671
|
+
tui.start_display_loop
|
1672
|
+
|
1673
|
+
begin
|
1674
|
+
# Get workflow configuration
|
1675
|
+
workflow_config = workflow_selector.select_workflow(harness_mode: false, mode: mode)
|
1676
|
+
actual_mode = workflow_config[:mode] || mode
|
1677
|
+
|
1678
|
+
# Pass workflow configuration to harness
|
1679
|
+
harness_options = {
|
1680
|
+
mode: actual_mode,
|
1681
|
+
workflow_type: workflow_config[:workflow_type],
|
1682
|
+
selected_steps: workflow_config[:steps],
|
1683
|
+
user_input: workflow_config[:user_input]
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
# Create and run the enhanced harness
|
1687
|
+
harness_runner = Aidp::Harness::EnhancedRunner.new(Dir.pwd, actual_mode, harness_options)
|
1688
|
+
result = harness_runner.run
|
1689
|
+
display_harness_result(result)
|
1690
|
+
rescue Interrupt
|
1691
|
+
display_message("\n\n⏹️ Interrupted by user", type: :warning)
|
1692
|
+
ensure
|
1693
|
+
tui.stop_display_loop
|
1694
|
+
end
|
1695
|
+
end
|
1696
|
+
end
|
1697
|
+
|
1101
1698
|
def display_config_usage
|
1102
1699
|
display_message("Usage: aidp config --interactive [--dry-run]", type: :info)
|
1103
1700
|
end
|
1701
|
+
|
1702
|
+
def run_skill_command(args)
|
1703
|
+
require_relative "skills"
|
1704
|
+
require "tty-table"
|
1705
|
+
|
1706
|
+
subcommand = args.shift
|
1707
|
+
|
1708
|
+
case subcommand
|
1709
|
+
when "list", nil
|
1710
|
+
# List all available skills
|
1711
|
+
begin
|
1712
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
1713
|
+
registry.load_skills
|
1714
|
+
|
1715
|
+
skills = registry.all
|
1716
|
+
|
1717
|
+
if skills.empty?
|
1718
|
+
display_message("No skills found.", type: :info)
|
1719
|
+
display_message("Create one in skills/ or .aidp/skills/", type: :muted)
|
1720
|
+
return
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
by_source = registry.by_source
|
1724
|
+
|
1725
|
+
if by_source[:builtin].any?
|
1726
|
+
display_message("Built-in Skills", type: :highlight)
|
1727
|
+
display_message("=" * 80, type: :muted)
|
1728
|
+
table_rows = by_source[:builtin].map do |skill_id|
|
1729
|
+
skill = registry.find(skill_id)
|
1730
|
+
[skill_id, skill.version, skill.description[0, 60]]
|
1731
|
+
end
|
1732
|
+
header = ["ID", "Version", "Description"]
|
1733
|
+
table = TTY::Table.new(header, table_rows)
|
1734
|
+
display_message(table.render(:basic), type: :info)
|
1735
|
+
display_message("", type: :info)
|
1736
|
+
end
|
1737
|
+
|
1738
|
+
if by_source[:custom].any?
|
1739
|
+
display_message("Custom Skills", type: :highlight)
|
1740
|
+
display_message("=" * 80, type: :muted)
|
1741
|
+
table_rows = by_source[:custom].map do |skill_id|
|
1742
|
+
skill = registry.find(skill_id)
|
1743
|
+
[skill_id, skill.version, skill.description[0, 60]]
|
1744
|
+
end
|
1745
|
+
header = ["ID", "Version", "Description"]
|
1746
|
+
table = TTY::Table.new(header, table_rows)
|
1747
|
+
display_message(table.render(:basic), type: :info)
|
1748
|
+
display_message("", type: :info)
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
display_message("Use 'aidp skill show <id>' for details", type: :muted)
|
1752
|
+
rescue => e
|
1753
|
+
display_message("Failed to list skills: #{e.message}", type: :error)
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
when "show"
|
1757
|
+
# Show detailed skill information
|
1758
|
+
skill_id = args.shift
|
1759
|
+
|
1760
|
+
unless skill_id
|
1761
|
+
display_message("Usage: aidp skill show <skill-id>", type: :info)
|
1762
|
+
return
|
1763
|
+
end
|
1764
|
+
|
1765
|
+
begin
|
1766
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
1767
|
+
registry.load_skills
|
1768
|
+
|
1769
|
+
skill = registry.find(skill_id)
|
1770
|
+
|
1771
|
+
unless skill
|
1772
|
+
display_message("Skill not found: #{skill_id}", type: :error)
|
1773
|
+
display_message("Use 'aidp skill list' to see available skills", type: :muted)
|
1774
|
+
return
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
details = skill.details
|
1778
|
+
|
1779
|
+
display_message("Skill: #{details[:name]} (#{details[:id]})", type: :highlight)
|
1780
|
+
display_message("=" * 80, type: :muted)
|
1781
|
+
display_message("Version: #{details[:version]}", type: :info)
|
1782
|
+
display_message("Source: #{details[:source]}", type: :muted)
|
1783
|
+
display_message("", type: :info)
|
1784
|
+
display_message("Description:", type: :info)
|
1785
|
+
display_message(" #{details[:description]}", type: :info)
|
1786
|
+
display_message("", type: :info)
|
1787
|
+
|
1788
|
+
if details[:expertise].any?
|
1789
|
+
display_message("Expertise:", type: :info)
|
1790
|
+
details[:expertise].each { |e| display_message(" • #{e}", type: :info) }
|
1791
|
+
display_message("", type: :info)
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
if details[:keywords].any?
|
1795
|
+
display_message("Keywords: #{details[:keywords].join(", ")}", type: :info)
|
1796
|
+
display_message("", type: :info)
|
1797
|
+
end
|
1798
|
+
|
1799
|
+
if details[:when_to_use].any?
|
1800
|
+
display_message("When to Use:", type: :info)
|
1801
|
+
details[:when_to_use].each { |w| display_message(" • #{w}", type: :info) }
|
1802
|
+
display_message("", type: :info)
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
if details[:when_not_to_use].any?
|
1806
|
+
display_message("When NOT to Use:", type: :info)
|
1807
|
+
details[:when_not_to_use].each { |w| display_message(" • #{w}", type: :info) }
|
1808
|
+
display_message("", type: :info)
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
if details[:compatible_providers].any?
|
1812
|
+
display_message("Compatible Providers: #{details[:compatible_providers].join(", ")}", type: :info)
|
1813
|
+
else
|
1814
|
+
display_message("Compatible Providers: all", type: :info)
|
1815
|
+
end
|
1816
|
+
rescue => e
|
1817
|
+
display_message("Failed to show skill: #{e.message}", type: :error)
|
1818
|
+
end
|
1819
|
+
|
1820
|
+
when "search"
|
1821
|
+
# Search skills by query
|
1822
|
+
query = args.join(" ")
|
1823
|
+
|
1824
|
+
unless query && !query.empty?
|
1825
|
+
display_message("Usage: aidp skill search <query>", type: :info)
|
1826
|
+
return
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
begin
|
1830
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
1831
|
+
registry.load_skills
|
1832
|
+
|
1833
|
+
matching_skills = registry.search(query)
|
1834
|
+
|
1835
|
+
if matching_skills.empty?
|
1836
|
+
display_message("No skills found matching '#{query}'", type: :info)
|
1837
|
+
return
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
display_message("Skills matching '#{query}':", type: :highlight)
|
1841
|
+
display_message("=" * 80, type: :muted)
|
1842
|
+
matching_skills.each do |skill|
|
1843
|
+
display_message(" • #{skill.id} - #{skill.description}", type: :info)
|
1844
|
+
end
|
1845
|
+
rescue => e
|
1846
|
+
display_message("Failed to search skills: #{e.message}", type: :error)
|
1847
|
+
end
|
1848
|
+
|
1849
|
+
when "validate"
|
1850
|
+
# Validate skill file format
|
1851
|
+
skill_path = args.shift
|
1852
|
+
|
1853
|
+
if skill_path
|
1854
|
+
# Validate specific file
|
1855
|
+
unless File.exist?(skill_path)
|
1856
|
+
display_message("File not found: #{skill_path}", type: :error)
|
1857
|
+
return
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
begin
|
1861
|
+
Aidp::Skills::Loader.load_from_file(skill_path)
|
1862
|
+
display_message("✓ Valid skill file: #{skill_path}", type: :success)
|
1863
|
+
rescue Aidp::Errors::ValidationError => e
|
1864
|
+
display_message("✗ Invalid skill file: #{skill_path}", type: :error)
|
1865
|
+
display_message(" #{e.message}", type: :error)
|
1866
|
+
end
|
1867
|
+
else
|
1868
|
+
# Validate all skills in registry
|
1869
|
+
begin
|
1870
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
1871
|
+
registry.load_skills
|
1872
|
+
|
1873
|
+
skills = registry.all
|
1874
|
+
|
1875
|
+
if skills.empty?
|
1876
|
+
display_message("No skills found to validate", type: :info)
|
1877
|
+
return
|
1878
|
+
end
|
1879
|
+
|
1880
|
+
display_message("Validating #{skills.size} skill(s)...", type: :info)
|
1881
|
+
display_message("", type: :info)
|
1882
|
+
|
1883
|
+
valid_count = 0
|
1884
|
+
skills.each do |skill|
|
1885
|
+
display_message("✓ #{skill.id} (v#{skill.version})", type: :success)
|
1886
|
+
valid_count += 1
|
1887
|
+
end
|
1888
|
+
|
1889
|
+
display_message("", type: :info)
|
1890
|
+
display_message("#{valid_count}/#{skills.size} skills are valid", type: :success)
|
1891
|
+
rescue => e
|
1892
|
+
display_message("Validation failed: #{e.message}", type: :error)
|
1893
|
+
end
|
1894
|
+
end
|
1895
|
+
|
1896
|
+
else
|
1897
|
+
display_message("Usage: aidp skill <command>", type: :info)
|
1898
|
+
display_message("", type: :info)
|
1899
|
+
display_message("Commands:", type: :info)
|
1900
|
+
display_message(" list List all available skills (default)", type: :info)
|
1901
|
+
display_message(" show <id> Show detailed skill information", type: :info)
|
1902
|
+
display_message(" search <query> Search skills by keyword", type: :info)
|
1903
|
+
display_message(" validate [path] Validate skill file format", type: :info)
|
1904
|
+
display_message("", type: :info)
|
1905
|
+
display_message("Examples:", type: :info)
|
1906
|
+
display_message(" aidp skill list # List all skills", type: :info)
|
1907
|
+
display_message(" aidp skill show repository_analyst # Show skill details", type: :info)
|
1908
|
+
display_message(" aidp skill search git # Search for git-related skills", type: :info)
|
1909
|
+
display_message(" aidp skill validate skills/my_skill/SKILL.md # Validate specific skill", type: :info)
|
1910
|
+
display_message(" aidp skill validate # Validate all skills", type: :info)
|
1911
|
+
end
|
1912
|
+
end
|
1104
1913
|
end # class << self
|
1105
1914
|
end
|
1106
1915
|
end
|