shipwright-cli 2.4.0 → 3.0.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 (161) hide show
  1. package/README.md +16 -11
  2. package/completions/_shipwright +1 -1
  3. package/completions/shipwright.bash +3 -8
  4. package/completions/shipwright.fish +1 -1
  5. package/config/defaults.json +111 -0
  6. package/config/event-schema.json +81 -0
  7. package/config/policy.json +13 -18
  8. package/dashboard/coverage/coverage-summary.json +14 -0
  9. package/dashboard/public/index.html +1 -1
  10. package/dashboard/server.ts +306 -17
  11. package/dashboard/src/components/charts/bar.test.ts +79 -0
  12. package/dashboard/src/components/charts/donut.test.ts +68 -0
  13. package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
  14. package/dashboard/src/components/charts/sparkline.test.ts +125 -0
  15. package/dashboard/src/core/api.test.ts +309 -0
  16. package/dashboard/src/core/helpers.test.ts +301 -0
  17. package/dashboard/src/core/router.test.ts +307 -0
  18. package/dashboard/src/core/router.ts +7 -0
  19. package/dashboard/src/core/sse.test.ts +144 -0
  20. package/dashboard/src/views/metrics.test.ts +186 -0
  21. package/dashboard/src/views/overview.test.ts +173 -0
  22. package/dashboard/src/views/pipelines.test.ts +183 -0
  23. package/dashboard/src/views/team.test.ts +253 -0
  24. package/dashboard/vitest.config.ts +14 -5
  25. package/docs/TIPS.md +1 -1
  26. package/docs/patterns/README.md +1 -1
  27. package/package.json +5 -7
  28. package/scripts/adapters/docker-deploy.sh +1 -1
  29. package/scripts/adapters/tmux-adapter.sh +11 -1
  30. package/scripts/adapters/wezterm-adapter.sh +1 -1
  31. package/scripts/check-version-consistency.sh +1 -1
  32. package/scripts/lib/architecture.sh +126 -0
  33. package/scripts/lib/bootstrap.sh +75 -0
  34. package/scripts/lib/compat.sh +89 -6
  35. package/scripts/lib/config.sh +91 -0
  36. package/scripts/lib/daemon-adaptive.sh +3 -3
  37. package/scripts/lib/daemon-dispatch.sh +39 -16
  38. package/scripts/lib/daemon-health.sh +1 -1
  39. package/scripts/lib/daemon-patrol.sh +24 -12
  40. package/scripts/lib/daemon-poll.sh +37 -25
  41. package/scripts/lib/daemon-state.sh +115 -23
  42. package/scripts/lib/daemon-triage.sh +30 -8
  43. package/scripts/lib/fleet-failover.sh +63 -0
  44. package/scripts/lib/helpers.sh +30 -6
  45. package/scripts/lib/pipeline-detection.sh +2 -2
  46. package/scripts/lib/pipeline-github.sh +9 -9
  47. package/scripts/lib/pipeline-intelligence.sh +85 -35
  48. package/scripts/lib/pipeline-quality-checks.sh +16 -16
  49. package/scripts/lib/pipeline-quality.sh +1 -1
  50. package/scripts/lib/pipeline-stages.sh +242 -28
  51. package/scripts/lib/pipeline-state.sh +40 -4
  52. package/scripts/lib/test-helpers.sh +247 -0
  53. package/scripts/postinstall.mjs +3 -11
  54. package/scripts/sw +10 -4
  55. package/scripts/sw-activity.sh +1 -11
  56. package/scripts/sw-adaptive.sh +109 -85
  57. package/scripts/sw-adversarial.sh +4 -14
  58. package/scripts/sw-architecture-enforcer.sh +1 -11
  59. package/scripts/sw-auth.sh +8 -17
  60. package/scripts/sw-autonomous.sh +111 -49
  61. package/scripts/sw-changelog.sh +1 -11
  62. package/scripts/sw-checkpoint.sh +144 -20
  63. package/scripts/sw-ci.sh +2 -12
  64. package/scripts/sw-cleanup.sh +13 -17
  65. package/scripts/sw-code-review.sh +16 -36
  66. package/scripts/sw-connect.sh +5 -12
  67. package/scripts/sw-context.sh +9 -26
  68. package/scripts/sw-cost.sh +6 -16
  69. package/scripts/sw-daemon.sh +75 -70
  70. package/scripts/sw-dashboard.sh +57 -17
  71. package/scripts/sw-db.sh +506 -15
  72. package/scripts/sw-decompose.sh +1 -11
  73. package/scripts/sw-deps.sh +15 -25
  74. package/scripts/sw-developer-simulation.sh +1 -11
  75. package/scripts/sw-discovery.sh +112 -30
  76. package/scripts/sw-doc-fleet.sh +7 -17
  77. package/scripts/sw-docs-agent.sh +6 -16
  78. package/scripts/sw-docs.sh +4 -12
  79. package/scripts/sw-doctor.sh +134 -43
  80. package/scripts/sw-dora.sh +11 -19
  81. package/scripts/sw-durable.sh +35 -52
  82. package/scripts/sw-e2e-orchestrator.sh +11 -27
  83. package/scripts/sw-eventbus.sh +115 -115
  84. package/scripts/sw-evidence.sh +114 -30
  85. package/scripts/sw-feedback.sh +3 -13
  86. package/scripts/sw-fix.sh +2 -20
  87. package/scripts/sw-fleet-discover.sh +1 -11
  88. package/scripts/sw-fleet-viz.sh +10 -18
  89. package/scripts/sw-fleet.sh +13 -17
  90. package/scripts/sw-github-app.sh +6 -16
  91. package/scripts/sw-github-checks.sh +1 -11
  92. package/scripts/sw-github-deploy.sh +1 -11
  93. package/scripts/sw-github-graphql.sh +2 -12
  94. package/scripts/sw-guild.sh +1 -11
  95. package/scripts/sw-heartbeat.sh +49 -12
  96. package/scripts/sw-hygiene.sh +45 -43
  97. package/scripts/sw-incident.sh +48 -74
  98. package/scripts/sw-init.sh +35 -37
  99. package/scripts/sw-instrument.sh +1 -11
  100. package/scripts/sw-intelligence.sh +362 -51
  101. package/scripts/sw-jira.sh +5 -14
  102. package/scripts/sw-launchd.sh +2 -12
  103. package/scripts/sw-linear.sh +8 -17
  104. package/scripts/sw-logs.sh +4 -12
  105. package/scripts/sw-loop.sh +641 -90
  106. package/scripts/sw-memory.sh +243 -17
  107. package/scripts/sw-mission-control.sh +2 -12
  108. package/scripts/sw-model-router.sh +73 -34
  109. package/scripts/sw-otel.sh +11 -21
  110. package/scripts/sw-oversight.sh +1 -11
  111. package/scripts/sw-patrol-meta.sh +5 -11
  112. package/scripts/sw-pipeline-composer.sh +7 -17
  113. package/scripts/sw-pipeline-vitals.sh +1 -11
  114. package/scripts/sw-pipeline.sh +478 -122
  115. package/scripts/sw-pm.sh +2 -12
  116. package/scripts/sw-pr-lifecycle.sh +27 -25
  117. package/scripts/sw-predictive.sh +16 -22
  118. package/scripts/sw-prep.sh +6 -16
  119. package/scripts/sw-ps.sh +1 -11
  120. package/scripts/sw-public-dashboard.sh +2 -12
  121. package/scripts/sw-quality.sh +77 -10
  122. package/scripts/sw-reaper.sh +1 -11
  123. package/scripts/sw-recruit.sh +15 -25
  124. package/scripts/sw-regression.sh +11 -21
  125. package/scripts/sw-release-manager.sh +19 -28
  126. package/scripts/sw-release.sh +8 -16
  127. package/scripts/sw-remote.sh +1 -11
  128. package/scripts/sw-replay.sh +48 -44
  129. package/scripts/sw-retro.sh +70 -92
  130. package/scripts/sw-review-rerun.sh +1 -1
  131. package/scripts/sw-scale.sh +109 -32
  132. package/scripts/sw-security-audit.sh +12 -22
  133. package/scripts/sw-self-optimize.sh +239 -23
  134. package/scripts/sw-session.sh +3 -13
  135. package/scripts/sw-setup.sh +8 -18
  136. package/scripts/sw-standup.sh +5 -15
  137. package/scripts/sw-status.sh +32 -23
  138. package/scripts/sw-strategic.sh +129 -13
  139. package/scripts/sw-stream.sh +1 -11
  140. package/scripts/sw-swarm.sh +76 -36
  141. package/scripts/sw-team-stages.sh +10 -20
  142. package/scripts/sw-templates.sh +4 -14
  143. package/scripts/sw-testgen.sh +3 -13
  144. package/scripts/sw-tmux-pipeline.sh +1 -19
  145. package/scripts/sw-tmux-role-color.sh +0 -10
  146. package/scripts/sw-tmux-status.sh +3 -11
  147. package/scripts/sw-tmux.sh +2 -20
  148. package/scripts/sw-trace.sh +1 -19
  149. package/scripts/sw-tracker-github.sh +0 -10
  150. package/scripts/sw-tracker-jira.sh +1 -11
  151. package/scripts/sw-tracker-linear.sh +1 -11
  152. package/scripts/sw-tracker.sh +7 -24
  153. package/scripts/sw-triage.sh +24 -34
  154. package/scripts/sw-upgrade.sh +5 -23
  155. package/scripts/sw-ux.sh +1 -19
  156. package/scripts/sw-webhook.sh +18 -32
  157. package/scripts/sw-widgets.sh +3 -21
  158. package/scripts/sw-worktree.sh +11 -27
  159. package/scripts/update-homebrew-sha.sh +67 -0
  160. package/templates/pipelines/tdd.json +72 -0
  161. 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.0.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.0.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() {
@@ -287,25 +289,25 @@ pr_review() {
287
289
  if echo "$diff_output" | grep -qE '(HACK|TODO|FIXME|XXX|BROKEN|DEBUG)'; then
288
290
  warnings="${warnings}
289
291
  - Found HACK/TODO/FIXME markers in code"
290
- ((issues_found++))
292
+ issues_found=$((issues_found + 1))
291
293
  fi
292
294
 
293
295
  if echo "$diff_output" | grep -qE 'console\.(log|warn|error)\('; then
294
296
  warnings="${warnings}
295
297
  - Found console.log statements (should use proper logging)"
296
- ((issues_found++))
298
+ issues_found=$((issues_found + 1))
297
299
  fi
298
300
 
299
301
  if [[ $line_additions -gt 500 ]]; then
300
302
  warnings="${warnings}
301
303
  - Large addition (${line_additions} lines) — consider splitting into smaller PRs"
302
- ((issues_found++))
304
+ issues_found=$((issues_found + 1))
303
305
  fi
304
306
 
305
307
  if [[ $file_count -gt 20 ]]; then
306
308
  warnings="${warnings}
307
309
  - Many files changed (${file_count}) — consider splitting"
308
- ((issues_found++))
310
+ issues_found=$((issues_found + 1))
309
311
  fi
310
312
 
311
313
  # Post review comment to PR
@@ -487,7 +489,7 @@ ${DIM}— Shipwright auto-lifecycle manager${RESET}"
487
489
  gh pr comment "$pr_number" --body "$close_comment" 2>/dev/null || true
488
490
  gh pr close "$pr_number" 2>/dev/null && {
489
491
  success "Closed PR #${pr_number}"
490
- ((closed_count++))
492
+ closed_count=$((closed_count + 1))
491
493
  emit_event "pr.closed_stale" "pr=${pr_number}" "age_days=${age_days}"
492
494
  }
493
495
  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.0.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.0.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.0.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.0.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.0.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
 
@@ -145,6 +136,62 @@ validate_quality() {
145
136
  emit_event "quality.validate" "pass=$all_pass" "score=$score"
146
137
  }
147
138
 
139
+ # ─── Semantic audit (Claude-powered, supplements grep) ───────────────────────
140
+ quality_semantic_audit() {
141
+ local target="${1:-$REPO_DIR}"
142
+ local audit_type="${2:-general}"
143
+ local results_file="${3:-}"
144
+
145
+ if ! command -v claude &>/dev/null; then
146
+ return 1
147
+ fi
148
+
149
+ local code_sample=""
150
+ local changed_files
151
+ changed_files=$(cd "$REPO_DIR" 2>/dev/null && git diff --name-only HEAD~1 2>/dev/null) || true
152
+ [[ -z "$changed_files" ]] && changed_files=$(cd "$REPO_DIR" 2>/dev/null && git diff --cached --name-only 2>/dev/null) || true
153
+ [[ -z "$changed_files" ]] && changed_files=$(find "$target" -type f \( -name "*.sh" -o -name "*.ts" -o -name "*.js" \) 2>/dev/null | head -20)
154
+
155
+ local file_count=0
156
+ while IFS= read -r file; do
157
+ [[ -z "$file" ]] && continue
158
+ local full_path="$file"
159
+ [[ "$file" != /* ]] && full_path="$REPO_DIR/$file"
160
+ [[ ! -f "$full_path" ]] && continue
161
+ [[ "$file_count" -ge 10 ]] && break
162
+ local content
163
+ content=$(head -200 "$full_path" 2>/dev/null)
164
+ local display_path="$file"
165
+ [[ "$full_path" == "$REPO_DIR"* ]] && display_path="${full_path#$REPO_DIR/}"
166
+ code_sample+="
167
+ --- $display_path ---
168
+ $content
169
+ "
170
+ file_count=$((file_count + 1))
171
+ done <<< "$changed_files"
172
+
173
+ [[ -z "$code_sample" ]] && return 1
174
+
175
+ 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:
176
+ [{\"severity\": \"critical|high|medium|low\", \"file\": \"path\", \"line\": N, \"category\": \"security|correctness|architecture|performance\", \"description\": \"specific issue\", \"suggestion\": \"how to fix\"}]
177
+
178
+ Only report REAL issues. Empty array [] if none found.
179
+
180
+ $code_sample"
181
+
182
+ local result
183
+ result=$(echo "$prompt" | timeout 60 claude -p 2>/dev/null) || return 1
184
+
185
+ local findings
186
+ findings=$(echo "$result" | grep -o '\[.*\]' | head -1) || findings="[]"
187
+
188
+ if [[ -n "$results_file" ]]; then
189
+ echo "$findings" > "$results_file"
190
+ else
191
+ echo "$findings"
192
+ fi
193
+ }
194
+
148
195
  # ─── Audit subcommand ───────────────────────────────────────────────────────
149
196
  audit_quality() {
150
197
  info "Running adversarial quality audits..."
@@ -206,6 +253,26 @@ audit_quality() {
206
253
  fi
207
254
  fi
208
255
 
256
+ # Run semantic audit when Claude is available (supplements grep)
257
+ local semantic_results
258
+ if semantic_results=$(quality_semantic_audit "$REPO_DIR" "general" 2>/dev/null); then
259
+ local semantic_count
260
+ semantic_count=$(echo "$semantic_results" | jq 'length' 2>/dev/null || echo "0")
261
+ if [[ -n "$semantic_count" && "$semantic_count" -gt 0 ]]; then
262
+ info " Semantic audit found $semantic_count additional issue(s)"
263
+ while IFS=$'\t' read -r cat line; do
264
+ [[ -z "$line" ]] && continue
265
+ case "${cat:-correctness}" in
266
+ security) security_findings+=( "$line" ) ;;
267
+ correctness) correctness_findings+=( "$line" ) ;;
268
+ architecture) architecture_findings+=( "$line" ) ;;
269
+ performance) correctness_findings+=( "$line" ) ;;
270
+ *) correctness_findings+=( "$line" ) ;;
271
+ esac
272
+ done < <(echo "$semantic_results" | jq -r '.[] | "\(.category)\t[\(.severity)] \(.file):\(.line // "?") - \(.description)"' 2>/dev/null)
273
+ fi
274
+ fi
275
+
209
276
  # Compile audit results
210
277
  local security_score=100
211
278
  [[ ${#security_findings[@]} -gt 0 ]] && security_score=$((100 - ${#security_findings[@]} * 25))
@@ -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.0.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