shipwright-cli 2.1.0 → 2.1.2

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.
Files changed (98) hide show
  1. package/package.json +1 -1
  2. package/scripts/sw +1 -1
  3. package/scripts/sw-activity.sh +1 -1
  4. package/scripts/sw-adaptive.sh +1 -1
  5. package/scripts/sw-adversarial.sh +1 -1
  6. package/scripts/sw-architecture-enforcer.sh +1 -1
  7. package/scripts/sw-auth.sh +1 -1
  8. package/scripts/sw-autonomous.sh +1 -1
  9. package/scripts/sw-changelog.sh +1 -1
  10. package/scripts/sw-checkpoint.sh +1 -1
  11. package/scripts/sw-ci.sh +1 -1
  12. package/scripts/sw-cleanup.sh +1 -1
  13. package/scripts/sw-code-review.sh +1 -1
  14. package/scripts/sw-connect.sh +1 -1
  15. package/scripts/sw-context.sh +1 -1
  16. package/scripts/sw-cost.sh +1 -1
  17. package/scripts/sw-daemon.sh +85 -4
  18. package/scripts/sw-dashboard.sh +1 -1
  19. package/scripts/sw-db.sh +1 -1
  20. package/scripts/sw-decompose.sh +1 -1
  21. package/scripts/sw-deps.sh +1 -1
  22. package/scripts/sw-developer-simulation.sh +1 -1
  23. package/scripts/sw-discovery.sh +1 -1
  24. package/scripts/sw-docs-agent.sh +1 -1
  25. package/scripts/sw-docs.sh +1 -1
  26. package/scripts/sw-doctor.sh +1 -1
  27. package/scripts/sw-dora.sh +1 -1
  28. package/scripts/sw-durable.sh +1 -1
  29. package/scripts/sw-e2e-orchestrator.sh +1 -1
  30. package/scripts/sw-eventbus.sh +1 -1
  31. package/scripts/sw-feedback.sh +1 -1
  32. package/scripts/sw-fix.sh +1 -1
  33. package/scripts/sw-fleet-discover.sh +1 -1
  34. package/scripts/sw-fleet-viz.sh +1 -1
  35. package/scripts/sw-fleet.sh +1 -1
  36. package/scripts/sw-github-app.sh +1 -1
  37. package/scripts/sw-github-checks.sh +1 -1
  38. package/scripts/sw-github-deploy.sh +1 -1
  39. package/scripts/sw-github-graphql.sh +1 -1
  40. package/scripts/sw-guild.sh +1 -1
  41. package/scripts/sw-heartbeat.sh +1 -1
  42. package/scripts/sw-hygiene.sh +1 -1
  43. package/scripts/sw-incident.sh +1 -1
  44. package/scripts/sw-init.sh +1 -1
  45. package/scripts/sw-instrument.sh +1 -1
  46. package/scripts/sw-intelligence.sh +1 -1
  47. package/scripts/sw-jira.sh +1 -1
  48. package/scripts/sw-launchd.sh +1 -1
  49. package/scripts/sw-linear.sh +1 -1
  50. package/scripts/sw-logs.sh +1 -1
  51. package/scripts/sw-loop.sh +24 -8
  52. package/scripts/sw-memory.sh +59 -57
  53. package/scripts/sw-mission-control.sh +1 -1
  54. package/scripts/sw-model-router.sh +1 -1
  55. package/scripts/sw-otel.sh +1 -1
  56. package/scripts/sw-oversight.sh +1 -1
  57. package/scripts/sw-pipeline-composer.sh +1 -1
  58. package/scripts/sw-pipeline-vitals.sh +1 -1
  59. package/scripts/sw-pipeline.sh +82 -7
  60. package/scripts/sw-pm.sh +1 -1
  61. package/scripts/sw-pr-lifecycle.sh +1 -1
  62. package/scripts/sw-predictive.sh +1 -1
  63. package/scripts/sw-prep.sh +18 -14
  64. package/scripts/sw-ps.sh +1 -1
  65. package/scripts/sw-public-dashboard.sh +1 -1
  66. package/scripts/sw-quality.sh +1 -1
  67. package/scripts/sw-reaper.sh +1 -1
  68. package/scripts/sw-regression.sh +1 -1
  69. package/scripts/sw-release-manager.sh +1 -1
  70. package/scripts/sw-release.sh +1 -1
  71. package/scripts/sw-remote.sh +1 -1
  72. package/scripts/sw-replay.sh +1 -1
  73. package/scripts/sw-retro.sh +1 -1
  74. package/scripts/sw-scale.sh +1 -1
  75. package/scripts/sw-security-audit.sh +1 -1
  76. package/scripts/sw-self-optimize.sh +1 -1
  77. package/scripts/sw-session.sh +1 -1
  78. package/scripts/sw-setup.sh +1 -1
  79. package/scripts/sw-standup.sh +1 -1
  80. package/scripts/sw-status.sh +1 -1
  81. package/scripts/sw-strategic.sh +1 -1
  82. package/scripts/sw-stream.sh +1 -1
  83. package/scripts/sw-swarm.sh +1 -1
  84. package/scripts/sw-team-stages.sh +1 -1
  85. package/scripts/sw-templates.sh +1 -1
  86. package/scripts/sw-testgen.sh +1 -1
  87. package/scripts/sw-tmux-pipeline.sh +1 -1
  88. package/scripts/sw-tmux.sh +1 -1
  89. package/scripts/sw-trace.sh +1 -1
  90. package/scripts/sw-tracker.sh +1 -1
  91. package/scripts/sw-triage.sh +1 -1
  92. package/scripts/sw-upgrade.sh +1 -1
  93. package/scripts/sw-ux.sh +30 -2
  94. package/scripts/sw-webhook.sh +1 -1
  95. package/scripts/sw-widgets.sh +1 -1
  96. package/scripts/sw-worktree.sh +1 -1
  97. package/templates/pipelines/autonomous.json +2 -2
  98. package/tmux/tmux.conf +3 -0
@@ -58,7 +58,7 @@ MAX_RESTARTS=0
58
58
  SESSION_RESTART=false
59
59
  RESTART_COUNT=0
60
60
  REPO_OVERRIDE=""
61
- VERSION="2.1.0"
61
+ VERSION="2.1.2"
62
62
 
63
63
  # ─── Token Tracking ─────────────────────────────────────────────────────────
64
64
  LOOP_INPUT_TOKENS=0
@@ -1021,19 +1021,34 @@ run_test_gate() {
1021
1021
  write_error_summary() {
1022
1022
  local error_json="$LOG_DIR/error-summary.json"
1023
1023
 
1024
- # Only write on test failure
1024
+ # Write on test failure OR build failure (non-zero exit from Claude iteration)
1025
+ local build_log="$LOG_DIR/iteration-${ITERATION}.log"
1025
1026
  if [[ "${TEST_PASSED:-}" != "false" ]]; then
1026
- # Clear previous error summary on success
1027
- rm -f "$error_json" 2>/dev/null || true
1028
- return
1027
+ # Check for build-level failures (Claude iteration exited non-zero or produced errors)
1028
+ local build_had_errors=false
1029
+ if [[ -f "$build_log" ]]; then
1030
+ local build_err_count
1031
+ build_err_count=$(tail -30 "$build_log" 2>/dev/null | grep -ciE '(error|fail|exception|panic|FATAL)' || true)
1032
+ [[ "${build_err_count:-0}" -gt 0 ]] && build_had_errors=true
1033
+ fi
1034
+ if [[ "$build_had_errors" != "true" ]]; then
1035
+ # Clear previous error summary on success
1036
+ rm -f "$error_json" 2>/dev/null || true
1037
+ return
1038
+ fi
1029
1039
  fi
1030
1040
 
1041
+ # Prefer test log, fall back to build log
1031
1042
  local test_log="${TEST_LOG_FILE:-$LOG_DIR/tests-iter-${ITERATION}.log}"
1032
- [[ ! -f "$test_log" ]] && return
1043
+ local source_log="$test_log"
1044
+ if [[ ! -f "$source_log" ]]; then
1045
+ source_log="$build_log"
1046
+ fi
1047
+ [[ ! -f "$source_log" ]] && return
1033
1048
 
1034
1049
  # Extract error lines (last 30 lines, grep for error patterns)
1035
1050
  local error_lines_raw
1036
- error_lines_raw=$(tail -30 "$test_log" 2>/dev/null | grep -iE '(error|fail|assert|exception|panic|FAIL|TypeError|ReferenceError|SyntaxError)' | head -10 || true)
1051
+ error_lines_raw=$(tail -30 "$source_log" 2>/dev/null | grep -iE '(error|fail|assert|exception|panic|FAIL|TypeError|ReferenceError|SyntaxError)' | head -10 || true)
1037
1052
 
1038
1053
  local error_count=0
1039
1054
  if [[ -n "$error_lines_raw" ]]; then
@@ -2434,8 +2449,9 @@ run_loop_with_restarts() {
2434
2449
  [[ -f "$old_log" ]] && mv "$old_log" "$restart_archive/" 2>/dev/null || true
2435
2450
  done
2436
2451
  # Archive progress.md and error-summary.json from previous session
2452
+ # IMPORTANT: copy (not move) error-summary.json so the fresh session can still read it
2437
2453
  [[ -f "$LOG_DIR/progress.md" ]] && cp "$LOG_DIR/progress.md" "$restart_archive/progress.md" 2>/dev/null || true
2438
- [[ -f "$LOG_DIR/error-summary.json" ]] && mv "$LOG_DIR/error-summary.json" "$restart_archive/" 2>/dev/null || true
2454
+ [[ -f "$LOG_DIR/error-summary.json" ]] && cp "$LOG_DIR/error-summary.json" "$restart_archive/" 2>/dev/null || true
2439
2455
 
2440
2456
  write_state
2441
2457
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -1585,59 +1585,61 @@ show_help() {
1585
1585
 
1586
1586
  # ─── Command Router ─────────────────────────────────────────────────────────
1587
1587
 
1588
- SUBCOMMAND="${1:-help}"
1589
- shift 2>/dev/null || true
1590
-
1591
- case "$SUBCOMMAND" in
1592
- show)
1593
- memory_show "$@"
1594
- ;;
1595
- search)
1596
- memory_search "$@"
1597
- ;;
1598
- forget)
1599
- memory_forget "$@"
1600
- ;;
1601
- export)
1602
- memory_export
1603
- ;;
1604
- import)
1605
- memory_import "$@"
1606
- ;;
1607
- stats)
1608
- memory_stats
1609
- ;;
1610
- capture)
1611
- memory_capture_pipeline "$@"
1612
- ;;
1613
- inject)
1614
- memory_inject_context "$@"
1615
- ;;
1616
- pattern)
1617
- memory_capture_pattern "$@"
1618
- ;;
1619
- get)
1620
- memory_get_baseline "$@"
1621
- ;;
1622
- metric)
1623
- memory_update_metrics "$@"
1624
- ;;
1625
- decision)
1626
- memory_capture_decision "$@"
1627
- ;;
1628
- analyze-failure)
1629
- memory_analyze_failure "$@"
1630
- ;;
1631
- fix-outcome)
1632
- memory_record_fix_outcome "$@"
1633
- ;;
1634
- help|--help|-h)
1635
- show_help
1636
- ;;
1637
- *)
1638
- error "Unknown command: ${SUBCOMMAND}"
1639
- echo ""
1640
- show_help
1641
- exit 1
1642
- ;;
1643
- esac
1588
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
1589
+ SUBCOMMAND="${1:-help}"
1590
+ shift 2>/dev/null || true
1591
+
1592
+ case "$SUBCOMMAND" in
1593
+ show)
1594
+ memory_show "$@"
1595
+ ;;
1596
+ search)
1597
+ memory_search "$@"
1598
+ ;;
1599
+ forget)
1600
+ memory_forget "$@"
1601
+ ;;
1602
+ export)
1603
+ memory_export
1604
+ ;;
1605
+ import)
1606
+ memory_import "$@"
1607
+ ;;
1608
+ stats)
1609
+ memory_stats
1610
+ ;;
1611
+ capture)
1612
+ memory_capture_pipeline "$@"
1613
+ ;;
1614
+ inject)
1615
+ memory_inject_context "$@"
1616
+ ;;
1617
+ pattern)
1618
+ memory_capture_pattern "$@"
1619
+ ;;
1620
+ get)
1621
+ memory_get_baseline "$@"
1622
+ ;;
1623
+ metric)
1624
+ memory_update_metrics "$@"
1625
+ ;;
1626
+ decision)
1627
+ memory_capture_decision "$@"
1628
+ ;;
1629
+ analyze-failure)
1630
+ memory_analyze_failure "$@"
1631
+ ;;
1632
+ fix-outcome)
1633
+ memory_record_fix_outcome "$@"
1634
+ ;;
1635
+ help|--help|-h)
1636
+ show_help
1637
+ ;;
1638
+ *)
1639
+ error "Unknown command: ${SUBCOMMAND}"
1640
+ echo ""
1641
+ show_help
1642
+ exit 1
1643
+ ;;
1644
+ esac
1645
+ fi
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.1.0"
10
+ VERSION="2.1.2"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.1.0"
10
+ VERSION="2.1.2"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.1.0"
10
+ VERSION="2.1.2"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
14
+ VERSION="2.1.2"
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
17
17
 
@@ -56,6 +56,20 @@ if [[ -f "$SCRIPT_DIR/sw-pipeline-vitals.sh" ]]; then
56
56
  source "$SCRIPT_DIR/sw-pipeline-vitals.sh"
57
57
  fi
58
58
 
59
+ # ─── Memory, Optimization & Discovery (optional) ─────────────────────────
60
+ # shellcheck source=sw-memory.sh
61
+ if [[ -f "$SCRIPT_DIR/sw-memory.sh" ]]; then
62
+ source "$SCRIPT_DIR/sw-memory.sh"
63
+ fi
64
+ # shellcheck source=sw-self-optimize.sh
65
+ if [[ -f "$SCRIPT_DIR/sw-self-optimize.sh" ]]; then
66
+ source "$SCRIPT_DIR/sw-self-optimize.sh"
67
+ fi
68
+ # shellcheck source=sw-discovery.sh
69
+ if [[ -f "$SCRIPT_DIR/sw-discovery.sh" ]]; then
70
+ source "$SCRIPT_DIR/sw-discovery.sh"
71
+ fi
72
+
59
73
  # ─── GitHub API Modules (optional) ─────────────────────────────────────────
60
74
  # shellcheck source=sw-github-graphql.sh
61
75
  [[ -f "$SCRIPT_DIR/sw-github-graphql.sh" ]] && source "$SCRIPT_DIR/sw-github-graphql.sh"
@@ -204,6 +218,7 @@ MODEL=""
204
218
  AGENTS=""
205
219
  PIPELINE_AGENT_ID="${PIPELINE_AGENT_ID:-pipeline-$$}"
206
220
  SKIP_GATES=false
221
+ HEADLESS=false
207
222
  GIT_BRANCH=""
208
223
  GITHUB_ISSUE=""
209
224
  TASK_TYPE=""
@@ -226,6 +241,7 @@ CLEANUP_WORKTREE=false
226
241
  ORIGINAL_REPO_DIR=""
227
242
  REPO_OVERRIDE=""
228
243
  _cleanup_done=""
244
+ PIPELINE_EXIT_CODE=1 # assume failure until run_pipeline succeeds
229
245
 
230
246
  # GitHub metadata (populated during intake)
231
247
  ISSUE_LABELS=""
@@ -273,6 +289,7 @@ show_help() {
273
289
  echo -e " ${DIM}--model <model>${RESET} Override AI model (opus, sonnet, haiku)"
274
290
  echo -e " ${DIM}--agents <n>${RESET} Override agent count"
275
291
  echo -e " ${DIM}--skip-gates${RESET} Auto-approve all gates (fully autonomous)"
292
+ echo -e " ${DIM}--headless${RESET} Full headless mode (skip gates, no prompts)"
276
293
  echo -e " ${DIM}--base <branch>${RESET} Base branch for PR (default: main)"
277
294
  echo -e " ${DIM}--reviewers \"a,b\"${RESET} Request PR reviewers (auto-detected if omitted)"
278
295
  echo -e " ${DIM}--labels \"a,b\"${RESET} Add labels to PR (inherited from issue if omitted)"
@@ -357,6 +374,7 @@ parse_args() {
357
374
  --model) MODEL="$2"; shift 2 ;;
358
375
  --agents) AGENTS="$2"; shift 2 ;;
359
376
  --skip-gates) SKIP_GATES=true; shift ;;
377
+ --headless) HEADLESS=true; SKIP_GATES=true; shift ;;
360
378
  --base) BASE_BRANCH="$2"; shift 2 ;;
361
379
  --reviewers) REVIEWERS="$2"; shift 2 ;;
362
380
  --labels) LABELS="$2"; shift 2 ;;
@@ -393,6 +411,20 @@ parse_args() {
393
411
  PIPELINE_NAME_ARG=""
394
412
  parse_args "$@"
395
413
 
414
+ # ─── Non-Interactive Detection ──────────────────────────────────────────────
415
+ # When stdin is not a terminal (background, pipe, nohup, tmux send-keys),
416
+ # auto-enable headless mode to prevent read prompts from killing the script.
417
+ if [[ ! -t 0 ]]; then
418
+ HEADLESS=true
419
+ if [[ "$SKIP_GATES" != "true" ]]; then
420
+ SKIP_GATES=true
421
+ fi
422
+ fi
423
+ # --worktree implies headless when stdin is not a terminal
424
+ if [[ "$AUTO_WORKTREE" == "true" && "$SKIP_GATES" != "true" && ! -t 0 ]]; then
425
+ SKIP_GATES=true
426
+ fi
427
+
396
428
  # ─── Directory Setup ────────────────────────────────────────────────────────
397
429
 
398
430
  setup_dirs() {
@@ -411,10 +443,11 @@ find_pipeline_config() {
411
443
  local name="$1"
412
444
  local locations=(
413
445
  "$REPO_DIR/templates/pipelines/${name}.json"
446
+ "${PROJECT_ROOT:-}/templates/pipelines/${name}.json"
414
447
  "$HOME/.shipwright/pipelines/${name}.json"
415
448
  )
416
449
  for loc in "${locations[@]}"; do
417
- if [[ -f "$loc" ]]; then
450
+ if [[ -n "$loc" && -f "$loc" ]]; then
418
451
  echo "$loc"
419
452
  return 0
420
453
  fi
@@ -7480,7 +7513,9 @@ run_pipeline() {
7480
7513
  if [[ "$build_gate" == "approve" && "$SKIP_GATES" != "true" ]]; then
7481
7514
  show_stage_preview "build"
7482
7515
  local answer=""
7483
- read -rp " Proceed with build+test (self-healing)? [Y/n] " answer
7516
+ if [[ -t 0 ]]; then
7517
+ read -rp " Proceed with build+test (self-healing)? [Y/n] " answer || true
7518
+ fi
7484
7519
  if [[ "$answer" =~ ^[Nn] ]]; then
7485
7520
  update_status "paused" "build"
7486
7521
  info "Pipeline paused. Resume with: ${DIM}shipwright pipeline resume${RESET}"
@@ -7518,7 +7553,12 @@ run_pipeline() {
7518
7553
  if [[ "$gate" == "approve" && "$SKIP_GATES" != "true" ]]; then
7519
7554
  show_stage_preview "$id"
7520
7555
  local answer=""
7521
- read -rp " Proceed with ${id}? [Y/n] " answer
7556
+ if [[ -t 0 ]]; then
7557
+ read -rp " Proceed with ${id}? [Y/n] " answer || true
7558
+ else
7559
+ # Non-interactive: auto-approve (shouldn't reach here if headless detection works)
7560
+ info "Non-interactive mode — auto-approving ${id}"
7561
+ fi
7522
7562
  if [[ "$answer" =~ ^[Nn] ]]; then
7523
7563
  update_status "paused" "$id"
7524
7564
  info "Pipeline paused at ${BOLD}$id${RESET}. Resume with: ${DIM}shipwright pipeline resume${RESET}"
@@ -7826,8 +7866,25 @@ pipeline_cleanup_worktree() {
7826
7866
 
7827
7867
  if [[ -n "${ORIGINAL_REPO_DIR:-}" && "$worktree_path" != "$ORIGINAL_REPO_DIR" ]]; then
7828
7868
  cd "$ORIGINAL_REPO_DIR" 2>/dev/null || cd /
7829
- info "Cleaning up worktree: ${DIM}${worktree_path}${RESET}"
7830
- git worktree remove --force "$worktree_path" 2>/dev/null || true
7869
+ # Only clean up worktree on success — preserve on failure for inspection
7870
+ if [[ "${PIPELINE_EXIT_CODE:-1}" -eq 0 ]]; then
7871
+ info "Cleaning up worktree: ${DIM}${worktree_path}${RESET}"
7872
+ # Extract branch name before removing worktree
7873
+ local _wt_branch=""
7874
+ _wt_branch=$(git worktree list --porcelain 2>/dev/null | grep -A1 "worktree ${worktree_path}$" | grep "^branch " | sed 's|^branch refs/heads/||' || true)
7875
+ git worktree remove --force "$worktree_path" 2>/dev/null || true
7876
+ # Clean up the local branch
7877
+ if [[ -n "$_wt_branch" ]]; then
7878
+ git branch -D "$_wt_branch" 2>/dev/null || true
7879
+ fi
7880
+ # Clean up the remote branch (if it was pushed)
7881
+ if [[ -n "$_wt_branch" && "${NO_GITHUB:-}" != "true" ]]; then
7882
+ git push origin --delete "$_wt_branch" 2>/dev/null || true
7883
+ fi
7884
+ else
7885
+ warn "Pipeline failed — worktree preserved for inspection: ${DIM}${worktree_path}${RESET}"
7886
+ warn "Clean up manually: ${DIM}git worktree remove --force ${worktree_path}${RESET}"
7887
+ fi
7831
7888
  fi
7832
7889
  }
7833
7890
 
@@ -8110,7 +8167,9 @@ pipeline_start() {
8110
8167
 
8111
8168
  local gate_count
8112
8169
  gate_count=$(jq '[.stages[] | select(.gate == "approve" and .enabled == true)] | length' "$PIPELINE_CONFIG")
8113
- if [[ "$SKIP_GATES" == "true" ]]; then
8170
+ if [[ "$HEADLESS" == "true" ]]; then
8171
+ echo -e " ${BOLD}Gates:${RESET} ${YELLOW}all auto (headless — non-interactive stdin detected)${RESET}"
8172
+ elif [[ "$SKIP_GATES" == "true" ]]; then
8114
8173
  echo -e " ${BOLD}Gates:${RESET} ${YELLOW}all auto (--skip-gates)${RESET}"
8115
8174
  else
8116
8175
  echo -e " ${BOLD}Gates:${RESET} ${gate_count} approval gate(s)"
@@ -8162,6 +8221,7 @@ pipeline_start() {
8162
8221
 
8163
8222
  run_pipeline
8164
8223
  local exit_code=$?
8224
+ PIPELINE_EXIT_CODE="$exit_code"
8165
8225
 
8166
8226
  # Send completion notification + event
8167
8227
  local total_dur_s=""
@@ -8252,12 +8312,27 @@ pipeline_start() {
8252
8312
  # Record pipeline outcome for model routing feedback loop
8253
8313
  if type optimize_analyze_outcome &>/dev/null 2>&1; then
8254
8314
  optimize_analyze_outcome "$STATE_FILE" 2>/dev/null || true
8315
+ # Tune template weights based on accumulated outcomes
8316
+ if type optimize_tune_templates &>/dev/null 2>&1; then
8317
+ optimize_tune_templates 2>/dev/null || true
8318
+ fi
8255
8319
  fi
8256
8320
 
8257
8321
  if type memory_finalize_pipeline &>/dev/null 2>&1; then
8258
8322
  memory_finalize_pipeline "$STATE_FILE" "$ARTIFACTS_DIR" 2>/dev/null || true
8259
8323
  fi
8260
8324
 
8325
+ # Broadcast discovery for cross-pipeline learning
8326
+ if type broadcast_discovery &>/dev/null 2>&1; then
8327
+ local _disc_result="failure"
8328
+ [[ "$exit_code" -eq 0 ]] && _disc_result="success"
8329
+ local _disc_files=""
8330
+ _disc_files=$(git diff --name-only HEAD~1 HEAD 2>/dev/null | head -20 | tr '\n' ',' || true)
8331
+ broadcast_discovery "pipeline_${_disc_result}" "${_disc_files:-unknown}" \
8332
+ "Pipeline ${_disc_result} for issue #${ISSUE_NUMBER:-0} (${PIPELINE_NAME:-unknown} template, stage=${CURRENT_STAGE_ID:-unknown})" \
8333
+ "${_disc_result}" 2>/dev/null || true
8334
+ fi
8335
+
8261
8336
  # Emit cost event — prefer actual cost from Claude CLI when available
8262
8337
  local model_key="${MODEL:-sonnet}"
8263
8338
  local total_cost
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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -1361,20 +1361,22 @@ prep_generate_manifest() {
1361
1361
 
1362
1362
  local files_json="{"
1363
1363
  local first=true
1364
- for entry in "${GENERATED_FILES[@]}"; do
1365
- local fname flines
1366
- fname="${entry%%|*}"
1367
- flines="${entry##*|}"
1368
- local checksum
1369
- checksum=$(compute_md5 "$PROJECT_ROOT/$fname" || echo "unknown")
1370
- if $first; then
1371
- first=false
1372
- else
1373
- files_json+=","
1374
- fi
1375
- files_json+="
1364
+ if [[ ${#GENERATED_FILES[@]} -gt 0 ]]; then
1365
+ for entry in "${GENERATED_FILES[@]}"; do
1366
+ local fname flines
1367
+ fname="${entry%%|*}"
1368
+ flines="${entry##*|}"
1369
+ local checksum
1370
+ checksum=$(compute_md5 "$PROJECT_ROOT/$fname" || echo "unknown")
1371
+ if $first; then
1372
+ first=false
1373
+ else
1374
+ files_json+=","
1375
+ fi
1376
+ files_json+="
1376
1377
  \"${fname}\": { \"checksum\": \"${checksum}\", \"lines\": ${flines} }"
1377
- done
1378
+ done
1379
+ fi
1378
1380
  files_json+="
1379
1381
  }"
1380
1382
 
@@ -1580,12 +1582,14 @@ prep_report() {
1580
1582
  echo -e " ${BOLD}Files:${RESET} ${#GENERATED_FILES[@]} generated"
1581
1583
  echo ""
1582
1584
  echo -e " ${BOLD}Created:${RESET}"
1585
+ if [[ ${#GENERATED_FILES[@]} -gt 0 ]]; then
1583
1586
  for entry in "${GENERATED_FILES[@]}"; do
1584
1587
  local fname flines
1585
1588
  fname="${entry%%|*}"
1586
1589
  flines="${entry##*|}"
1587
1590
  printf " ${GREEN}${BOLD}✓${RESET} %-42s ${DIM}(%s lines)${RESET}\n" "$fname" "$flines"
1588
1591
  done
1592
+ fi
1589
1593
  echo ""
1590
1594
  echo -e " ${DIM}Next steps:${RESET}"
1591
1595
  echo -e " ${DIM}1. Review generated files and customize as needed${RESET}"
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.1.0"
8
+ VERSION="2.1.2"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -11,7 +11,7 @@
11
11
  # ║ shipwright reaper --watch Continuous loop (default: 5s) ║
12
12
  # ║ shipwright reaper --dry-run Preview what would be reaped ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="2.1.0"
14
+ VERSION="2.1.2"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
12
12
 
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -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.1.0"
9
+ VERSION="2.1.2"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────