shipwright-cli 2.4.0 → 3.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.
Files changed (169) hide show
  1. package/README.md +16 -11
  2. package/completions/_shipwright +248 -94
  3. package/completions/shipwright.bash +68 -19
  4. package/completions/shipwright.fish +310 -42
  5. package/config/decision-tiers.json +55 -0
  6. package/config/defaults.json +111 -0
  7. package/config/event-schema.json +218 -0
  8. package/config/policy.json +21 -18
  9. package/dashboard/coverage/coverage-summary.json +14 -0
  10. package/dashboard/public/index.html +1 -1
  11. package/dashboard/server.ts +306 -17
  12. package/dashboard/src/components/charts/bar.test.ts +79 -0
  13. package/dashboard/src/components/charts/donut.test.ts +68 -0
  14. package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
  15. package/dashboard/src/components/charts/sparkline.test.ts +125 -0
  16. package/dashboard/src/core/api.test.ts +309 -0
  17. package/dashboard/src/core/helpers.test.ts +301 -0
  18. package/dashboard/src/core/router.test.ts +307 -0
  19. package/dashboard/src/core/router.ts +7 -0
  20. package/dashboard/src/core/sse.test.ts +144 -0
  21. package/dashboard/src/views/metrics.test.ts +186 -0
  22. package/dashboard/src/views/overview.test.ts +173 -0
  23. package/dashboard/src/views/pipelines.test.ts +183 -0
  24. package/dashboard/src/views/team.test.ts +253 -0
  25. package/dashboard/vitest.config.ts +14 -5
  26. package/docs/TIPS.md +1 -1
  27. package/docs/patterns/README.md +1 -1
  28. package/package.json +7 -9
  29. package/scripts/adapters/docker-deploy.sh +1 -1
  30. package/scripts/adapters/tmux-adapter.sh +11 -1
  31. package/scripts/adapters/wezterm-adapter.sh +1 -1
  32. package/scripts/check-version-consistency.sh +1 -1
  33. package/scripts/lib/architecture.sh +127 -0
  34. package/scripts/lib/bootstrap.sh +75 -0
  35. package/scripts/lib/compat.sh +89 -6
  36. package/scripts/lib/config.sh +91 -0
  37. package/scripts/lib/daemon-adaptive.sh +3 -3
  38. package/scripts/lib/daemon-dispatch.sh +63 -17
  39. package/scripts/lib/daemon-failure.sh +0 -0
  40. package/scripts/lib/daemon-health.sh +1 -1
  41. package/scripts/lib/daemon-patrol.sh +64 -17
  42. package/scripts/lib/daemon-poll.sh +54 -25
  43. package/scripts/lib/daemon-state.sh +125 -23
  44. package/scripts/lib/daemon-triage.sh +31 -9
  45. package/scripts/lib/decide-autonomy.sh +295 -0
  46. package/scripts/lib/decide-scoring.sh +228 -0
  47. package/scripts/lib/decide-signals.sh +462 -0
  48. package/scripts/lib/fleet-failover.sh +63 -0
  49. package/scripts/lib/helpers.sh +29 -6
  50. package/scripts/lib/pipeline-detection.sh +2 -2
  51. package/scripts/lib/pipeline-github.sh +9 -9
  52. package/scripts/lib/pipeline-intelligence.sh +105 -38
  53. package/scripts/lib/pipeline-quality-checks.sh +17 -16
  54. package/scripts/lib/pipeline-quality.sh +1 -1
  55. package/scripts/lib/pipeline-stages.sh +440 -59
  56. package/scripts/lib/pipeline-state.sh +54 -4
  57. package/scripts/lib/policy.sh +0 -0
  58. package/scripts/lib/test-helpers.sh +247 -0
  59. package/scripts/postinstall.mjs +78 -12
  60. package/scripts/signals/example-collector.sh +36 -0
  61. package/scripts/sw +17 -7
  62. package/scripts/sw-activity.sh +1 -11
  63. package/scripts/sw-adaptive.sh +109 -85
  64. package/scripts/sw-adversarial.sh +4 -14
  65. package/scripts/sw-architecture-enforcer.sh +1 -11
  66. package/scripts/sw-auth.sh +8 -17
  67. package/scripts/sw-autonomous.sh +111 -49
  68. package/scripts/sw-changelog.sh +1 -11
  69. package/scripts/sw-checkpoint.sh +144 -20
  70. package/scripts/sw-ci.sh +2 -12
  71. package/scripts/sw-cleanup.sh +13 -17
  72. package/scripts/sw-code-review.sh +16 -36
  73. package/scripts/sw-connect.sh +5 -12
  74. package/scripts/sw-context.sh +9 -26
  75. package/scripts/sw-cost.sh +17 -18
  76. package/scripts/sw-daemon.sh +76 -71
  77. package/scripts/sw-dashboard.sh +57 -17
  78. package/scripts/sw-db.sh +524 -26
  79. package/scripts/sw-decide.sh +685 -0
  80. package/scripts/sw-decompose.sh +1 -11
  81. package/scripts/sw-deps.sh +15 -25
  82. package/scripts/sw-developer-simulation.sh +1 -11
  83. package/scripts/sw-discovery.sh +138 -30
  84. package/scripts/sw-doc-fleet.sh +7 -17
  85. package/scripts/sw-docs-agent.sh +6 -16
  86. package/scripts/sw-docs.sh +4 -12
  87. package/scripts/sw-doctor.sh +134 -43
  88. package/scripts/sw-dora.sh +11 -19
  89. package/scripts/sw-durable.sh +35 -52
  90. package/scripts/sw-e2e-orchestrator.sh +11 -27
  91. package/scripts/sw-eventbus.sh +115 -115
  92. package/scripts/sw-evidence.sh +114 -30
  93. package/scripts/sw-feedback.sh +3 -13
  94. package/scripts/sw-fix.sh +2 -20
  95. package/scripts/sw-fleet-discover.sh +1 -11
  96. package/scripts/sw-fleet-viz.sh +10 -18
  97. package/scripts/sw-fleet.sh +13 -17
  98. package/scripts/sw-github-app.sh +6 -16
  99. package/scripts/sw-github-checks.sh +1 -11
  100. package/scripts/sw-github-deploy.sh +1 -11
  101. package/scripts/sw-github-graphql.sh +2 -12
  102. package/scripts/sw-guild.sh +1 -11
  103. package/scripts/sw-heartbeat.sh +49 -12
  104. package/scripts/sw-hygiene.sh +45 -43
  105. package/scripts/sw-incident.sh +48 -74
  106. package/scripts/sw-init.sh +35 -37
  107. package/scripts/sw-instrument.sh +1 -11
  108. package/scripts/sw-intelligence.sh +368 -53
  109. package/scripts/sw-jira.sh +5 -14
  110. package/scripts/sw-launchd.sh +2 -12
  111. package/scripts/sw-linear.sh +8 -17
  112. package/scripts/sw-logs.sh +4 -12
  113. package/scripts/sw-loop.sh +905 -104
  114. package/scripts/sw-memory.sh +263 -20
  115. package/scripts/sw-mission-control.sh +2 -12
  116. package/scripts/sw-model-router.sh +73 -34
  117. package/scripts/sw-otel.sh +15 -23
  118. package/scripts/sw-oversight.sh +1 -11
  119. package/scripts/sw-patrol-meta.sh +5 -11
  120. package/scripts/sw-pipeline-composer.sh +7 -17
  121. package/scripts/sw-pipeline-vitals.sh +1 -11
  122. package/scripts/sw-pipeline.sh +550 -122
  123. package/scripts/sw-pm.sh +2 -12
  124. package/scripts/sw-pr-lifecycle.sh +33 -28
  125. package/scripts/sw-predictive.sh +16 -22
  126. package/scripts/sw-prep.sh +6 -16
  127. package/scripts/sw-ps.sh +1 -11
  128. package/scripts/sw-public-dashboard.sh +2 -12
  129. package/scripts/sw-quality.sh +85 -14
  130. package/scripts/sw-reaper.sh +1 -11
  131. package/scripts/sw-recruit.sh +15 -25
  132. package/scripts/sw-regression.sh +11 -21
  133. package/scripts/sw-release-manager.sh +19 -28
  134. package/scripts/sw-release.sh +8 -16
  135. package/scripts/sw-remote.sh +1 -11
  136. package/scripts/sw-replay.sh +48 -44
  137. package/scripts/sw-retro.sh +70 -92
  138. package/scripts/sw-review-rerun.sh +1 -1
  139. package/scripts/sw-scale.sh +174 -41
  140. package/scripts/sw-security-audit.sh +12 -22
  141. package/scripts/sw-self-optimize.sh +239 -23
  142. package/scripts/sw-session.sh +5 -15
  143. package/scripts/sw-setup.sh +8 -18
  144. package/scripts/sw-standup.sh +5 -15
  145. package/scripts/sw-status.sh +32 -23
  146. package/scripts/sw-strategic.sh +129 -13
  147. package/scripts/sw-stream.sh +1 -11
  148. package/scripts/sw-swarm.sh +76 -36
  149. package/scripts/sw-team-stages.sh +10 -20
  150. package/scripts/sw-templates.sh +4 -14
  151. package/scripts/sw-testgen.sh +3 -13
  152. package/scripts/sw-tmux-pipeline.sh +1 -19
  153. package/scripts/sw-tmux-role-color.sh +0 -10
  154. package/scripts/sw-tmux-status.sh +3 -11
  155. package/scripts/sw-tmux.sh +2 -20
  156. package/scripts/sw-trace.sh +1 -19
  157. package/scripts/sw-tracker-github.sh +0 -10
  158. package/scripts/sw-tracker-jira.sh +1 -11
  159. package/scripts/sw-tracker-linear.sh +1 -11
  160. package/scripts/sw-tracker.sh +7 -24
  161. package/scripts/sw-triage.sh +29 -39
  162. package/scripts/sw-upgrade.sh +5 -23
  163. package/scripts/sw-ux.sh +1 -19
  164. package/scripts/sw-webhook.sh +18 -32
  165. package/scripts/sw-widgets.sh +3 -21
  166. package/scripts/sw-worktree.sh +11 -27
  167. package/scripts/update-homebrew-sha.sh +73 -0
  168. package/templates/pipelines/tdd.json +72 -0
  169. package/scripts/sw-pipeline.sh.mock +0 -7
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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -32,16 +32,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
32
32
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
33
33
  }
34
34
  fi
35
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
36
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
37
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
38
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
39
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
40
- RED="${RED:-\033[38;2;248;113;113m}"
41
- DIM="${DIM:-\033[2m}"
42
- BOLD="${BOLD:-\033[1m}"
43
- RESET="${RESET:-\033[0m}"
44
-
45
35
  # ─── PM History Storage ──────────────────────────────────────────────────────
46
36
  PM_HISTORY="${HOME}/.shipwright/pm-history.json"
47
37
 
@@ -224,7 +214,7 @@ recommend_team() {
224
214
  if [[ -n "$issue_title" ]]; then
225
215
  local recruit_result
226
216
  recruit_result=$(bash "$SCRIPT_DIR/sw-recruit.sh" team --json "$issue_title" 2>/dev/null) || true
227
- if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team' &>/dev/null 2>&1; then
217
+ if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team' >/dev/null 2>&1; then
228
218
  local recruit_roles recruit_model recruit_agents recruit_cost
229
219
  recruit_roles=$(echo "$recruit_result" | jq -r '.team | join(",")')
230
220
  recruit_model=$(echo "$recruit_result" | jq -r '.model // "sonnet"')
@@ -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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -26,24 +26,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
26
  now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
27
27
  now_epoch() { date +%s; }
28
28
  fi
29
- if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
- emit_event() {
31
- local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
32
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
- while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
- echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
- }
36
- fi
37
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
- RED="${RED:-\033[38;2;248;113;113m}"
43
- DIM="${DIM:-\033[2m}"
44
- BOLD="${BOLD:-\033[1m}"
45
- RESET="${RESET:-\033[0m}"
46
-
47
29
  # ─── Configuration Helpers ──────────────────────────────────────────────────
48
30
 
49
31
  get_pr_config() {
@@ -67,7 +49,27 @@ get_pr_head_sha() {
67
49
  get_pr_checks_status() {
68
50
  local pr_number="$1"
69
51
  # Returns: success, failure, pending, or unknown
70
- gh pr checks "$pr_number" 2>/dev/null | jq -r '.[] | select(.status == "completed") | .conclusion' | sort | uniq -c | sort -rn | head -1 || echo "unknown"
52
+ # gh pr checks requires --json flag to produce JSON output
53
+ local checks_json
54
+ checks_json=$(gh pr checks "$pr_number" --json name,state,conclusion 2>/dev/null || echo "[]")
55
+
56
+ # Handle empty or non-JSON response
57
+ if [[ -z "$checks_json" ]] || ! echo "$checks_json" | jq empty 2>/dev/null; then
58
+ echo "unknown"
59
+ return
60
+ fi
61
+
62
+ local total failed pending
63
+ total=$(echo "$checks_json" | jq 'length' 2>/dev/null || echo "0")
64
+ [[ "$total" -eq 0 ]] && { echo "unknown"; return; }
65
+
66
+ failed=$(echo "$checks_json" | jq '[.[] | select(.conclusion == "FAILURE" or .conclusion == "failure")] | length' 2>/dev/null || echo "0")
67
+ [[ "$failed" -gt 0 ]] && { echo "failure"; return; }
68
+
69
+ pending=$(echo "$checks_json" | jq '[.[] | select(.state == "PENDING" or .state == "QUEUED" or .state == "IN_PROGRESS")] | length' 2>/dev/null || echo "0")
70
+ [[ "$pending" -gt 0 ]] && { echo "pending"; return; }
71
+
72
+ echo "success"
71
73
  }
72
74
 
73
75
  has_merge_conflicts() {
@@ -272,13 +274,16 @@ pr_review() {
272
274
  # Evaluate quality criteria
273
275
  local issues_found=0
274
276
  local file_count
275
- file_count=$(echo "$diff_output" | grep -c '^diff --git' || echo "0")
277
+ file_count=$(echo "$diff_output" | grep -c '^diff --git' || true)
278
+ file_count="${file_count:-0}"
276
279
 
277
280
  local line_additions
278
- line_additions=$(echo "$diff_output" | grep -c '^+' || echo "0")
281
+ line_additions=$(echo "$diff_output" | grep -c '^+' || true)
282
+ line_additions="${line_additions:-0}"
279
283
 
280
284
  local line_deletions
281
- line_deletions=$(echo "$diff_output" | grep -c '^-' || echo "0")
285
+ line_deletions=$(echo "$diff_output" | grep -c '^-' || true)
286
+ line_deletions="${line_deletions:-0}"
282
287
 
283
288
  info "Diff analysis: ${file_count} files, +${line_additions}/-${line_deletions} lines"
284
289
 
@@ -287,25 +292,25 @@ pr_review() {
287
292
  if echo "$diff_output" | grep -qE '(HACK|TODO|FIXME|XXX|BROKEN|DEBUG)'; then
288
293
  warnings="${warnings}
289
294
  - Found HACK/TODO/FIXME markers in code"
290
- ((issues_found++))
295
+ issues_found=$((issues_found + 1))
291
296
  fi
292
297
 
293
298
  if echo "$diff_output" | grep -qE 'console\.(log|warn|error)\('; then
294
299
  warnings="${warnings}
295
300
  - Found console.log statements (should use proper logging)"
296
- ((issues_found++))
301
+ issues_found=$((issues_found + 1))
297
302
  fi
298
303
 
299
304
  if [[ $line_additions -gt 500 ]]; then
300
305
  warnings="${warnings}
301
306
  - Large addition (${line_additions} lines) — consider splitting into smaller PRs"
302
- ((issues_found++))
307
+ issues_found=$((issues_found + 1))
303
308
  fi
304
309
 
305
310
  if [[ $file_count -gt 20 ]]; then
306
311
  warnings="${warnings}
307
312
  - Many files changed (${file_count}) — consider splitting"
308
- ((issues_found++))
313
+ issues_found=$((issues_found + 1))
309
314
  fi
310
315
 
311
316
  # Post review comment to PR
@@ -487,7 +492,7 @@ ${DIM}— Shipwright auto-lifecycle manager${RESET}"
487
492
  gh pr comment "$pr_number" --body "$close_comment" 2>/dev/null || true
488
493
  gh pr close "$pr_number" 2>/dev/null && {
489
494
  success "Closed PR #${pr_number}"
490
- ((closed_count++))
495
+ closed_count=$((closed_count + 1))
491
496
  emit_event "pr.closed_stale" "pr=${pr_number}" "age_days=${age_days}"
492
497
  }
493
498
  fi
@@ -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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
34
34
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
35
  }
36
36
  fi
37
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
- RED="${RED:-\033[38;2;248;113;113m}"
43
- DIM="${DIM:-\033[2m}"
44
- BOLD="${BOLD:-\033[1m}"
45
- RESET="${RESET:-\033[0m}"
46
-
47
37
  # ─── Structured Event Log ──────────────────────────────────────────────────
48
38
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
49
39
 
@@ -169,7 +159,7 @@ _predictive_record_anomaly() {
169
159
  '{ts: $ts, ts_epoch: $epoch, stage: $stage, metric: $metric, severity: $severity, value: $value, baseline: $baseline, confirmed: null}')
170
160
  echo "$record" >> "$tracking_file"
171
161
  # Rotate anomaly tracking to prevent unbounded growth
172
- type rotate_jsonl &>/dev/null 2>&1 && rotate_jsonl "$tracking_file" 5000
162
+ type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$tracking_file" 5000
173
163
  }
174
164
 
175
165
  # predictive_confirm_anomaly <stage> <metric_name> <was_real_failure>
@@ -308,7 +298,7 @@ _predictive_github_risk_factors() {
308
298
  local issue_json="$1"
309
299
  local risk_factors='{"security_risk": 0, "churn_risk": 0, "contributor_risk": 0, "recurrence_risk": 0}'
310
300
 
311
- type _gh_detect_repo &>/dev/null 2>&1 || { echo "$risk_factors"; return 0; }
301
+ type _gh_detect_repo >/dev/null 2>&1 || { echo "$risk_factors"; return 0; }
312
302
  _gh_detect_repo 2>/dev/null || { echo "$risk_factors"; return 0; }
313
303
 
314
304
  local owner="${GH_OWNER:-}" repo="${GH_REPO:-}"
@@ -316,7 +306,7 @@ _predictive_github_risk_factors() {
316
306
 
317
307
  # Security risk: active alerts
318
308
  local sec_risk=0
319
- if type gh_security_alerts &>/dev/null 2>&1; then
309
+ if type gh_security_alerts >/dev/null 2>&1; then
320
310
  local alert_count
321
311
  alert_count=$(gh_security_alerts "$owner" "$repo" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
322
312
  if [[ "${alert_count:-0}" -gt 10 ]]; then
@@ -330,7 +320,7 @@ _predictive_github_risk_factors() {
330
320
 
331
321
  # Recurrence risk: similar past issues
332
322
  local rec_risk=0
333
- if type gh_similar_issues &>/dev/null 2>&1; then
323
+ if type gh_similar_issues >/dev/null 2>&1; then
334
324
  local title
335
325
  title=$(echo "$issue_json" | jq -r '.title // ""' 2>/dev/null | head -c 100)
336
326
  if [[ -n "$title" ]]; then
@@ -346,7 +336,7 @@ _predictive_github_risk_factors() {
346
336
 
347
337
  # Contributor risk: low contributor count = bus factor risk
348
338
  local cont_risk=0
349
- if type gh_contributors &>/dev/null 2>&1; then
339
+ if type gh_contributors >/dev/null 2>&1; then
350
340
  local contributor_count
351
341
  contributor_count=$(gh_contributors "$owner" "$repo" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
352
342
  if [[ "${contributor_count:-0}" -lt 2 ]]; then
@@ -368,7 +358,7 @@ predict_pipeline_risk() {
368
358
  local issue_json="${1:-"{}"}"
369
359
  local repo_context="${2:-}"
370
360
 
371
- if [[ "$INTELLIGENCE_AVAILABLE" == "true" ]] && command -v _intelligence_call_claude &>/dev/null; then
361
+ if [[ "$INTELLIGENCE_AVAILABLE" == "true" ]] && command -v _intelligence_call_claude >/dev/null 2>&1; then
372
362
  local prompt
373
363
  prompt="Analyze this issue for pipeline risk. Return ONLY valid JSON.
374
364
 
@@ -381,7 +371,7 @@ Return JSON format:
381
371
  local result
382
372
  result=$(_intelligence_call_claude "$prompt" 2>/dev/null || echo "")
383
373
 
384
- if [[ -n "$result" ]] && echo "$result" | jq -e '.overall_risk' &>/dev/null; then
374
+ if [[ -n "$result" ]] && echo "$result" | jq -e '.overall_risk' >/dev/null 2>&1; then
385
375
  # Validate range
386
376
  local risk
387
377
  risk=$(echo "$result" | jq '.overall_risk')
@@ -503,7 +493,7 @@ $(head -100 "$file_path" 2>/dev/null || true)
503
493
  return 0
504
494
  fi
505
495
 
506
- if [[ "$INTELLIGENCE_AVAILABLE" != "true" ]] || ! command -v _intelligence_call_claude &>/dev/null; then
496
+ if [[ "$INTELLIGENCE_AVAILABLE" != "true" ]] || ! command -v _intelligence_call_claude >/dev/null 2>&1; then
507
497
  echo '[]'
508
498
  return 0
509
499
  fi
@@ -524,7 +514,7 @@ Only return findings with severity 'high' or 'critical'. Return [] if nothing si
524
514
  local result
525
515
  result=$(_intelligence_call_claude "$prompt" 2>/dev/null || echo "")
526
516
 
527
- if [[ -n "$result" ]] && echo "$result" | jq -e 'type == "array"' &>/dev/null; then
517
+ if [[ -n "$result" ]] && echo "$result" | jq -e 'type == "array"' >/dev/null 2>&1; then
528
518
  # Filter to only high/critical findings
529
519
  local filtered
530
520
  filtered=$(echo "$result" | jq '[.[] | select(.severity == "high" or .severity == "critical")]')
@@ -588,9 +578,13 @@ predict_detect_anomaly() {
588
578
  return 0
589
579
  fi
590
580
 
591
- # Get per-metric thresholds (adaptive or default)
581
+ # Get per-metric thresholds (adaptive from intelligence/DB, or file-based, or default)
592
582
  local metric_critical_mult metric_warning_mult
593
- metric_critical_mult=$(_predictive_get_anomaly_threshold "$metric_name")
583
+ if [[ "$(type -t get_anomaly_threshold 2>/dev/null)" == "function" ]]; then
584
+ metric_critical_mult=$(get_anomaly_threshold)
585
+ else
586
+ metric_critical_mult=$(_predictive_get_anomaly_threshold "$metric_name")
587
+ fi
594
588
  metric_warning_mult=$(_predictive_get_warning_multiplier "$metric_name")
595
589
 
596
590
  # Calculate thresholds using awk for floating-point
@@ -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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -38,16 +38,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
38
38
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
39
39
  }
40
40
  fi
41
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
42
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
43
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
44
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
45
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
46
- RED="${RED:-\033[38;2;248;113;113m}"
47
- DIM="${DIM:-\033[2m}"
48
- BOLD="${BOLD:-\033[1m}"
49
- RESET="${RESET:-\033[0m}"
50
-
51
41
  # ─── Defaults ───────────────────────────────────────────────────────────────
52
42
  FORCE=false
53
43
  CHECK_ONLY=false
@@ -153,7 +143,7 @@ done
153
143
  # ─── prep_init ──────────────────────────────────────────────────────────────
154
144
 
155
145
  prep_init() {
156
- if ! git rev-parse --is-inside-work-tree &>/dev/null; then
146
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
157
147
  error "Not inside a git repository"
158
148
  exit 1
159
149
  fi
@@ -591,7 +581,7 @@ prep_extract_patterns() {
591
581
  # ─── Intelligence Check ──────────────────────────────────────────────────
592
582
 
593
583
  intelligence_available() {
594
- command -v claude &>/dev/null || return 1
584
+ command -v claude >/dev/null 2>&1 || return 1
595
585
  # Honor --with-claude flag
596
586
  $WITH_CLAUDE && return 0
597
587
  # Check daemon config for intelligence.enabled
@@ -1407,7 +1397,7 @@ prep_generate_manifest() {
1407
1397
  HEREDOC
1408
1398
 
1409
1399
  # Validate JSON
1410
- if command -v jq &>/dev/null; then
1400
+ if command -v jq >/dev/null 2>&1; then
1411
1401
  if ! jq empty "$filepath" 2>/dev/null; then
1412
1402
  warn "prep-manifest.json may have invalid JSON — check manually"
1413
1403
  fi
@@ -1422,7 +1412,7 @@ HEREDOC
1422
1412
  prep_with_claude() {
1423
1413
  if ! $WITH_CLAUDE; then return; fi
1424
1414
 
1425
- if ! command -v claude &>/dev/null; then
1415
+ if ! command -v claude >/dev/null 2>&1; then
1426
1416
  warn "claude CLI not found — skipping deep analysis"
1427
1417
  return
1428
1418
  fi
@@ -1471,7 +1461,7 @@ prep_validate() {
1471
1461
  local issues=0
1472
1462
 
1473
1463
  # Check JSON files
1474
- if command -v jq &>/dev/null; then
1464
+ if command -v jq >/dev/null 2>&1; then
1475
1465
  for f in "$PROJECT_ROOT/.claude/settings.json" "$PROJECT_ROOT/.claude/prep-manifest.json"; do
1476
1466
  if [[ -f "$f" ]] && ! jq empty "$f" 2>/dev/null; then
1477
1467
  warn "Invalid JSON: ${f##"$PROJECT_ROOT"/}"
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.4.0"
8
+ VERSION="3.1.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -31,16 +31,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
31
31
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
32
32
  }
33
33
  fi
34
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
35
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
36
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
37
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
38
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
39
- RED="${RED:-\033[38;2;248;113;113m}"
40
- DIM="${DIM:-\033[2m}"
41
- BOLD="${BOLD:-\033[1m}"
42
- RESET="${RESET:-\033[0m}"
43
-
44
34
  # ─── Format idle time ───────────────────────────────────────────────────────
45
35
  format_idle() {
46
36
  local seconds="$1"
@@ -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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
34
34
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
35
  }
36
36
  fi
37
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
38
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
39
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
40
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
41
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
42
- RED="${RED:-\033[38;2;248;113;113m}"
43
- DIM="${DIM:-\033[2m}"
44
- BOLD="${BOLD:-\033[1m}"
45
- RESET="${RESET:-\033[0m}"
46
-
47
37
  # ─── Paths ──────────────────────────────────────────────────────────────────
48
38
  PUB_DASH_DIR="${HOME}/.shipwright/public-dashboard"
49
39
  SHARE_LINKS_FILE="${PUB_DASH_DIR}/share-links.json"
@@ -133,7 +123,7 @@ gather_pipeline_state() {
133
123
  # ─── Generate Token ─────────────────────────────────────────────────────────
134
124
  generate_token() {
135
125
  # Create a read-only token (32 hex chars)
136
- if command -v openssl &>/dev/null; then
126
+ if command -v openssl >/dev/null 2>&1; then
137
127
  openssl rand -hex 16
138
128
  else
139
129
  # Fallback to simple pseudo-random
@@ -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.4.0"
9
+ VERSION="3.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -33,15 +33,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
33
33
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
34
34
  }
35
35
  fi
36
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
37
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
38
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
39
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
40
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
41
- RED="${RED:-\033[38;2;248;113;113m}"
42
- DIM="${DIM:-\033[2m}"
43
- BOLD="${BOLD:-\033[1m}"
44
- RESET="${RESET:-\033[0m}"
45
36
  # Policy + quality thresholds (lib/pipeline-quality.sh sets QUALITY_* from config/policy.json)
46
37
  [[ -f "$SCRIPT_DIR/lib/pipeline-quality.sh" ]] && source "$SCRIPT_DIR/lib/pipeline-quality.sh"
47
38
 
@@ -102,7 +93,8 @@ validate_quality() {
102
93
  local todos_pass=true
103
94
  if [[ -d "$REPO_DIR/.git" ]]; then
104
95
  local todo_count
105
- todo_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -cE '^\+.*(TODO|FIXME)' || echo "0")
96
+ todo_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -cE '^\+.*(TODO|FIXME)' || true)
97
+ todo_count="${todo_count:-0}"
106
98
  if [[ "$todo_count" -gt 0 ]]; then
107
99
  todos_pass=false
108
100
  all_pass=false
@@ -115,7 +107,8 @@ validate_quality() {
115
107
  local secret_patterns="(password|secret|token|api[_-]?key|aws_access|private_key)"
116
108
  if [[ -d "$REPO_DIR/.git" ]]; then
117
109
  local secret_count
118
- secret_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -ciE "$secret_patterns" || echo "0")
110
+ secret_count=$(cd "$REPO_DIR" && git diff --cached 2>/dev/null | grep -ciE "$secret_patterns" || true)
111
+ secret_count="${secret_count:-0}"
119
112
  if [[ "$secret_count" -gt 3 ]]; then
120
113
  secrets_pass=false
121
114
  all_pass=false
@@ -145,6 +138,62 @@ validate_quality() {
145
138
  emit_event "quality.validate" "pass=$all_pass" "score=$score"
146
139
  }
147
140
 
141
+ # ─── Semantic audit (Claude-powered, supplements grep) ───────────────────────
142
+ quality_semantic_audit() {
143
+ local target="${1:-$REPO_DIR}"
144
+ local audit_type="${2:-general}"
145
+ local results_file="${3:-}"
146
+
147
+ if ! command -v claude &>/dev/null; then
148
+ return 1
149
+ fi
150
+
151
+ local code_sample=""
152
+ local changed_files
153
+ changed_files=$(cd "$REPO_DIR" 2>/dev/null && git diff --name-only HEAD~1 2>/dev/null) || true
154
+ [[ -z "$changed_files" ]] && changed_files=$(cd "$REPO_DIR" 2>/dev/null && git diff --cached --name-only 2>/dev/null) || true
155
+ [[ -z "$changed_files" ]] && changed_files=$(find "$target" -type f \( -name "*.sh" -o -name "*.ts" -o -name "*.js" \) 2>/dev/null | head -20)
156
+
157
+ local file_count=0
158
+ while IFS= read -r file; do
159
+ [[ -z "$file" ]] && continue
160
+ local full_path="$file"
161
+ [[ "$file" != /* ]] && full_path="$REPO_DIR/$file"
162
+ [[ ! -f "$full_path" ]] && continue
163
+ [[ "$file_count" -ge 10 ]] && break
164
+ local content
165
+ content=$(head -200 "$full_path" 2>/dev/null)
166
+ local display_path="$file"
167
+ [[ "$full_path" == "$REPO_DIR"* ]] && display_path="${full_path#$REPO_DIR/}"
168
+ code_sample+="
169
+ --- $display_path ---
170
+ $content
171
+ "
172
+ file_count=$((file_count + 1))
173
+ done <<< "$changed_files"
174
+
175
+ [[ -z "$code_sample" ]] && return 1
176
+
177
+ local prompt="You are a senior code reviewer performing a ${audit_type} audit. Analyze these files for real issues only - no style nits, no false positives. Return JSON array of findings:
178
+ [{\"severity\": \"critical|high|medium|low\", \"file\": \"path\", \"line\": N, \"category\": \"security|correctness|architecture|performance\", \"description\": \"specific issue\", \"suggestion\": \"how to fix\"}]
179
+
180
+ Only report REAL issues. Empty array [] if none found.
181
+
182
+ $code_sample"
183
+
184
+ local result
185
+ result=$(echo "$prompt" | timeout 60 claude -p 2>/dev/null) || return 1
186
+
187
+ local findings
188
+ findings=$(echo "$result" | grep -o '\[.*\]' | head -1) || findings="[]"
189
+
190
+ if [[ -n "$results_file" ]]; then
191
+ echo "$findings" > "$results_file"
192
+ else
193
+ echo "$findings"
194
+ fi
195
+ }
196
+
148
197
  # ─── Audit subcommand ───────────────────────────────────────────────────────
149
198
  audit_quality() {
150
199
  info "Running adversarial quality audits..."
@@ -206,6 +255,26 @@ audit_quality() {
206
255
  fi
207
256
  fi
208
257
 
258
+ # Run semantic audit when Claude is available (supplements grep)
259
+ local semantic_results
260
+ if semantic_results=$(quality_semantic_audit "$REPO_DIR" "general" 2>/dev/null); then
261
+ local semantic_count
262
+ semantic_count=$(echo "$semantic_results" | jq 'length' 2>/dev/null || echo "0")
263
+ if [[ -n "$semantic_count" && "$semantic_count" -gt 0 ]]; then
264
+ info " Semantic audit found $semantic_count additional issue(s)"
265
+ while IFS=$'\t' read -r cat line; do
266
+ [[ -z "$line" ]] && continue
267
+ case "${cat:-correctness}" in
268
+ security) security_findings+=( "$line" ) ;;
269
+ correctness) correctness_findings+=( "$line" ) ;;
270
+ architecture) architecture_findings+=( "$line" ) ;;
271
+ performance) correctness_findings+=( "$line" ) ;;
272
+ *) correctness_findings+=( "$line" ) ;;
273
+ esac
274
+ done < <(echo "$semantic_results" | jq -r '.[] | "\(.category)\t[\(.severity)] \(.file):\(.line // "?") - \(.description)"' 2>/dev/null)
275
+ fi
276
+ fi
277
+
209
278
  # Compile audit results
210
279
  local security_score=100
211
280
  [[ ${#security_findings[@]} -gt 0 ]] && security_score=$((100 - ${#security_findings[@]} * 25))
@@ -255,7 +324,8 @@ completion_detection() {
255
324
  # Check diminishing returns: < 10 lines changed in last 3 iterations
256
325
  local recent_changes=0
257
326
  if [[ -f "$ARTIFACTS_DIR/progress.md" ]]; then
258
- recent_changes=$(grep -c "^### Iteration" "$ARTIFACTS_DIR/progress.md" || echo "0")
327
+ recent_changes=$(grep -c "^### Iteration" "$ARTIFACTS_DIR/progress.md" || true)
328
+ recent_changes="${recent_changes:-0}"
259
329
  fi
260
330
 
261
331
  # Check if tests went from failing to passing
@@ -272,7 +342,8 @@ completion_detection() {
272
342
  local subtasks_done=true
273
343
  if [[ -f ".claude/goal.md" ]]; then
274
344
  local unchecked_count
275
- unchecked_count=$(grep -c "^- \[ \]" ".claude/goal.md" 2>/dev/null || echo "0")
345
+ unchecked_count=$(grep -c "^- \[ \]" ".claude/goal.md" 2>/dev/null || true)
346
+ unchecked_count="${unchecked_count:-0}"
276
347
  if [[ "$unchecked_count" -gt 0 ]]; then
277
348
  subtasks_done=false
278
349
  fi
@@ -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.4.0"
14
+ VERSION="3.1.0"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17
 
@@ -37,16 +37,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
37
37
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
38
38
  }
39
39
  fi
40
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
41
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
42
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
43
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
44
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
45
- RED="${RED:-\033[38;2;248;113;113m}"
46
- DIM="${DIM:-\033[2m}"
47
- BOLD="${BOLD:-\033[1m}"
48
- RESET="${RESET:-\033[0m}"
49
-
50
40
  # ─── Defaults ──────────────────────────────────────────────────────────────
51
41
  WATCH=false
52
42
  DRY_RUN=false