shipwright-cli 2.0.0 → 2.1.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.
- package/README.md +160 -72
- package/completions/_shipwright +59 -7
- package/completions/shipwright.bash +24 -4
- package/completions/shipwright.fish +80 -2
- package/dashboard/server.ts +208 -0
- package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
- package/docs/tmux-research/TMUX-AUDIT.md +925 -0
- package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
- package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
- package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
- package/package.json +2 -2
- package/scripts/lib/helpers.sh +7 -0
- package/scripts/sw +116 -2
- package/scripts/sw-activity.sh +1 -1
- package/scripts/sw-adaptive.sh +1 -1
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +128 -38
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +1 -1
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +62 -1
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +44 -3
- package/scripts/sw-daemon.sh +155 -27
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +958 -118
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +49 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +1 -1
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +1 -1
- package/scripts/sw-feedback.sh +23 -15
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +1 -1
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +1 -1
- package/scripts/sw-github-checks.sh +4 -4
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +1 -1
- package/scripts/sw-incident.sh +45 -6
- package/scripts/sw-init.sh +150 -24
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +204 -19
- package/scripts/sw-memory.sh +18 -1
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +1 -1
- package/scripts/sw-otel.sh +1 -1
- package/scripts/sw-oversight.sh +76 -1
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +261 -12
- package/scripts/sw-pm.sh +70 -5
- package/scripts/sw-pr-lifecycle.sh +1 -1
- package/scripts/sw-predictive.sh +8 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +1 -1
- package/scripts/sw-quality.sh +1 -1
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +1853 -178
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-scale.sh +1 -1
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +263 -127
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +44 -2
- package/scripts/sw-strategic.sh +189 -41
- package/scripts/sw-stream.sh +1 -1
- package/scripts/sw-swarm.sh +42 -5
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +4 -4
- package/scripts/sw-testgen.sh +66 -15
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux-role-color.sh +58 -0
- package/scripts/sw-tmux-status.sh +128 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +61 -37
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +1 -1
- package/scripts/sw-widgets.sh +1 -1
- package/scripts/sw-worktree.sh +1 -1
- package/templates/pipelines/autonomous.json +2 -2
- package/tmux/shipwright-overlay.conf +35 -17
- package/tmux/tmux.conf +23 -21
package/scripts/sw-pipeline.sh
CHANGED
|
@@ -11,7 +11,7 @@ unset CLAUDECODE 2>/dev/null || true
|
|
|
11
11
|
# Ignore SIGHUP so tmux attach/detach doesn't kill long-running plan/design/review stages
|
|
12
12
|
trap '' HUP
|
|
13
13
|
|
|
14
|
-
VERSION="2.
|
|
14
|
+
VERSION="2.1.0"
|
|
15
15
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
16
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
17
17
|
|
|
@@ -202,6 +202,7 @@ PIPELINE_CONFIG=""
|
|
|
202
202
|
TEST_CMD=""
|
|
203
203
|
MODEL=""
|
|
204
204
|
AGENTS=""
|
|
205
|
+
PIPELINE_AGENT_ID="${PIPELINE_AGENT_ID:-pipeline-$$}"
|
|
205
206
|
SKIP_GATES=false
|
|
206
207
|
GIT_BRANCH=""
|
|
207
208
|
GITHUB_ISSUE=""
|
|
@@ -223,6 +224,7 @@ AUTO_WORKTREE=false
|
|
|
223
224
|
WORKTREE_NAME=""
|
|
224
225
|
CLEANUP_WORKTREE=false
|
|
225
226
|
ORIGINAL_REPO_DIR=""
|
|
227
|
+
REPO_OVERRIDE=""
|
|
226
228
|
_cleanup_done=""
|
|
227
229
|
|
|
228
230
|
# GitHub metadata (populated during intake)
|
|
@@ -264,6 +266,8 @@ show_help() {
|
|
|
264
266
|
echo -e "${BOLD}START OPTIONS${RESET}"
|
|
265
267
|
echo -e " ${DIM}--goal \"description\"${RESET} What to build (required unless --issue)"
|
|
266
268
|
echo -e " ${DIM}--issue <number>${RESET} Fetch goal from GitHub issue"
|
|
269
|
+
echo -e " ${DIM}--repo <path>${RESET} Change to directory before running (must be a git repo)"
|
|
270
|
+
echo -e " ${DIM}--local${RESET} Alias for --no-github --no-github-label (local-only mode)"
|
|
267
271
|
echo -e " ${DIM}--pipeline <name>${RESET} Pipeline template (default: standard)"
|
|
268
272
|
echo -e " ${DIM}--test-cmd \"command\"${RESET} Override test command (auto-detected if omitted)"
|
|
269
273
|
echo -e " ${DIM}--model <model>${RESET} Override AI model (opus, sonnet, haiku)"
|
|
@@ -346,6 +350,8 @@ parse_args() {
|
|
|
346
350
|
case "$1" in
|
|
347
351
|
--goal) GOAL="$2"; shift 2 ;;
|
|
348
352
|
--issue) ISSUE_NUMBER="$2"; shift 2 ;;
|
|
353
|
+
--repo) REPO_OVERRIDE="$2"; shift 2 ;;
|
|
354
|
+
--local) NO_GITHUB=true; NO_GITHUB_LABEL=true; shift ;;
|
|
349
355
|
--pipeline|--template) PIPELINE_NAME="$2"; shift 2 ;;
|
|
350
356
|
--test-cmd) TEST_CMD="$2"; shift 2 ;;
|
|
351
357
|
--model) MODEL="$2"; shift 2 ;;
|
|
@@ -396,6 +402,7 @@ setup_dirs() {
|
|
|
396
402
|
ARTIFACTS_DIR="$STATE_DIR/pipeline-artifacts"
|
|
397
403
|
TASKS_FILE="$STATE_DIR/pipeline-tasks.md"
|
|
398
404
|
mkdir -p "$STATE_DIR" "$ARTIFACTS_DIR"
|
|
405
|
+
export SHIPWRIGHT_PIPELINE_ID="pipeline-$$-${ISSUE_NUMBER:-0}"
|
|
399
406
|
}
|
|
400
407
|
|
|
401
408
|
# ─── Pipeline Config Loading ───────────────────────────────────────────────
|
|
@@ -1155,6 +1162,21 @@ get_stage_timing() {
|
|
|
1155
1162
|
fi
|
|
1156
1163
|
}
|
|
1157
1164
|
|
|
1165
|
+
# Raw seconds for a stage (for memory baseline updates)
|
|
1166
|
+
get_stage_timing_seconds() {
|
|
1167
|
+
local stage_id="$1"
|
|
1168
|
+
local start_e end_e
|
|
1169
|
+
start_e=$(echo "$STAGE_TIMINGS" | grep "^${stage_id}_start:" | cut -d: -f2 | tail -1 || true)
|
|
1170
|
+
end_e=$(echo "$STAGE_TIMINGS" | grep "^${stage_id}_end:" | cut -d: -f2 | tail -1 || true)
|
|
1171
|
+
if [[ -n "$start_e" && -n "$end_e" ]]; then
|
|
1172
|
+
echo $(( end_e - start_e ))
|
|
1173
|
+
elif [[ -n "$start_e" ]]; then
|
|
1174
|
+
echo $(( $(now_epoch) - start_e ))
|
|
1175
|
+
else
|
|
1176
|
+
echo "0"
|
|
1177
|
+
fi
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1158
1180
|
get_stage_description() {
|
|
1159
1181
|
local stage_id="$1"
|
|
1160
1182
|
|
|
@@ -1250,6 +1272,22 @@ mark_stage_complete() {
|
|
|
1250
1272
|
log_stage "$stage_id" "complete (${timing})"
|
|
1251
1273
|
write_state
|
|
1252
1274
|
|
|
1275
|
+
record_stage_effectiveness "$stage_id" "complete"
|
|
1276
|
+
# Update memory baselines and predictive baselines for stage durations
|
|
1277
|
+
if [[ "$stage_id" == "test" || "$stage_id" == "build" ]]; then
|
|
1278
|
+
local secs
|
|
1279
|
+
secs=$(get_stage_timing_seconds "$stage_id")
|
|
1280
|
+
if [[ -n "$secs" && "$secs" != "0" ]]; then
|
|
1281
|
+
[[ -x "$SCRIPT_DIR/sw-memory.sh" ]] && bash "$SCRIPT_DIR/sw-memory.sh" metric "${stage_id}_duration_s" "$secs" 2>/dev/null || true
|
|
1282
|
+
if [[ -x "$SCRIPT_DIR/sw-predictive.sh" ]]; then
|
|
1283
|
+
local anomaly_sev
|
|
1284
|
+
anomaly_sev=$(bash "$SCRIPT_DIR/sw-predictive.sh" anomaly "$stage_id" "duration_s" "$secs" 2>/dev/null || echo "normal")
|
|
1285
|
+
[[ "$anomaly_sev" == "critical" || "$anomaly_sev" == "warning" ]] && emit_event "pipeline.anomaly" "stage=$stage_id" "metric=duration_s" "value=$secs" "severity=$anomaly_sev" 2>/dev/null || true
|
|
1286
|
+
bash "$SCRIPT_DIR/sw-predictive.sh" baseline "$stage_id" "duration_s" "$secs" 2>/dev/null || true
|
|
1287
|
+
fi
|
|
1288
|
+
fi
|
|
1289
|
+
fi
|
|
1290
|
+
|
|
1253
1291
|
# Update GitHub progress comment
|
|
1254
1292
|
if [[ -n "$ISSUE_NUMBER" ]]; then
|
|
1255
1293
|
local body
|
|
@@ -1346,9 +1384,40 @@ verify_stage_artifacts() {
|
|
|
1346
1384
|
return "$missing"
|
|
1347
1385
|
}
|
|
1348
1386
|
|
|
1387
|
+
# Self-aware pipeline: record stage effectiveness for meta-cognition
|
|
1388
|
+
STAGE_EFFECTIVENESS_FILE="${HOME}/.shipwright/stage-effectiveness.jsonl"
|
|
1389
|
+
record_stage_effectiveness() {
|
|
1390
|
+
local stage_id="$1" outcome="${2:-failed}"
|
|
1391
|
+
mkdir -p "${HOME}/.shipwright"
|
|
1392
|
+
echo "{\"stage\":\"$stage_id\",\"outcome\":\"$outcome\",\"ts\":\"$(now_iso)\"}" >> "${STAGE_EFFECTIVENESS_FILE}"
|
|
1393
|
+
# Keep last 100 entries
|
|
1394
|
+
tail -100 "${STAGE_EFFECTIVENESS_FILE}" > "${STAGE_EFFECTIVENESS_FILE}.tmp" 2>/dev/null && mv "${STAGE_EFFECTIVENESS_FILE}.tmp" "${STAGE_EFFECTIVENESS_FILE}" 2>/dev/null || true
|
|
1395
|
+
}
|
|
1396
|
+
get_stage_self_awareness_hint() {
|
|
1397
|
+
local stage_id="$1"
|
|
1398
|
+
[[ ! -f "$STAGE_EFFECTIVENESS_FILE" ]] && return 0
|
|
1399
|
+
local recent
|
|
1400
|
+
recent=$(grep "\"stage\":\"$stage_id\"" "$STAGE_EFFECTIVENESS_FILE" 2>/dev/null | tail -10 || true)
|
|
1401
|
+
[[ -z "$recent" ]] && return 0
|
|
1402
|
+
local failures=0 total=0
|
|
1403
|
+
while IFS= read -r line; do
|
|
1404
|
+
[[ -z "$line" ]] && continue
|
|
1405
|
+
total=$((total + 1))
|
|
1406
|
+
echo "$line" | grep -q '"outcome":"failed"' && failures=$((failures + 1)) || true
|
|
1407
|
+
done <<< "$recent"
|
|
1408
|
+
if [[ "$total" -ge 3 ]] && [[ $((failures * 100 / total)) -ge 50 ]]; then
|
|
1409
|
+
case "$stage_id" in
|
|
1410
|
+
plan) echo "Recent plan stage failures: consider adding more context or breaking the goal into smaller steps." ;;
|
|
1411
|
+
build) echo "Recent build stage failures: consider adding test expectations or simplifying the change." ;;
|
|
1412
|
+
*) echo "Recent $stage_id failures: review past logs and adjust approach." ;;
|
|
1413
|
+
esac
|
|
1414
|
+
fi
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1349
1417
|
mark_stage_failed() {
|
|
1350
1418
|
local stage_id="$1"
|
|
1351
1419
|
record_stage_end "$stage_id"
|
|
1420
|
+
record_stage_effectiveness "$stage_id" "failed"
|
|
1352
1421
|
set_stage_status "$stage_id" "failed"
|
|
1353
1422
|
local timing
|
|
1354
1423
|
timing=$(get_stage_timing "$stage_id")
|
|
@@ -1779,6 +1848,28 @@ ${memory_summary}
|
|
|
1779
1848
|
fi
|
|
1780
1849
|
fi
|
|
1781
1850
|
|
|
1851
|
+
# Self-aware pipeline: inject hint when plan stage has been failing recently
|
|
1852
|
+
local plan_hint
|
|
1853
|
+
plan_hint=$(get_stage_self_awareness_hint "plan" 2>/dev/null || true)
|
|
1854
|
+
if [[ -n "$plan_hint" ]]; then
|
|
1855
|
+
plan_prompt="${plan_prompt}
|
|
1856
|
+
## Self-Assessment (recent plan stage performance)
|
|
1857
|
+
${plan_hint}
|
|
1858
|
+
"
|
|
1859
|
+
fi
|
|
1860
|
+
|
|
1861
|
+
# Inject cross-pipeline discoveries (from other concurrent/similar pipelines)
|
|
1862
|
+
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
1863
|
+
local plan_discoveries
|
|
1864
|
+
plan_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.json" 2>/dev/null | head -20 || true)
|
|
1865
|
+
if [[ -n "$plan_discoveries" ]]; then
|
|
1866
|
+
plan_prompt="${plan_prompt}
|
|
1867
|
+
## Discoveries from Other Pipelines
|
|
1868
|
+
${plan_discoveries}
|
|
1869
|
+
"
|
|
1870
|
+
fi
|
|
1871
|
+
fi
|
|
1872
|
+
|
|
1782
1873
|
# Inject architecture patterns from intelligence layer
|
|
1783
1874
|
local repo_hash_plan
|
|
1784
1875
|
repo_hash_plan=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-12 || echo "unknown")
|
|
@@ -2151,6 +2242,12 @@ stage_design() {
|
|
|
2151
2242
|
memory_context=$(bash "$SCRIPT_DIR/sw-memory.sh" inject "design" 2>/dev/null) || true
|
|
2152
2243
|
fi
|
|
2153
2244
|
|
|
2245
|
+
# Inject cross-pipeline discoveries for design stage
|
|
2246
|
+
local design_discoveries=""
|
|
2247
|
+
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
2248
|
+
design_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "*.md,*.ts,*.tsx,*.js" 2>/dev/null | head -20 || true)
|
|
2249
|
+
fi
|
|
2250
|
+
|
|
2154
2251
|
# Inject architecture model patterns if available
|
|
2155
2252
|
local arch_context=""
|
|
2156
2253
|
local repo_hash
|
|
@@ -2210,7 +2307,10 @@ ${memory_context}
|
|
|
2210
2307
|
}${arch_context:+
|
|
2211
2308
|
## Architecture Model (from previous designs)
|
|
2212
2309
|
${arch_context}
|
|
2213
|
-
}${design_antipatterns}
|
|
2310
|
+
}${design_antipatterns}${design_discoveries:+
|
|
2311
|
+
## Discoveries from Other Pipelines
|
|
2312
|
+
${design_discoveries}
|
|
2313
|
+
}
|
|
2214
2314
|
## Required Output — Architecture Decision Record
|
|
2215
2315
|
|
|
2216
2316
|
Produce this EXACT format:
|
|
@@ -2334,6 +2434,18 @@ Historical context (lessons from previous pipelines):
|
|
|
2334
2434
|
${memory_context}"
|
|
2335
2435
|
fi
|
|
2336
2436
|
|
|
2437
|
+
# Inject cross-pipeline discoveries for build stage
|
|
2438
|
+
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
2439
|
+
local build_discoveries
|
|
2440
|
+
build_discoveries=$("$SCRIPT_DIR/sw-discovery.sh" inject "src/*,*.ts,*.tsx,*.js" 2>/dev/null | head -20 || true)
|
|
2441
|
+
if [[ -n "$build_discoveries" ]]; then
|
|
2442
|
+
enriched_goal="${enriched_goal}
|
|
2443
|
+
|
|
2444
|
+
Discoveries from other pipelines:
|
|
2445
|
+
${build_discoveries}"
|
|
2446
|
+
fi
|
|
2447
|
+
fi
|
|
2448
|
+
|
|
2337
2449
|
# Add task list context
|
|
2338
2450
|
if [[ -s "$TASKS_FILE" ]]; then
|
|
2339
2451
|
enriched_goal="${enriched_goal}
|
|
@@ -2380,6 +2492,19 @@ Coverage baseline: ${coverage_baseline}% — do not decrease coverage."
|
|
|
2380
2492
|
fi
|
|
2381
2493
|
fi
|
|
2382
2494
|
|
|
2495
|
+
# Predictive: inject prevention hints when risk/memory patterns suggest build-stage failures
|
|
2496
|
+
if [[ -x "$SCRIPT_DIR/sw-predictive.sh" ]]; then
|
|
2497
|
+
local issue_json_build="{}"
|
|
2498
|
+
[[ -n "${ISSUE_NUMBER:-}" ]] && issue_json_build=$(jq -n --arg title "${GOAL:-}" --arg num "${ISSUE_NUMBER:-}" '{title: $title, number: $num}')
|
|
2499
|
+
local prevention_text
|
|
2500
|
+
prevention_text=$(bash "$SCRIPT_DIR/sw-predictive.sh" inject-prevention "build" "$issue_json_build" 2>/dev/null || true)
|
|
2501
|
+
if [[ -n "$prevention_text" ]]; then
|
|
2502
|
+
enriched_goal="${enriched_goal}
|
|
2503
|
+
|
|
2504
|
+
${prevention_text}"
|
|
2505
|
+
fi
|
|
2506
|
+
fi
|
|
2507
|
+
|
|
2383
2508
|
loop_args+=("$enriched_goal")
|
|
2384
2509
|
|
|
2385
2510
|
# Build loop args from pipeline config + CLI overrides
|
|
@@ -2432,6 +2557,23 @@ Coverage baseline: ${coverage_baseline}% — do not decrease coverage."
|
|
|
2432
2557
|
build_model="$CLAUDE_MODEL"
|
|
2433
2558
|
fi
|
|
2434
2559
|
|
|
2560
|
+
# Recruit-powered model selection (when no explicit override)
|
|
2561
|
+
if [[ -z "$MODEL" ]] && [[ -x "$SCRIPT_DIR/sw-recruit.sh" ]]; then
|
|
2562
|
+
local _recruit_goal="${GOAL:-}"
|
|
2563
|
+
if [[ -n "$_recruit_goal" ]]; then
|
|
2564
|
+
local _recruit_match
|
|
2565
|
+
_recruit_match=$(bash "$SCRIPT_DIR/sw-recruit.sh" match --json "$_recruit_goal" 2>/dev/null) || true
|
|
2566
|
+
if [[ -n "$_recruit_match" ]]; then
|
|
2567
|
+
local _recruit_model
|
|
2568
|
+
_recruit_model=$(echo "$_recruit_match" | jq -r '.model // ""' 2>/dev/null) || true
|
|
2569
|
+
if [[ -n "$_recruit_model" && "$_recruit_model" != "null" && "$_recruit_model" != "" ]]; then
|
|
2570
|
+
info "Recruit recommends model: ${CYAN}${_recruit_model}${RESET} for this task"
|
|
2571
|
+
build_model="$_recruit_model"
|
|
2572
|
+
fi
|
|
2573
|
+
fi
|
|
2574
|
+
fi
|
|
2575
|
+
fi
|
|
2576
|
+
|
|
2435
2577
|
[[ -n "$test_cmd" && "$test_cmd" != "null" ]] && loop_args+=(--test-cmd "$test_cmd")
|
|
2436
2578
|
loop_args+=(--max-iterations "$max_iter")
|
|
2437
2579
|
loop_args+=(--model "$build_model")
|
|
@@ -2488,6 +2630,23 @@ Coverage baseline: ${coverage_baseline}% — do not decrease coverage."
|
|
|
2488
2630
|
}
|
|
2489
2631
|
parse_claude_tokens "$_token_log"
|
|
2490
2632
|
|
|
2633
|
+
# Read accumulated token counts from build loop (written by sw-loop.sh)
|
|
2634
|
+
local _loop_token_file="${PROJECT_ROOT}/.claude/loop-logs/loop-tokens.json"
|
|
2635
|
+
if [[ -f "$_loop_token_file" ]] && command -v jq &>/dev/null; then
|
|
2636
|
+
local _loop_in _loop_out _loop_cost
|
|
2637
|
+
_loop_in=$(jq -r '.input_tokens // 0' "$_loop_token_file" 2>/dev/null || echo "0")
|
|
2638
|
+
_loop_out=$(jq -r '.output_tokens // 0' "$_loop_token_file" 2>/dev/null || echo "0")
|
|
2639
|
+
_loop_cost=$(jq -r '.cost_usd // 0' "$_loop_token_file" 2>/dev/null || echo "0")
|
|
2640
|
+
TOTAL_INPUT_TOKENS=$(( TOTAL_INPUT_TOKENS + ${_loop_in:-0} ))
|
|
2641
|
+
TOTAL_OUTPUT_TOKENS=$(( TOTAL_OUTPUT_TOKENS + ${_loop_out:-0} ))
|
|
2642
|
+
if [[ -n "$_loop_cost" && "$_loop_cost" != "0" && "$_loop_cost" != "null" ]]; then
|
|
2643
|
+
TOTAL_COST_USD="${_loop_cost}"
|
|
2644
|
+
fi
|
|
2645
|
+
if [[ "${_loop_in:-0}" -gt 0 || "${_loop_out:-0}" -gt 0 ]]; then
|
|
2646
|
+
info "Build loop tokens: in=${_loop_in} out=${_loop_out} cost=\$${_loop_cost:-0}"
|
|
2647
|
+
fi
|
|
2648
|
+
fi
|
|
2649
|
+
|
|
2491
2650
|
# Count commits made during build
|
|
2492
2651
|
local commit_count
|
|
2493
2652
|
commit_count=$(git log --oneline "${BASE_BRANCH}..HEAD" 2>/dev/null | wc -l | xargs)
|
|
@@ -2792,6 +2951,22 @@ $(cat "$diff_file")"
|
|
|
2792
2951
|
success "Review clean"
|
|
2793
2952
|
fi
|
|
2794
2953
|
|
|
2954
|
+
# ── Oversight gate: pipeline review/quality stages block on verdict ──
|
|
2955
|
+
if [[ -x "$SCRIPT_DIR/sw-oversight.sh" ]] && [[ "${SKIP_GATES:-false}" != "true" ]]; then
|
|
2956
|
+
local reject_reason=""
|
|
2957
|
+
local _sec_count
|
|
2958
|
+
_sec_count=$(grep -ciE '\*\*\[?Security\]?\*\*' "$review_file" 2>/dev/null || true)
|
|
2959
|
+
_sec_count="${_sec_count:-0}"
|
|
2960
|
+
local _blocking=$((critical_count + _sec_count))
|
|
2961
|
+
[[ "$_blocking" -gt 0 ]] && reject_reason="Review found ${_blocking} critical/security issue(s)"
|
|
2962
|
+
if ! bash "$SCRIPT_DIR/sw-oversight.sh" gate --diff "$diff_file" --description "${GOAL:-Pipeline review}" --reject-if "$reject_reason" >/dev/null 2>&1; then
|
|
2963
|
+
error "Oversight gate rejected — blocking pipeline"
|
|
2964
|
+
emit_event "review.oversight_blocked" "issue=${ISSUE_NUMBER:-0}"
|
|
2965
|
+
log_stage "review" "BLOCKED: oversight gate rejected"
|
|
2966
|
+
return 1
|
|
2967
|
+
fi
|
|
2968
|
+
fi
|
|
2969
|
+
|
|
2795
2970
|
# ── Review Blocking Gate ──
|
|
2796
2971
|
# Block pipeline on critical/security issues unless compound_quality handles them
|
|
2797
2972
|
local security_count
|
|
@@ -3771,6 +3946,8 @@ stage_monitor() {
|
|
|
3771
3946
|
fi
|
|
3772
3947
|
|
|
3773
3948
|
local report_file="$ARTIFACTS_DIR/monitor-report.md"
|
|
3949
|
+
local deploy_log_file="$ARTIFACTS_DIR/deploy-logs.txt"
|
|
3950
|
+
: > "$deploy_log_file"
|
|
3774
3951
|
local total_errors=0
|
|
3775
3952
|
local poll_interval=30 # seconds between polls
|
|
3776
3953
|
local total_polls=$(( (duration_minutes * 60) / poll_interval ))
|
|
@@ -3818,10 +3995,11 @@ stage_monitor() {
|
|
|
3818
3995
|
fi
|
|
3819
3996
|
fi
|
|
3820
3997
|
|
|
3821
|
-
# Log command check
|
|
3998
|
+
# Log command check (accumulate deploy logs for feedback collect)
|
|
3822
3999
|
if [[ -n "$log_cmd" ]]; then
|
|
3823
4000
|
local log_output
|
|
3824
4001
|
log_output=$(bash -c "$log_cmd" 2>/dev/null || true)
|
|
4002
|
+
[[ -n "$log_output" ]] && echo "$log_output" >> "$deploy_log_file"
|
|
3825
4003
|
local error_count=0
|
|
3826
4004
|
if [[ -n "$log_output" ]]; then
|
|
3827
4005
|
error_count=$(echo "$log_output" | grep -cE "$log_pattern" 2>/dev/null || true)
|
|
@@ -3856,13 +4034,24 @@ stage_monitor() {
|
|
|
3856
4034
|
"total_errors=$total_errors" \
|
|
3857
4035
|
"threshold=$error_threshold"
|
|
3858
4036
|
|
|
3859
|
-
#
|
|
3860
|
-
if [[ "$
|
|
4037
|
+
# Feedback loop: collect deploy logs and optionally create issue
|
|
4038
|
+
if [[ -f "$deploy_log_file" ]] && [[ -s "$deploy_log_file" ]] && [[ -x "$SCRIPT_DIR/sw-feedback.sh" ]]; then
|
|
4039
|
+
(cd "$PROJECT_ROOT" && ARTIFACTS_DIR="$ARTIFACTS_DIR" bash "$SCRIPT_DIR/sw-feedback.sh" collect "$deploy_log_file" 2>/dev/null) || true
|
|
4040
|
+
(cd "$PROJECT_ROOT" && ARTIFACTS_DIR="$ARTIFACTS_DIR" bash "$SCRIPT_DIR/sw-feedback.sh" create-issue 2>/dev/null) || true
|
|
4041
|
+
fi
|
|
4042
|
+
|
|
4043
|
+
# Auto-rollback: feedback rollback (GitHub Deployments API) and/or config rollback_cmd
|
|
4044
|
+
if [[ "$auto_rollback" == "true" ]]; then
|
|
3861
4045
|
warn "Auto-rolling back..."
|
|
3862
4046
|
echo "" >> "$report_file"
|
|
3863
4047
|
echo "## Rollback" >> "$report_file"
|
|
3864
4048
|
|
|
3865
|
-
|
|
4049
|
+
# Trigger feedback rollback (calls sw-github-deploy.sh rollback)
|
|
4050
|
+
if [[ -x "$SCRIPT_DIR/sw-feedback.sh" ]]; then
|
|
4051
|
+
(cd "$PROJECT_ROOT" && ARTIFACTS_DIR="$ARTIFACTS_DIR" bash "$SCRIPT_DIR/sw-feedback.sh" rollback production "Monitor threshold exceeded (${total_errors} errors)" >> "$report_file" 2>&1) || true
|
|
4052
|
+
fi
|
|
4053
|
+
|
|
4054
|
+
if [[ -n "$rollback_cmd" ]] && bash -c "$rollback_cmd" >> "$report_file" 2>&1; then
|
|
3866
4055
|
success "Rollback executed"
|
|
3867
4056
|
echo "Rollback: ✅ success" >> "$report_file"
|
|
3868
4057
|
|
|
@@ -7076,6 +7265,12 @@ Focus on fixing the failing tests while keeping all passing tests working."
|
|
|
7076
7265
|
_snap_error="${_snap_error:-}"
|
|
7077
7266
|
pipeline_emit_progress_snapshot "${ISSUE_NUMBER}" "${CURRENT_STAGE_ID:-test}" "${cycle:-0}" "${_diff_count:-0}" "${_snap_files}" "${_snap_error}" 2>/dev/null || true
|
|
7078
7267
|
fi
|
|
7268
|
+
# Record fix outcome when tests pass after a retry with memory injection (pipeline path)
|
|
7269
|
+
if [[ "$cycle" -gt 1 && -n "${last_test_error:-}" ]] && [[ -x "$SCRIPT_DIR/sw-memory.sh" ]]; then
|
|
7270
|
+
local _sig
|
|
7271
|
+
_sig=$(echo "$last_test_error" | head -3 | tr '\n' ' ' | sed 's/^ *//;s/ *$//')
|
|
7272
|
+
[[ -n "$_sig" ]] && bash "$SCRIPT_DIR/sw-memory.sh" fix-outcome "$_sig" "true" "true" 2>/dev/null || true
|
|
7273
|
+
fi
|
|
7079
7274
|
return 0 # Tests passed!
|
|
7080
7275
|
fi
|
|
7081
7276
|
|
|
@@ -7426,11 +7621,29 @@ run_pipeline() {
|
|
|
7426
7621
|
if run_stage_with_retry "$id"; then
|
|
7427
7622
|
mark_stage_complete "$id"
|
|
7428
7623
|
completed=$((completed + 1))
|
|
7624
|
+
# Capture project pattern after intake (for memory context in later stages)
|
|
7625
|
+
if [[ "$id" == "intake" ]] && [[ -x "$SCRIPT_DIR/sw-memory.sh" ]]; then
|
|
7626
|
+
(cd "$REPO_DIR" && bash "$SCRIPT_DIR/sw-memory.sh" pattern "project" "{}" 2>/dev/null) || true
|
|
7627
|
+
fi
|
|
7429
7628
|
local timing stage_dur_s
|
|
7430
7629
|
timing=$(get_stage_timing "$id")
|
|
7431
7630
|
stage_dur_s=$(( $(now_epoch) - stage_start_epoch ))
|
|
7432
7631
|
success "Stage ${BOLD}$id${RESET} complete ${DIM}(${timing})${RESET}"
|
|
7433
7632
|
emit_event "stage.completed" "issue=${ISSUE_NUMBER:-0}" "stage=$id" "duration_s=$stage_dur_s"
|
|
7633
|
+
# Broadcast discovery for cross-pipeline learning
|
|
7634
|
+
if [[ -x "$SCRIPT_DIR/sw-discovery.sh" ]]; then
|
|
7635
|
+
local _disc_cat _disc_patterns _disc_text
|
|
7636
|
+
_disc_cat="$id"
|
|
7637
|
+
case "$id" in
|
|
7638
|
+
plan) _disc_patterns="*.md"; _disc_text="Plan completed: ${GOAL:-goal}" ;;
|
|
7639
|
+
design) _disc_patterns="*.md,*.ts,*.tsx,*.js"; _disc_text="Design completed for ${GOAL:-goal}" ;;
|
|
7640
|
+
build) _disc_patterns="src/*,*.ts,*.tsx,*.js"; _disc_text="Build completed" ;;
|
|
7641
|
+
test) _disc_patterns="*.test.*,*_test.*"; _disc_text="Tests passed" ;;
|
|
7642
|
+
review) _disc_patterns="*.md,*.ts,*.tsx"; _disc_text="Review completed" ;;
|
|
7643
|
+
*) _disc_patterns="*"; _disc_text="Stage $id completed" ;;
|
|
7644
|
+
esac
|
|
7645
|
+
bash "$SCRIPT_DIR/sw-discovery.sh" broadcast "$_disc_cat" "$_disc_patterns" "$_disc_text" "" 2>/dev/null || true
|
|
7646
|
+
fi
|
|
7434
7647
|
# Log model used for prediction feedback
|
|
7435
7648
|
echo "${id}|${stage_model_used}|true" >> "${ARTIFACTS_DIR}/model-routing.log"
|
|
7436
7649
|
else
|
|
@@ -7787,6 +8000,24 @@ run_dry_run() {
|
|
|
7787
8000
|
# ─── Subcommands ────────────────────────────────────────────────────────────
|
|
7788
8001
|
|
|
7789
8002
|
pipeline_start() {
|
|
8003
|
+
# Handle --repo flag: change to directory before running
|
|
8004
|
+
if [[ -n "$REPO_OVERRIDE" ]]; then
|
|
8005
|
+
if [[ ! -d "$REPO_OVERRIDE" ]]; then
|
|
8006
|
+
error "Directory does not exist: $REPO_OVERRIDE"
|
|
8007
|
+
exit 1
|
|
8008
|
+
fi
|
|
8009
|
+
if ! cd "$REPO_OVERRIDE" 2>/dev/null; then
|
|
8010
|
+
error "Cannot cd to: $REPO_OVERRIDE"
|
|
8011
|
+
exit 1
|
|
8012
|
+
fi
|
|
8013
|
+
if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
8014
|
+
error "Not a git repository: $REPO_OVERRIDE"
|
|
8015
|
+
exit 1
|
|
8016
|
+
fi
|
|
8017
|
+
ORIGINAL_REPO_DIR="$(pwd)"
|
|
8018
|
+
info "Using repository: $ORIGINAL_REPO_DIR"
|
|
8019
|
+
fi
|
|
8020
|
+
|
|
7790
8021
|
if [[ -z "$GOAL" && -z "$ISSUE_NUMBER" ]]; then
|
|
7791
8022
|
error "Must provide --goal or --issue"
|
|
7792
8023
|
echo -e " Example: ${DIM}shipwright pipeline start --goal \"Add JWT auth\"${RESET}"
|
|
@@ -7946,9 +8177,15 @@ pipeline_start() {
|
|
|
7946
8177
|
"result=success" \
|
|
7947
8178
|
"duration_s=${total_dur_s:-0}" \
|
|
7948
8179
|
"pr_url=${pr_url:-}" \
|
|
8180
|
+
"agent_id=${PIPELINE_AGENT_ID}" \
|
|
7949
8181
|
"input_tokens=$TOTAL_INPUT_TOKENS" \
|
|
7950
8182
|
"output_tokens=$TOTAL_OUTPUT_TOKENS" \
|
|
7951
8183
|
"self_heal_count=$SELF_HEAL_COUNT"
|
|
8184
|
+
|
|
8185
|
+
# Auto-ingest pipeline outcome into recruit profiles
|
|
8186
|
+
if [[ -x "$SCRIPT_DIR/sw-recruit.sh" ]]; then
|
|
8187
|
+
bash "$SCRIPT_DIR/sw-recruit.sh" ingest-pipeline 1 2>/dev/null || true
|
|
8188
|
+
fi
|
|
7952
8189
|
else
|
|
7953
8190
|
notify "Pipeline Failed" "Goal: ${GOAL}\nFailed at: ${CURRENT_STAGE_ID:-unknown}" "error"
|
|
7954
8191
|
emit_event "pipeline.completed" \
|
|
@@ -7956,10 +8193,16 @@ pipeline_start() {
|
|
|
7956
8193
|
"result=failure" \
|
|
7957
8194
|
"duration_s=${total_dur_s:-0}" \
|
|
7958
8195
|
"failed_stage=${CURRENT_STAGE_ID:-unknown}" \
|
|
8196
|
+
"agent_id=${PIPELINE_AGENT_ID}" \
|
|
7959
8197
|
"input_tokens=$TOTAL_INPUT_TOKENS" \
|
|
7960
8198
|
"output_tokens=$TOTAL_OUTPUT_TOKENS" \
|
|
7961
8199
|
"self_heal_count=$SELF_HEAL_COUNT"
|
|
7962
8200
|
|
|
8201
|
+
# Auto-ingest pipeline outcome into recruit profiles
|
|
8202
|
+
if [[ -x "$SCRIPT_DIR/sw-recruit.sh" ]]; then
|
|
8203
|
+
bash "$SCRIPT_DIR/sw-recruit.sh" ingest-pipeline 1 2>/dev/null || true
|
|
8204
|
+
fi
|
|
8205
|
+
|
|
7963
8206
|
# Capture failure learnings to memory
|
|
7964
8207
|
if [[ -x "$SCRIPT_DIR/sw-memory.sh" ]]; then
|
|
7965
8208
|
bash "$SCRIPT_DIR/sw-memory.sh" capture "$STATE_FILE" "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
@@ -8015,18 +8258,24 @@ pipeline_start() {
|
|
|
8015
8258
|
memory_finalize_pipeline "$STATE_FILE" "$ARTIFACTS_DIR" 2>/dev/null || true
|
|
8016
8259
|
fi
|
|
8017
8260
|
|
|
8018
|
-
# Emit cost event
|
|
8261
|
+
# Emit cost event — prefer actual cost from Claude CLI when available
|
|
8019
8262
|
local model_key="${MODEL:-sonnet}"
|
|
8020
|
-
local
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8263
|
+
local total_cost
|
|
8264
|
+
if [[ -n "${TOTAL_COST_USD:-}" && "${TOTAL_COST_USD}" != "0" && "${TOTAL_COST_USD}" != "null" ]]; then
|
|
8265
|
+
total_cost="${TOTAL_COST_USD}"
|
|
8266
|
+
else
|
|
8267
|
+
# Fallback: estimate from token counts and model rates
|
|
8268
|
+
local input_cost output_cost
|
|
8269
|
+
input_cost=$(awk -v tokens="$TOTAL_INPUT_TOKENS" -v rate="$(echo "$COST_MODEL_RATES" | jq -r ".${model_key}.input // 3")" 'BEGIN{printf "%.4f", (tokens / 1000000) * rate}')
|
|
8270
|
+
output_cost=$(awk -v tokens="$TOTAL_OUTPUT_TOKENS" -v rate="$(echo "$COST_MODEL_RATES" | jq -r ".${model_key}.output // 15")" 'BEGIN{printf "%.4f", (tokens / 1000000) * rate}')
|
|
8271
|
+
total_cost=$(awk -v i="$input_cost" -v o="$output_cost" 'BEGIN{printf "%.4f", i + o}')
|
|
8272
|
+
fi
|
|
8024
8273
|
|
|
8025
8274
|
emit_event "pipeline.cost" \
|
|
8026
8275
|
"input_tokens=$TOTAL_INPUT_TOKENS" \
|
|
8027
8276
|
"output_tokens=$TOTAL_OUTPUT_TOKENS" \
|
|
8028
8277
|
"model=$model_key" \
|
|
8029
|
-
"
|
|
8278
|
+
"cost_usd=$total_cost"
|
|
8030
8279
|
|
|
8031
8280
|
return $exit_code
|
|
8032
8281
|
}
|
package/scripts/sw-pm.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="2.
|
|
9
|
+
VERSION="2.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
|
@@ -223,9 +223,57 @@ analyze_issue() {
|
|
|
223
223
|
|
|
224
224
|
# ─── recommend_team <analysis_json> ──────────────────────────────────────────
|
|
225
225
|
# Based on analysis, recommend team composition
|
|
226
|
+
# Tries recruit's AI/heuristic team composition first, falls back to hardcoded rules.
|
|
226
227
|
recommend_team() {
|
|
227
228
|
local analysis="$1"
|
|
228
229
|
|
|
230
|
+
# ── Try recruit-powered team composition first ──
|
|
231
|
+
if [[ -x "${SCRIPT_DIR:-}/sw-recruit.sh" ]]; then
|
|
232
|
+
local issue_title
|
|
233
|
+
issue_title=$(echo "$analysis" | jq -r '.title // .recommendation // ""' 2>/dev/null || true)
|
|
234
|
+
if [[ -n "$issue_title" ]]; then
|
|
235
|
+
local recruit_result
|
|
236
|
+
recruit_result=$(bash "$SCRIPT_DIR/sw-recruit.sh" team --json "$issue_title" 2>/dev/null) || true
|
|
237
|
+
if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team' &>/dev/null 2>&1; then
|
|
238
|
+
local recruit_roles recruit_model recruit_agents recruit_cost
|
|
239
|
+
recruit_roles=$(echo "$recruit_result" | jq -r '.team | join(",")')
|
|
240
|
+
recruit_model=$(echo "$recruit_result" | jq -r '.model // "sonnet"')
|
|
241
|
+
recruit_agents=$(echo "$recruit_result" | jq -r '.agents // 2')
|
|
242
|
+
recruit_cost=$(echo "$recruit_result" | jq -r '.estimated_cost // 0')
|
|
243
|
+
|
|
244
|
+
# Map recruit roles/model to PM output format
|
|
245
|
+
local max_iterations=5
|
|
246
|
+
local template="standard"
|
|
247
|
+
if [[ "$recruit_agents" -ge 4 ]]; then template="full"; max_iterations=8;
|
|
248
|
+
elif [[ "$recruit_agents" -le 1 ]]; then template="fast"; max_iterations=3;
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
local team_rec
|
|
252
|
+
team_rec=$(jq -n \
|
|
253
|
+
--arg roles "$recruit_roles" \
|
|
254
|
+
--arg template "$template" \
|
|
255
|
+
--arg model "$recruit_model" \
|
|
256
|
+
--arg max_iter "$max_iterations" \
|
|
257
|
+
--arg agents "$recruit_agents" \
|
|
258
|
+
--arg cost "$recruit_cost" \
|
|
259
|
+
'{
|
|
260
|
+
roles: ($roles | split(",")),
|
|
261
|
+
template: $template,
|
|
262
|
+
model: $model,
|
|
263
|
+
max_iterations: ($max_iter | tonumber),
|
|
264
|
+
estimated_agents: ($agents | tonumber),
|
|
265
|
+
confidence_percent: 80,
|
|
266
|
+
risk_factors: "recruit-powered recommendation",
|
|
267
|
+
mitigation_strategies: "AI-optimized team composition",
|
|
268
|
+
source: "recruit"
|
|
269
|
+
}')
|
|
270
|
+
echo "$team_rec"
|
|
271
|
+
return 0
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# ── Fallback: hardcoded heuristic team composition ──
|
|
229
277
|
local complexity risk is_security is_perf file_scope
|
|
230
278
|
complexity=$(echo "$analysis" | jq -r '.complexity')
|
|
231
279
|
risk=$(echo "$analysis" | jq -r '.risk')
|
|
@@ -442,15 +490,22 @@ cmd_orchestrate() {
|
|
|
442
490
|
emit_event "pm.orchestrate" "issue=${issue_num}"
|
|
443
491
|
}
|
|
444
492
|
|
|
445
|
-
# ─── cmd_recommend <issue_num>
|
|
493
|
+
# ─── cmd_recommend <issue_num> [--json] ──────────────────────────────────────
|
|
446
494
|
cmd_recommend() {
|
|
447
|
-
local
|
|
495
|
+
local json_mode="false"
|
|
496
|
+
local issue_num=""
|
|
497
|
+
if [[ "${1:-}" == "--json" ]]; then
|
|
498
|
+
json_mode="true"
|
|
499
|
+
issue_num="${2:-}"
|
|
500
|
+
else
|
|
501
|
+
issue_num="${1:-}"
|
|
502
|
+
fi
|
|
448
503
|
if [[ -z "$issue_num" ]]; then
|
|
449
|
-
error "Usage: shipwright pm recommend <issue-num>"
|
|
504
|
+
error "Usage: shipwright pm recommend <issue-num> [--json]"
|
|
450
505
|
return 1
|
|
451
506
|
fi
|
|
452
507
|
|
|
453
|
-
info "Generating full PM recommendation for issue #${issue_num}..."
|
|
508
|
+
[[ "$json_mode" != "true" ]] && info "Generating full PM recommendation for issue #${issue_num}..."
|
|
454
509
|
local analysis team_rec stages
|
|
455
510
|
|
|
456
511
|
analysis=$(analyze_issue "$issue_num")
|
|
@@ -472,6 +527,16 @@ cmd_recommend() {
|
|
|
472
527
|
recommendation_timestamp: "'$(now_iso)'"
|
|
473
528
|
}')
|
|
474
529
|
|
|
530
|
+
if [[ "$json_mode" == "true" ]]; then
|
|
531
|
+
echo "$recommendation"
|
|
532
|
+
ensure_pm_history
|
|
533
|
+
local tmp_hist
|
|
534
|
+
tmp_hist=$(mktemp)
|
|
535
|
+
jq --argjson rec "$recommendation" '.decisions += [$rec]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
|
|
536
|
+
emit_event "pm.recommend" "issue=${issue_num}"
|
|
537
|
+
return 0
|
|
538
|
+
fi
|
|
539
|
+
|
|
475
540
|
# Pretty-print
|
|
476
541
|
echo ""
|
|
477
542
|
echo -e "${BOLD}PM RECOMMENDATION FOR ISSUE #${issue_num}${RESET}"
|
package/scripts/sw-predictive.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="2.
|
|
9
|
+
VERSION="2.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -810,6 +810,13 @@ main() {
|
|
|
810
810
|
confirm-anomaly) predictive_confirm_anomaly "$@" ;;
|
|
811
811
|
patrol) patrol_ai_analyze "$@" ;;
|
|
812
812
|
baseline) predict_update_baseline "$@" ;;
|
|
813
|
+
inject-prevention)
|
|
814
|
+
local stage="${1:-build}"
|
|
815
|
+
local issue_json="${2:-{}}"
|
|
816
|
+
local mem_ctx="${3:-}"
|
|
817
|
+
[[ -n "$mem_ctx" && -f "$mem_ctx" ]] && mem_ctx=$(cat "$mem_ctx" 2>/dev/null || true)
|
|
818
|
+
predict_inject_prevention "$stage" "$issue_json" "$mem_ctx" || true
|
|
819
|
+
;;
|
|
813
820
|
help|--help|-h) show_help ;;
|
|
814
821
|
*) error "Unknown command: $cmd"; exit 1 ;;
|
|
815
822
|
esac
|
package/scripts/sw-prep.sh
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
9
|
-
VERSION="2.
|
|
9
|
+
VERSION="2.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Handle subcommands ───────────────────────────────────────────────────────
|
package/scripts/sw-ps.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ║ Displays a table of agents running in claude-* tmux windows with ║
|
|
6
6
|
# ║ PID, status, idle time, and pane references. ║
|
|
7
7
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
8
|
-
VERSION="2.
|
|
8
|
+
VERSION="2.1.0"
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
11
11
|
|
package/scripts/sw-quality.sh
CHANGED