shipwright-cli 3.0.0 → 3.2.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.
Files changed (143) hide show
  1. package/README.md +21 -7
  2. package/completions/_shipwright +247 -93
  3. package/completions/shipwright.bash +69 -15
  4. package/completions/shipwright.fish +309 -41
  5. package/config/decision-tiers.json +55 -0
  6. package/config/defaults.json +25 -2
  7. package/config/event-schema.json +142 -5
  8. package/config/policy.json +8 -0
  9. package/dashboard/public/index.html +6 -0
  10. package/dashboard/public/styles.css +76 -0
  11. package/dashboard/server.ts +51 -0
  12. package/dashboard/src/core/api.ts +5 -0
  13. package/dashboard/src/types/api.ts +10 -0
  14. package/dashboard/src/views/metrics.ts +69 -1
  15. package/package.json +3 -3
  16. package/scripts/lib/architecture.sh +2 -1
  17. package/scripts/lib/bootstrap.sh +0 -0
  18. package/scripts/lib/config.sh +0 -0
  19. package/scripts/lib/daemon-adaptive.sh +4 -2
  20. package/scripts/lib/daemon-dispatch.sh +24 -1
  21. package/scripts/lib/daemon-failure.sh +0 -0
  22. package/scripts/lib/daemon-health.sh +0 -0
  23. package/scripts/lib/daemon-patrol.sh +42 -7
  24. package/scripts/lib/daemon-poll.sh +17 -0
  25. package/scripts/lib/daemon-state.sh +17 -0
  26. package/scripts/lib/daemon-triage.sh +1 -1
  27. package/scripts/lib/decide-autonomy.sh +295 -0
  28. package/scripts/lib/decide-scoring.sh +228 -0
  29. package/scripts/lib/decide-signals.sh +462 -0
  30. package/scripts/lib/fleet-failover.sh +0 -0
  31. package/scripts/lib/helpers.sh +19 -18
  32. package/scripts/lib/pipeline-detection.sh +1 -1
  33. package/scripts/lib/pipeline-github.sh +0 -0
  34. package/scripts/lib/pipeline-intelligence.sh +23 -4
  35. package/scripts/lib/pipeline-quality-checks.sh +11 -6
  36. package/scripts/lib/pipeline-quality.sh +0 -0
  37. package/scripts/lib/pipeline-stages.sh +330 -33
  38. package/scripts/lib/pipeline-state.sh +14 -0
  39. package/scripts/lib/policy.sh +0 -0
  40. package/scripts/lib/test-helpers.sh +0 -0
  41. package/scripts/postinstall.mjs +75 -1
  42. package/scripts/signals/example-collector.sh +36 -0
  43. package/scripts/sw +8 -4
  44. package/scripts/sw-activity.sh +1 -7
  45. package/scripts/sw-adaptive.sh +7 -7
  46. package/scripts/sw-adversarial.sh +1 -1
  47. package/scripts/sw-architecture-enforcer.sh +1 -1
  48. package/scripts/sw-auth.sh +1 -1
  49. package/scripts/sw-autonomous.sh +1 -1
  50. package/scripts/sw-changelog.sh +1 -1
  51. package/scripts/sw-checkpoint.sh +1 -1
  52. package/scripts/sw-ci.sh +11 -6
  53. package/scripts/sw-cleanup.sh +1 -1
  54. package/scripts/sw-code-review.sh +36 -17
  55. package/scripts/sw-connect.sh +1 -1
  56. package/scripts/sw-context.sh +1 -1
  57. package/scripts/sw-cost.sh +71 -5
  58. package/scripts/sw-daemon.sh +6 -3
  59. package/scripts/sw-dashboard.sh +1 -1
  60. package/scripts/sw-db.sh +53 -38
  61. package/scripts/sw-decide.sh +685 -0
  62. package/scripts/sw-decompose.sh +1 -1
  63. package/scripts/sw-deps.sh +1 -1
  64. package/scripts/sw-developer-simulation.sh +1 -1
  65. package/scripts/sw-discovery.sh +80 -4
  66. package/scripts/sw-doc-fleet.sh +1 -1
  67. package/scripts/sw-docs-agent.sh +1 -1
  68. package/scripts/sw-docs.sh +1 -1
  69. package/scripts/sw-doctor.sh +1 -1
  70. package/scripts/sw-dora.sh +1 -1
  71. package/scripts/sw-durable.sh +9 -5
  72. package/scripts/sw-e2e-orchestrator.sh +1 -1
  73. package/scripts/sw-eventbus.sh +7 -4
  74. package/scripts/sw-evidence.sh +1 -1
  75. package/scripts/sw-feedback.sh +1 -1
  76. package/scripts/sw-fix.sh +1 -1
  77. package/scripts/sw-fleet-discover.sh +1 -1
  78. package/scripts/sw-fleet-viz.sh +6 -4
  79. package/scripts/sw-fleet.sh +1 -1
  80. package/scripts/sw-github-app.sh +3 -2
  81. package/scripts/sw-github-checks.sh +1 -1
  82. package/scripts/sw-github-deploy.sh +1 -1
  83. package/scripts/sw-github-graphql.sh +1 -1
  84. package/scripts/sw-guild.sh +1 -1
  85. package/scripts/sw-heartbeat.sh +1 -1
  86. package/scripts/sw-hygiene.sh +5 -3
  87. package/scripts/sw-incident.sh +9 -5
  88. package/scripts/sw-init.sh +1 -1
  89. package/scripts/sw-instrument.sh +1 -1
  90. package/scripts/sw-intelligence.sh +11 -6
  91. package/scripts/sw-jira.sh +1 -1
  92. package/scripts/sw-launchd.sh +1 -1
  93. package/scripts/sw-linear.sh +1 -1
  94. package/scripts/sw-logs.sh +1 -1
  95. package/scripts/sw-loop.sh +338 -32
  96. package/scripts/sw-memory.sh +23 -6
  97. package/scripts/sw-mission-control.sh +1 -1
  98. package/scripts/sw-model-router.sh +3 -2
  99. package/scripts/sw-otel.sh +8 -4
  100. package/scripts/sw-oversight.sh +1 -1
  101. package/scripts/sw-pipeline-composer.sh +3 -1
  102. package/scripts/sw-pipeline-vitals.sh +11 -6
  103. package/scripts/sw-pipeline.sh +92 -8
  104. package/scripts/sw-pm.sh +5 -4
  105. package/scripts/sw-pr-lifecycle.sh +7 -4
  106. package/scripts/sw-predictive.sh +11 -5
  107. package/scripts/sw-prep.sh +1 -1
  108. package/scripts/sw-ps.sh +1 -1
  109. package/scripts/sw-public-dashboard.sh +3 -2
  110. package/scripts/sw-quality.sh +21 -10
  111. package/scripts/sw-reaper.sh +1 -1
  112. package/scripts/sw-recruit.sh +1 -1
  113. package/scripts/sw-regression.sh +1 -1
  114. package/scripts/sw-release-manager.sh +1 -1
  115. package/scripts/sw-release.sh +1 -1
  116. package/scripts/sw-remote.sh +1 -1
  117. package/scripts/sw-replay.sh +1 -1
  118. package/scripts/sw-retro.sh +1 -1
  119. package/scripts/sw-review-rerun.sh +1 -1
  120. package/scripts/sw-scale.sh +69 -11
  121. package/scripts/sw-security-audit.sh +1 -1
  122. package/scripts/sw-self-optimize.sh +168 -4
  123. package/scripts/sw-session.sh +3 -3
  124. package/scripts/sw-setup.sh +1 -1
  125. package/scripts/sw-standup.sh +1 -1
  126. package/scripts/sw-status.sh +1 -1
  127. package/scripts/sw-strategic.sh +11 -6
  128. package/scripts/sw-stream.sh +7 -4
  129. package/scripts/sw-swarm.sh +3 -2
  130. package/scripts/sw-team-stages.sh +1 -1
  131. package/scripts/sw-templates.sh +3 -3
  132. package/scripts/sw-testgen.sh +11 -6
  133. package/scripts/sw-tmux-pipeline.sh +1 -1
  134. package/scripts/sw-tmux.sh +35 -1
  135. package/scripts/sw-trace.sh +1 -1
  136. package/scripts/sw-tracker.sh +1 -1
  137. package/scripts/sw-triage.sh +7 -7
  138. package/scripts/sw-upgrade.sh +1 -1
  139. package/scripts/sw-ux.sh +1 -1
  140. package/scripts/sw-webhook.sh +3 -2
  141. package/scripts/sw-widgets.sh +7 -4
  142. package/scripts/sw-worktree.sh +1 -1
  143. package/scripts/update-homebrew-sha.sh +21 -15
@@ -5,7 +5,7 @@
5
5
  # ║ Streams tmux pane output in real-time to the dashboard or CLI. ║
6
6
  # ║ Captures output periodically, tags by agent/team, supports replay. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.0.0"
8
+ VERSION="3.2.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -113,7 +113,8 @@ capture_pane_output() {
113
113
 
114
114
  # Trim to buffer size (keep latest N lines)
115
115
  local line_count
116
- line_count=$(wc -l < "$pane_file" 2>/dev/null || echo 0)
116
+ line_count=$(wc -l < "$pane_file" 2>/dev/null || true)
117
+ line_count="${line_count:-0}"
117
118
  if [[ "$line_count" -gt "$BUFFER_LINES" ]]; then
118
119
  local skip=$((line_count - BUFFER_LINES))
119
120
  tail -n "$BUFFER_LINES" "$pane_file" > "${pane_file}.tmp"
@@ -273,8 +274,10 @@ stream_list() {
273
274
 
274
275
  # Get file size and line count
275
276
  local file_size lines_count
276
- file_size=$(stat -f%z "$stream_file" 2>/dev/null || stat -c%s "$stream_file" 2>/dev/null || echo 0)
277
- lines_count=$(wc -l < "$stream_file" 2>/dev/null || echo 0)
277
+ file_size=$(stat -f%z "$stream_file" 2>/dev/null || stat -c%s "$stream_file" 2>/dev/null || true)
278
+ file_size="${file_size:-0}"
279
+ lines_count=$(wc -l < "$stream_file" 2>/dev/null || true)
280
+ lines_count="${lines_count:-0}"
278
281
 
279
282
  # Get latest timestamp
280
283
  local latest_ts
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -359,7 +359,8 @@ cmd_scale() {
359
359
  fi
360
360
 
361
361
  local current_agents
362
- current_agents=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -cE '^shipwright-sw-agent|^swarm-' || echo "0")
362
+ current_agents=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -cE '^shipwright-sw-agent|^swarm-' || true)
363
+ current_agents="${current_agents:-0}"
363
364
 
364
365
  local target_agents=1
365
366
  if [[ "$queue_depth" -gt 5 ]]; then
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -5,7 +5,7 @@
5
5
  # ║ Templates define reusable agent team configurations (roles, layout, ║
6
6
  # ║ focus areas) that shipwright session --template can use to scaffold teams. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.0.0"
8
+ VERSION="3.2.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -82,8 +82,8 @@ json_agent_count() {
82
82
  if command -v jq >/dev/null 2>&1; then
83
83
  jq -r '.agents // [] | length' "$file" 2>/dev/null
84
84
  else
85
- grep -c '"name"' "$file" 2>/dev/null || echo "0"
86
- fi
85
+ grep -c '"name"' "$file" 2>/dev/null || true
86
+ fi | tr -d ' ' | sed 's/^$/0/'
87
87
  }
88
88
 
89
89
  # Print agent details from a template
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -98,7 +98,8 @@ analyze_coverage() {
98
98
  if [[ -f "$target_script" ]]; then
99
99
  # Parse function definitions
100
100
  function_names=$(grep -E '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' "$target_script" | sed 's/().*//' || echo "")
101
- total_functions=$(echo "$function_names" | grep -c . || echo "0")
101
+ total_functions=$(echo "$function_names" | grep -c . || true)
102
+ total_functions="${total_functions:-0}"
102
103
  fi
103
104
 
104
105
  # Find existing tests that call these functions
@@ -182,7 +183,8 @@ EOF
182
183
  fi
183
184
 
184
185
  local untested_count=0
185
- [[ -n "$untested_functions" ]] && untested_count=$(echo "$untested_functions" | grep -c . || echo "0")
186
+ [[ -n "$untested_functions" ]] && untested_count=$(echo "$untested_functions" | grep -c . || true)
187
+ untested_count="${untested_count:-0}"
186
188
 
187
189
  if [[ $untested_count -eq 0 ]]; then
188
190
  success "All functions have tests"
@@ -338,15 +340,18 @@ score_quality() {
338
340
 
339
341
  # Count assertions
340
342
  local assertion_count=0
341
- assertion_count=$(grep -c -E '(assert_|test_|expect_)' "$test_file" || echo "0")
343
+ assertion_count=$(grep -c -E '(assert_|test_|expect_)' "$test_file" || true)
344
+ assertion_count="${assertion_count:-0}"
342
345
 
343
346
  # Count edge case patterns
344
347
  local edge_case_count=0
345
- edge_case_count=$(grep -c -E '(empty|null|nil|missing|invalid|error|fail)' "$test_file" || echo "0")
348
+ edge_case_count=$(grep -c -E '(empty|null|nil|missing|invalid|error|fail)' "$test_file" || true)
349
+ edge_case_count="${edge_case_count:-0}"
346
350
 
347
351
  # Count error path tests
348
352
  local error_path_count=0
349
- error_path_count=$(grep -c -E '(exit|return 1|error|ERROR)' "$test_file" || echo "0")
353
+ error_path_count=$(grep -c -E '(exit|return 1|error|ERROR)' "$test_file" || true)
354
+ error_path_count="${error_path_count:-0}"
350
355
 
351
356
  # Calculate score (0-100)
352
357
  local quality_score=0
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
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 tmux fix — Auto-fix common issues ║
12
12
  # ║ shipwright tmux reload — Reload tmux config ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="3.0.0"
14
+ VERSION="3.2.0"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17
 
@@ -227,6 +227,16 @@ tmux_doctor() {
227
227
  check_warn "pane-border-status: ${border_status} — agent names won't show"
228
228
  fi
229
229
 
230
+ # Check pane-active-border-style for cyan accent
231
+ local border_style
232
+ border_style="$(tmux show-option -gv pane-active-border-style 2>/dev/null || echo "")"
233
+ if [[ "$border_style" == *"#00d4ff"* ]]; then
234
+ check_pass "pane-active-border-style: cyan accent applied"
235
+ else
236
+ check_warn "Pane border format missing cyan accent"
237
+ echo -e " ${DIM}Fix: set -g pane-active-border-style 'fg=#00d4ff,bg=#1a1a2e'${RESET}"
238
+ fi
239
+
230
240
  # Check dark theme hooks
231
241
  if tmux show-hooks -g 2>/dev/null | grep -q "after-split-window"; then
232
242
  check_pass "Dark theme hooks active"
@@ -499,6 +509,30 @@ tmux_fix() {
499
509
  fixed=$((fixed + 1))
500
510
  fi
501
511
 
512
+ # Fix 11: default-terminal
513
+ local term
514
+ term="$(tmux show-option -gv default-terminal 2>/dev/null || echo "unknown")"
515
+ if [[ "$term" != *"256color"* ]]; then
516
+ tmux set -g default-terminal "tmux-256color" 2>/dev/null
517
+ success "Fixed: default-terminal → tmux-256color (was ${term})"
518
+ fixed=$((fixed + 1))
519
+ fi
520
+
521
+ # Fix 12: pane-active-border-style (cyan accent)
522
+ local border_style
523
+ border_style="$(tmux show-option -gv pane-active-border-style 2>/dev/null || echo "")"
524
+ if [[ "$border_style" != *"#00d4ff"* ]]; then
525
+ tmux set -g pane-active-border-style "fg=#00d4ff,bg=#1a1a2e" 2>/dev/null
526
+ success "Fixed: pane-active-border-style → cyan accent (#00d4ff)"
527
+ fixed=$((fixed + 1))
528
+ fi
529
+
530
+ # Source overlay for complete styling (pane-border-format, hooks, etc.)
531
+ if [[ -f "$HOME/.tmux/shipwright-overlay.conf" ]]; then
532
+ tmux source-file "$HOME/.tmux/shipwright-overlay.conf" 2>/dev/null && \
533
+ success "Sourced: shipwright-overlay.conf" || true
534
+ fi
535
+
502
536
  echo ""
503
537
  if [[ $fixed -eq 0 ]]; then
504
538
  success "No fixes needed — tmux is already optimized!"
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
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="3.0.0"
9
+ VERSION="3.2.0"
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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -601,7 +601,7 @@ cmd_team() {
601
601
  fi
602
602
  fi
603
603
 
604
- # ── Fallback: hardcoded complexity/risk mapping ──
604
+ # ── Fallback: default complexity/risk mapping ──
605
605
  if [[ -z "$template" ]]; then
606
606
  case "${complexity}-${risk}" in
607
607
  trivial-low|simple-low)
@@ -672,13 +672,13 @@ cmd_batch() {
672
672
  info "Found ${CYAN}${issue_count}${RESET} unlabeled issues"
673
673
 
674
674
  local success_count=0
675
- echo "$issues_json" | jq -r '.[] | .number' | while IFS= read -r number; do
675
+ while IFS= read -r number; do
676
676
  if cmd_label "$number" >/dev/null 2>&1; then
677
677
  success_count=$((success_count + 1))
678
678
  fi
679
- done
679
+ done < <(echo "$issues_json" | jq -r '.[] | .number')
680
680
 
681
- success "Labeled ${CYAN}${issue_count}${RESET} issues"
681
+ success "Labeled ${CYAN}${success_count}${RESET} of ${CYAN}${issue_count}${RESET} issues"
682
682
  emit_event "triage_batch_complete" "issue_count=$issue_count"
683
683
  }
684
684
 
@@ -698,7 +698,7 @@ cmd_report() {
698
698
  complexity_counts='{}'
699
699
  priority_counts='{}'
700
700
 
701
- echo "$issues_json" | jq -c '.[]' | while IFS= read -r issue_json; do
701
+ while IFS= read -r issue_json; do
702
702
  local labels
703
703
  labels=$(echo "$issue_json" | jq -r '.labels[].name' | tr '\n' ',')
704
704
 
@@ -716,7 +716,7 @@ cmd_report() {
716
716
  local priority
717
717
  priority=$(echo "$labels" | grep -oE "priority:[a-z-]+" | cut -d: -f2 | head -1 || echo "unknown")
718
718
  priority_counts=$(echo "$priority_counts" | jq --arg p "$priority" '.[$p] = (.[$p] // 0) + 1')
719
- done
719
+ done < <(echo "$issues_json" | jq -c '.[]')
720
720
 
721
721
  # Output report
722
722
  echo ""
@@ -2,7 +2,7 @@
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
3
  # ║ sw upgrade — Detect and apply updates from the repo ║
4
4
  # ╚═══════════════════════════════════════════════════════════════════════════╝
5
- VERSION="3.0.0"
5
+ VERSION="3.2.0"
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
package/scripts/sw-ux.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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -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="3.0.0"
9
+ VERSION="3.2.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -373,7 +373,8 @@ cmd_status() {
373
373
  echo ""
374
374
  if [[ -f "$WEBHOOK_EVENTS_FILE" ]]; then
375
375
  local event_count
376
- event_count=$(wc -l < "$WEBHOOK_EVENTS_FILE" 2>/dev/null || echo 0)
376
+ event_count=$(wc -l < "$WEBHOOK_EVENTS_FILE" 2>/dev/null || true)
377
+ event_count="${event_count:-0}"
377
378
  info "Recent webhook events (${event_count} total):"
378
379
  tail -5 "$WEBHOOK_EVENTS_FILE" 2>/dev/null | jq -c '{ts, repo, issue, label}' || echo " (no events yet)"
379
380
  else
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="3.0.0"
11
+ VERSION="3.2.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14
14
 
@@ -82,7 +82,8 @@ _get_test_stats() {
82
82
 
83
83
  # Count test-related events
84
84
  local pass_count
85
- pass_count=$(grep -i "test.*passed" "$EVENTS_FILE" 2>/dev/null | wc -l || echo "0")
85
+ pass_count=$(grep -i "test.*passed" "$EVENTS_FILE" 2>/dev/null | wc -l || true)
86
+ pass_count="${pass_count:-0}"
86
87
  pass_count=$(_safe_num "$pass_count")
87
88
 
88
89
  echo "$pass_count"
@@ -109,11 +110,13 @@ _get_health_score() {
109
110
  recent_events=$(tail -n 100 "$EVENTS_FILE" 2>/dev/null || true)
110
111
 
111
112
  local success_count
112
- success_count=$(echo "$recent_events" | grep -i "stage.*completed" | wc -l || echo "0")
113
+ success_count=$(echo "$recent_events" | grep -i "stage.*completed" | wc -l || true)
114
+ success_count="${success_count:-0}"
113
115
  success_count=$(_safe_num "$success_count")
114
116
 
115
117
  local fail_count
116
- fail_count=$(echo "$recent_events" | grep -i "stage.*failed" | wc -l || echo "0")
118
+ fail_count=$(echo "$recent_events" | grep -i "stage.*failed" | wc -l || true)
119
+ fail_count="${fail_count:-0}"
117
120
  fail_count=$(_safe_num "$fail_count")
118
121
 
119
122
  local total=$((success_count + fail_count))
@@ -5,7 +5,7 @@
5
5
  # ║ Each agent gets its own worktree so parallel agents don't clobber ║
6
6
  # ║ each other's files. Worktrees live in .worktrees/ relative to root. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.0.0"
8
+ VERSION="3.2.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -8,12 +8,17 @@
8
8
  # Run after release artifacts are uploaded to GitHub Releases.
9
9
  set -euo pipefail
10
10
 
11
- VERSION="3.0.0"
12
- VERSION_NUM="${VERSION#v}"
11
+ TAG="${1:-}"
12
+ if [[ -z "$TAG" ]]; then
13
+ echo "Usage: scripts/update-homebrew-sha.sh <version-tag>" >&2
14
+ echo " Example: scripts/update-homebrew-sha.sh v3.0.0" >&2
15
+ exit 1
16
+ fi
17
+ VERSION_NUM="${TAG#v}"
13
18
  REPO="${SHIPWRIGHT_GITHUB_REPO:-sethdford/shipwright}"
14
19
  REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
15
20
  FORMULA="${REPO_ROOT}/homebrew/shipwright.rb"
16
- BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}"
21
+ BASE_URL="https://github.com/${REPO}/releases/download/${TAG}"
17
22
 
18
23
  TMPDIR="${TMPDIR:-/tmp}/shipwright-sha-$$"
19
24
  mkdir -p "$TMPDIR"
@@ -47,18 +52,19 @@ for platform in darwin-arm64 darwin-x86_64 linux-x86_64; do
47
52
  done
48
53
 
49
54
  # Update formula: version and SHA256 values
50
- # sed -i '' on macOS, sed -i on Linux
51
- if [[ "$(uname -s)" == "Darwin" ]]; then
52
- sed -i '' "s|version \"[^\"]*\"|version \"${VERSION_NUM}\"|g" "$FORMULA"
53
- sed -i '' "s|sha256 \"PLACEHOLDER_DARWIN_ARM64_SHA256\"|sha256 \"${sha_darwin_arm64}\"|g" "$FORMULA"
54
- sed -i '' "s|sha256 \"PLACEHOLDER_DARWIN_X86_64_SHA256\"|sha256 \"${sha_darwin_x86_64}\"|g" "$FORMULA"
55
- sed -i '' "s|sha256 \"PLACEHOLDER_LINUX_X86_64_SHA256\"|sha256 \"${sha_linux_x86_64}\"|g" "$FORMULA"
56
- else
57
- sed -i "s|version \"[^\"]*\"|version \"${VERSION_NUM}\"|g" "$FORMULA"
58
- sed -i "s|sha256 \"PLACEHOLDER_DARWIN_ARM64_SHA256\"|sha256 \"${sha_darwin_arm64}\"|g" "$FORMULA"
59
- sed -i "s|sha256 \"PLACEHOLDER_DARWIN_X86_64_SHA256\"|sha256 \"${sha_darwin_x86_64}\"|g" "$FORMULA"
60
- sed -i "s|sha256 \"PLACEHOLDER_LINUX_X86_64_SHA256\"|sha256 \"${sha_linux_x86_64}\"|g" "$FORMULA"
61
- fi
55
+ # The formula has 3 sha256 lines in order: darwin-arm64, darwin-x86_64, linux-x86_64
56
+ # We use awk to replace them positionally so re-releases work (not just placeholder matches)
57
+ awk -v ver="$VERSION_NUM" \
58
+ -v sha1="$sha_darwin_arm64" \
59
+ -v sha2="$sha_darwin_x86_64" \
60
+ -v sha3="$sha_linux_x86_64" \
61
+ -v n=0 '
62
+ /^ version "/ { $0 = " version \"" ver "\"" }
63
+ /sha256 "/ { n++; if (n==1) sub(/sha256 "[^"]*"/, "sha256 \"" sha1 "\"");
64
+ if (n==2) sub(/sha256 "[^"]*"/, "sha256 \"" sha2 "\"");
65
+ if (n==3) sub(/sha256 "[^"]*"/, "sha256 \"" sha3 "\"") }
66
+ { print }
67
+ ' "$FORMULA" > "${FORMULA}.tmp" && mv "${FORMULA}.tmp" "$FORMULA"
62
68
 
63
69
  echo ""
64
70
  echo "Updated $FORMULA for v${VERSION_NUM}:"