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
@@ -6,11 +6,11 @@
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
  # ─── Dependency check ─────────────────────────────────────────────────────────
13
- if ! command -v jq &>/dev/null; then
13
+ if ! command -v jq >/dev/null 2>&1; then
14
14
  echo "ERROR: sw-scale.sh requires 'jq'. Install with: brew install jq (macOS) or apt install jq (Linux)" >&2
15
15
  exit 1
16
16
  fi
@@ -39,16 +39,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
39
39
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
40
40
  }
41
41
  fi
42
- CYAN="${CYAN:-\033[38;2;0;212;255m}"
43
- PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
44
- BLUE="${BLUE:-\033[38;2;0;102;255m}"
45
- GREEN="${GREEN:-\033[38;2;74;222;128m}"
46
- YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
47
- RED="${RED:-\033[38;2;248;113;113m}"
48
- DIM="${DIM:-\033[2m}"
49
- BOLD="${BOLD:-\033[1m}"
50
- RESET="${RESET:-\033[0m}"
51
-
52
42
  # ─── Constants ──────────────────────────────────────────────────────────────
53
43
  SCALE_RULES_FILE="${HOME}/.shipwright/scale-rules.json"
54
44
  SCALE_EVENTS_FILE="${HOME}/.shipwright/scale-events.jsonl"
@@ -148,13 +138,19 @@ emit_scale_event() {
148
138
  '{ts: $ts, action: $action, role: $role, reason: $reason, context: $context}')
149
139
 
150
140
  echo "$event" >> "$SCALE_EVENTS_FILE"
151
- type rotate_jsonl &>/dev/null 2>&1 && rotate_jsonl "$SCALE_EVENTS_FILE" 5000
141
+ type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$SCALE_EVENTS_FILE" 5000
152
142
  }
153
143
 
154
144
  # ─── Scale Up: spawn new agent ───────────────────────────────────────────
155
145
  cmd_up() {
156
- local role="${1:-builder}"
157
- shift 2>/dev/null || true
146
+ local count="${1:-1}"
147
+ local role="${2:-builder}"
148
+
149
+ # Parse: "up builder" -> count=1 role=builder; "up 2 tester" -> count=2 role=tester
150
+ if ! [[ "$count" =~ ^[0-9]+$ ]]; then
151
+ role="$count"
152
+ count=1
153
+ fi
158
154
 
159
155
  ensure_dirs
160
156
  init_rules
@@ -178,43 +174,124 @@ cmd_up() {
178
174
  local max_size
179
175
  max_size=$(jq -r '.max_team_size // 8' "$SCALE_RULES_FILE")
180
176
 
181
- info "Scaling up team with ${role} agent"
177
+ info "Scaling up team with ${count} ${role} agent(s)"
182
178
  echo -e " Max team size: ${CYAN}${max_size}${RESET}"
183
179
  echo -e " Role: ${CYAN}${role}${RESET}"
184
180
  echo ""
185
181
 
186
- # TODO: Integrate with tmux/SendMessage to spawn agent
187
- # For now, emit event and log
188
- emit_scale_event "up" "$role" "manual" "$*"
182
+ local repo_root
183
+ repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || repo_root="$(cd "$SCRIPT_DIR/.." 2>/dev/null && pwd)"
184
+
185
+ if ! command -v tmux &>/dev/null; then
186
+ warn "tmux not available - cannot spawn agents"
187
+ echo "Install tmux to enable agent scaling: brew install tmux"
188
+ emit_scale_event "up" "$role" "manual" "tmux_unavailable"
189
+ update_scale_state
190
+ success "Scale-up event recorded (role: ${role})"
191
+ echo ""
192
+ echo -e " ${DIM}Note: Actual agent spawn requires tmux (brew install tmux)${RESET}"
193
+ return 1
194
+ fi
195
+
196
+ for i in $(seq 1 "$count"); do
197
+ local agent_name="sw-agent-${role}-$(date +%s)-${i}"
198
+ local session_name="shipwright-${agent_name}"
199
+
200
+ # Spawn a real agent in a tmux session
201
+ tmux new-session -d -s "$session_name" \
202
+ "cd \"${repo_root}\" && SW_AGENT_ROLE=$role SW_AGENT_NAME=$agent_name bash scripts/sw-daemon.sh start --role $role 2>&1 | tee /tmp/sw-agent-${agent_name}.log" 2>/dev/null && {
203
+ emit_scale_event "up" "$role" "agent_started" "agent=$agent_name"
204
+ echo "Started agent $agent_name in tmux session $session_name"
205
+ } || {
206
+ warn "Failed to spawn agent $agent_name in tmux"
207
+ }
208
+ done
209
+
210
+ emit_scale_event "up" "$role" "manual" "count=$count"
189
211
  update_scale_state
190
212
 
191
- success "Scale-up event recorded (role: ${role})"
213
+ success "Scale-up event recorded (role: ${role}, count: ${count})"
192
214
  echo ""
193
- echo -e " ${DIM}Note: Actual agent spawn requires tmux/claude integration${RESET}"
194
215
  }
195
216
 
196
217
  # ─── Scale Down: send shutdown to agent ──────────────────────────────────
197
218
  cmd_down() {
198
- local agent_id="${1:-}"
199
- shift 2>/dev/null || true
219
+ local first_arg="${1:-}"
220
+ local second_arg="${2:-}"
200
221
 
201
- if [[ -z "$agent_id" ]]; then
202
- error "Usage: shipwright scale down <agent-id>"
222
+ # Require at least one argument for backward compat (down <agent-id> or down <count> [role])
223
+ if [[ -z "$first_arg" ]]; then
224
+ error "Usage: shipwright scale down <agent-id|count> [role]"
203
225
  return 1
204
226
  fi
205
227
 
228
+ local count="$first_arg"
229
+ local role="$second_arg"
230
+
231
+ # Backward compat: "down agent-42" -> treat as session/agent identifier
232
+ if [[ -n "$count" ]] && [[ "$count" != *[0-9]* ]] || [[ "$count" == agent-* ]]; then
233
+ # Specific agent/session id
234
+ local session_pattern="*${count}*"
235
+ local sessions
236
+ sessions=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -E "shipwright|swarm" | grep -i "$count" || true)
237
+ if [[ -z "$sessions" ]]; then
238
+ emit_scale_event "down" "unknown" "manual" "agent_id=$count"
239
+ update_scale_state
240
+ success "Scale-down event recorded (agent: ${count})"
241
+ return 0
242
+ fi
243
+ local session
244
+ session=$(echo "$sessions" | head -1)
245
+ tmux kill-session -t "$session" 2>/dev/null && {
246
+ emit_scale_event "down" "unknown" "agent_stopped" "session=$session"
247
+ echo "Stopped agent session: $session"
248
+ }
249
+ emit_scale_event "down" "unknown" "manual" "agent_id=$count"
250
+ update_scale_state
251
+ success "Scale-down event recorded (agent: ${count})"
252
+ return 0
253
+ fi
254
+
255
+ # Numeric count
256
+ if ! [[ "$count" =~ ^[0-9]+$ ]]; then
257
+ count=1
258
+ fi
259
+
206
260
  ensure_dirs
207
261
  init_rules
208
262
 
209
- info "Scaling down agent: ${agent_id}"
210
- echo ""
263
+ # Find running agent sessions
264
+ local sessions
265
+ sessions=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep '^shipwright-sw-agent\|^swarm-' || true)
211
266
 
212
- # TODO: Integrate with SendMessage to shut down agent
213
- emit_scale_event "down" "unknown" "manual" "agent_id=$agent_id"
214
- update_scale_state
267
+ if [[ -z "$sessions" ]]; then
268
+ echo "No running agents to stop"
269
+ emit_scale_event "down" "unknown" "manual" "none_running"
270
+ update_scale_state
271
+ return 0
272
+ fi
215
273
 
216
- success "Scale-down event recorded (agent: ${agent_id})"
217
- echo -e " ${DIM}Note: Agent shutdown requires SendMessage integration${RESET}"
274
+ local stopped=0
275
+ while IFS= read -r session; do
276
+ [[ "$stopped" -ge "$count" ]] && break
277
+ [[ -z "$session" ]] && continue
278
+
279
+ # Filter by role if specified
280
+ if [[ -n "$role" ]] && ! echo "$session" | grep -q "$role"; then
281
+ continue
282
+ fi
283
+
284
+ if tmux kill-session -t "$session" 2>/dev/null; then
285
+ stopped=$((stopped + 1))
286
+ emit_scale_event "down" "unknown" "agent_stopped" "session=$session"
287
+ echo "Stopped agent session: $session"
288
+ fi
289
+ done <<< "$sessions"
290
+
291
+ emit_scale_event "down" "unknown" "manual" "count=$stopped"
292
+ update_scale_state
293
+ echo "Stopped $stopped agent(s)"
294
+ success "Scale-down event recorded"
218
295
  }
219
296
 
220
297
  # ─── Manage scaling rules ────────────────────────────────────────────────
@@ -347,16 +424,72 @@ cmd_recommend() {
347
424
  echo -e " Modules changed: ${CYAN}${module_threshold}${RESET} (add reviewer above this)"
348
425
  echo ""
349
426
 
350
- # TODO: Parse pipeline context to generate actual recommendations
351
- echo -e " ${DIM}Recommendations require active pipeline context (passed via environment)${RESET}"
352
- echo ""
427
+ # Read pipeline context from env or pipeline-state.md
428
+ local actual_iterations="${ACTUAL_ITERATIONS:-0}"
429
+ local test_coverage="${TEST_COVERAGE:-0}"
430
+ local module_count="${MODULE_COUNT:-0}"
431
+
432
+ # Try to extract from pipeline-state.md if env vars not set
433
+ local state_file=".claude/pipeline-state.md"
434
+ if [[ "$actual_iterations" == "0" && -f "$state_file" ]]; then
435
+ actual_iterations=$(grep -oE 'iterations?[: ]+([0-9]+)' "$state_file" 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
436
+ actual_iterations="${actual_iterations:-0}"
437
+ fi
438
+ if [[ "$test_coverage" == "0" && -f "$state_file" ]]; then
439
+ test_coverage=$(grep -oE 'coverage[: ]+([0-9]+)' "$state_file" 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
440
+ test_coverage="${test_coverage:-0}"
441
+ fi
442
+
443
+ # Count changed modules via git diff
444
+ if [[ "$module_count" == "0" ]] && command -v git >/dev/null 2>&1; then
445
+ local base_branch="${BASE_BRANCH:-main}"
446
+ if git rev-parse --verify "$base_branch" >/dev/null 2>&1; then
447
+ module_count=$(git diff --name-only "${base_branch}..HEAD" 2>/dev/null \
448
+ | sed 's|/[^/]*$||' | sort -u | wc -l | xargs || echo "0")
449
+ fi
450
+ module_count="${module_count:-0}"
451
+ fi
452
+
453
+ local has_recommendations=false
454
+
455
+ # Check iterations against threshold
456
+ if [[ "$actual_iterations" -gt 0 && "$actual_iterations" -ge "$iteration_threshold" ]]; then
457
+ echo -e " ${YELLOW}⚠${RESET} Failed ${actual_iterations} iterations (threshold: ${iteration_threshold})"
458
+ echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
459
+ echo ""
460
+ has_recommendations=true
461
+ fi
462
+
463
+ # Check coverage against threshold
464
+ if [[ "$test_coverage" -gt 0 && "$test_coverage" -lt "$coverage_threshold" ]]; then
465
+ echo -e " ${YELLOW}⚠${RESET} Coverage at ${test_coverage}% (threshold: ${coverage_threshold}%)"
466
+ echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
467
+ echo ""
468
+ has_recommendations=true
469
+ fi
470
+
471
+ # Check module count against threshold
472
+ if [[ "$module_count" -gt 0 && "$module_count" -ge "$module_threshold" ]]; then
473
+ echo -e " ${YELLOW}⚠${RESET} ${module_count} modules changed (threshold: ${module_threshold})"
474
+ echo -e " ${CYAN}→ Recommend adding: reviewer${RESET}"
475
+ echo ""
476
+ has_recommendations=true
477
+ fi
478
+
479
+ if [[ "$has_recommendations" == "false" ]]; then
480
+ if [[ "$actual_iterations" == "0" && "$test_coverage" == "0" && "$module_count" == "0" ]]; then
481
+ echo -e " ${DIM}No pipeline context available — run during an active pipeline or set ACTUAL_ITERATIONS, TEST_COVERAGE, MODULE_COUNT${RESET}"
482
+ else
483
+ success "All metrics within thresholds — no scaling changes needed"
484
+ fi
485
+ echo ""
486
+ fi
353
487
 
354
- # Example output when context is available:
355
- # echo -e " ${YELLOW}⚠${RESET} Failed 4 iterations (threshold: 3)"
356
- # echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
357
- # echo ""
358
- # echo -e " ${YELLOW}⚠${RESET} Coverage at 45% (threshold: 60%)"
359
- # echo -e " ${CYAN}→ Recommend adding: tester${RESET}"
488
+ emit_event "scale.recommendation" \
489
+ "iterations=$actual_iterations" \
490
+ "coverage=$test_coverage" \
491
+ "modules=$module_count" \
492
+ "has_recommendations=$has_recommendations"
360
493
  }
361
494
 
362
495
  # ─── Help message ────────────────────────────────────────────────────────
@@ -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,16 +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
-
46
36
  # ─── Audit State ───────────────────────────────────────────────────────────
47
37
  FINDINGS=()
48
38
  CRITICAL_COUNT=0
@@ -60,10 +50,10 @@ add_finding() {
60
50
 
61
51
  local color=""
62
52
  case "$priority" in
63
- CRITICAL) color="$RED"; ((CRITICAL_COUNT++)) ;;
64
- HIGH) color="$RED"; ((HIGH_COUNT++)) ;;
65
- MEDIUM) color="$YELLOW"; ((MEDIUM_COUNT++)) ;;
66
- LOW) color="$BLUE"; ((LOW_COUNT++)) ;;
53
+ CRITICAL) color="$RED"; CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) ;;
54
+ HIGH) color="$RED"; HIGH_COUNT=$((HIGH_COUNT + 1)) ;;
55
+ MEDIUM) color="$YELLOW"; MEDIUM_COUNT=$((MEDIUM_COUNT + 1)) ;;
56
+ LOW) color="$BLUE"; LOW_COUNT=$((LOW_COUNT + 1)) ;;
67
57
  esac
68
58
 
69
59
  FINDINGS+=("${priority}|${category}|${title}|${description}|${remediation}")
@@ -133,7 +123,7 @@ scan_licenses() {
133
123
  [[ -f "$REPO_DIR/Cargo.toml" ]] && has_cargo=true
134
124
 
135
125
  # Check npm licenses
136
- if $has_npm && command -v npm &>/dev/null; then
126
+ if $has_npm && command -v npm >/dev/null 2>&1; then
137
127
  while IFS= read -r line; do
138
128
  [[ "$line" =~ GPL|AGPL ]] && [[ ! "$line" =~ MIT|Apache|BSD ]] && \
139
129
  add_finding "MEDIUM" "licenses" "GPL/AGPL dependency in npm project" \
@@ -173,10 +163,10 @@ scan_vulnerabilities() {
173
163
  local vuln_count=0
174
164
 
175
165
  # Check npm vulnerabilities
176
- if [[ -f "$REPO_DIR/package.json" ]] && command -v npm &>/dev/null; then
166
+ if [[ -f "$REPO_DIR/package.json" ]] && command -v npm >/dev/null 2>&1; then
177
167
  while IFS= read -r line; do
178
168
  [[ -z "$line" ]] && continue
179
- ((vuln_count++))
169
+ vuln_count=$((vuln_count + 1))
180
170
  add_finding "HIGH" "vulnerabilities" "npm security vulnerability" \
181
171
  "Found npm audit issue: $line" \
182
172
  "Run 'npm audit fix' to remediate. Update vulnerable dependencies. Re-test after updates."
@@ -184,11 +174,11 @@ scan_vulnerabilities() {
184
174
  fi
185
175
 
186
176
  # Check pip vulnerabilities
187
- if [[ -f "$REPO_DIR/requirements.txt" ]] && command -v pip &>/dev/null; then
188
- if command -v safety &>/dev/null; then
177
+ if [[ -f "$REPO_DIR/requirements.txt" ]] && command -v pip >/dev/null 2>&1; then
178
+ if command -v safety >/dev/null 2>&1; then
189
179
  while IFS= read -r line; do
190
180
  [[ -z "$line" ]] && continue
191
- ((vuln_count++))
181
+ vuln_count=$((vuln_count + 1))
192
182
  add_finding "HIGH" "vulnerabilities" "Python package vulnerability" \
193
183
  "Found via safety: $line" \
194
184
  "Update vulnerable package. Test compatibility. Run safety check after updates."
@@ -210,7 +200,7 @@ generate_sbom() {
210
200
  local sbom='{"bomFormat":"CycloneDX","specVersion":"1.4","version":1,"components":[]}'
211
201
 
212
202
  # Add npm packages
213
- if [[ -f "$REPO_DIR/package.json" ]] && command -v npm &>/dev/null; then
203
+ if [[ -f "$REPO_DIR/package.json" ]] && command -v npm >/dev/null 2>&1; then
214
204
  local npm_list
215
205
  npm_list=$(npm list --json 2>/dev/null || echo '{"dependencies":{}}')
216
206
  while IFS='=' read -r name version; do