shipwright-cli 2.3.1 → 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 (162) hide show
  1. package/README.md +95 -28
  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 +155 -2
  8. package/config/policy.schema.json +162 -1
  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 +15 -5
  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 +126 -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 +39 -16
  39. package/scripts/lib/daemon-health.sh +1 -1
  40. package/scripts/lib/daemon-patrol.sh +24 -12
  41. package/scripts/lib/daemon-poll.sh +37 -25
  42. package/scripts/lib/daemon-state.sh +115 -23
  43. package/scripts/lib/daemon-triage.sh +30 -8
  44. package/scripts/lib/fleet-failover.sh +63 -0
  45. package/scripts/lib/helpers.sh +30 -6
  46. package/scripts/lib/pipeline-detection.sh +2 -2
  47. package/scripts/lib/pipeline-github.sh +9 -9
  48. package/scripts/lib/pipeline-intelligence.sh +85 -35
  49. package/scripts/lib/pipeline-quality-checks.sh +16 -16
  50. package/scripts/lib/pipeline-quality.sh +1 -1
  51. package/scripts/lib/pipeline-stages.sh +242 -28
  52. package/scripts/lib/pipeline-state.sh +40 -4
  53. package/scripts/lib/test-helpers.sh +247 -0
  54. package/scripts/postinstall.mjs +3 -11
  55. package/scripts/sw +10 -4
  56. package/scripts/sw-activity.sh +1 -11
  57. package/scripts/sw-adaptive.sh +109 -85
  58. package/scripts/sw-adversarial.sh +4 -14
  59. package/scripts/sw-architecture-enforcer.sh +1 -11
  60. package/scripts/sw-auth.sh +8 -17
  61. package/scripts/sw-autonomous.sh +111 -49
  62. package/scripts/sw-changelog.sh +1 -11
  63. package/scripts/sw-checkpoint.sh +144 -20
  64. package/scripts/sw-ci.sh +2 -12
  65. package/scripts/sw-cleanup.sh +13 -17
  66. package/scripts/sw-code-review.sh +16 -36
  67. package/scripts/sw-connect.sh +5 -12
  68. package/scripts/sw-context.sh +9 -26
  69. package/scripts/sw-cost.sh +6 -16
  70. package/scripts/sw-daemon.sh +75 -70
  71. package/scripts/sw-dashboard.sh +57 -17
  72. package/scripts/sw-db.sh +506 -15
  73. package/scripts/sw-decompose.sh +1 -11
  74. package/scripts/sw-deps.sh +15 -25
  75. package/scripts/sw-developer-simulation.sh +1 -11
  76. package/scripts/sw-discovery.sh +112 -30
  77. package/scripts/sw-doc-fleet.sh +7 -17
  78. package/scripts/sw-docs-agent.sh +6 -16
  79. package/scripts/sw-docs.sh +4 -12
  80. package/scripts/sw-doctor.sh +134 -43
  81. package/scripts/sw-dora.sh +11 -19
  82. package/scripts/sw-durable.sh +35 -52
  83. package/scripts/sw-e2e-orchestrator.sh +11 -27
  84. package/scripts/sw-eventbus.sh +115 -115
  85. package/scripts/sw-evidence.sh +748 -0
  86. package/scripts/sw-feedback.sh +3 -13
  87. package/scripts/sw-fix.sh +2 -20
  88. package/scripts/sw-fleet-discover.sh +1 -11
  89. package/scripts/sw-fleet-viz.sh +10 -18
  90. package/scripts/sw-fleet.sh +13 -17
  91. package/scripts/sw-github-app.sh +6 -16
  92. package/scripts/sw-github-checks.sh +1 -11
  93. package/scripts/sw-github-deploy.sh +1 -11
  94. package/scripts/sw-github-graphql.sh +2 -12
  95. package/scripts/sw-guild.sh +1 -11
  96. package/scripts/sw-heartbeat.sh +49 -12
  97. package/scripts/sw-hygiene.sh +45 -43
  98. package/scripts/sw-incident.sh +284 -67
  99. package/scripts/sw-init.sh +35 -37
  100. package/scripts/sw-instrument.sh +1 -11
  101. package/scripts/sw-intelligence.sh +362 -51
  102. package/scripts/sw-jira.sh +5 -14
  103. package/scripts/sw-launchd.sh +2 -12
  104. package/scripts/sw-linear.sh +8 -17
  105. package/scripts/sw-logs.sh +4 -12
  106. package/scripts/sw-loop.sh +641 -90
  107. package/scripts/sw-memory.sh +243 -17
  108. package/scripts/sw-mission-control.sh +2 -12
  109. package/scripts/sw-model-router.sh +73 -34
  110. package/scripts/sw-otel.sh +11 -21
  111. package/scripts/sw-oversight.sh +1 -11
  112. package/scripts/sw-patrol-meta.sh +5 -11
  113. package/scripts/sw-pipeline-composer.sh +7 -17
  114. package/scripts/sw-pipeline-vitals.sh +1 -11
  115. package/scripts/sw-pipeline.sh +478 -122
  116. package/scripts/sw-pm.sh +2 -12
  117. package/scripts/sw-pr-lifecycle.sh +203 -29
  118. package/scripts/sw-predictive.sh +16 -22
  119. package/scripts/sw-prep.sh +6 -16
  120. package/scripts/sw-ps.sh +1 -11
  121. package/scripts/sw-public-dashboard.sh +2 -12
  122. package/scripts/sw-quality.sh +77 -10
  123. package/scripts/sw-reaper.sh +1 -11
  124. package/scripts/sw-recruit.sh +15 -25
  125. package/scripts/sw-regression.sh +11 -21
  126. package/scripts/sw-release-manager.sh +19 -28
  127. package/scripts/sw-release.sh +8 -16
  128. package/scripts/sw-remote.sh +1 -11
  129. package/scripts/sw-replay.sh +48 -44
  130. package/scripts/sw-retro.sh +70 -92
  131. package/scripts/sw-review-rerun.sh +220 -0
  132. package/scripts/sw-scale.sh +109 -32
  133. package/scripts/sw-security-audit.sh +12 -22
  134. package/scripts/sw-self-optimize.sh +239 -23
  135. package/scripts/sw-session.sh +3 -13
  136. package/scripts/sw-setup.sh +8 -18
  137. package/scripts/sw-standup.sh +5 -15
  138. package/scripts/sw-status.sh +32 -23
  139. package/scripts/sw-strategic.sh +129 -13
  140. package/scripts/sw-stream.sh +1 -11
  141. package/scripts/sw-swarm.sh +76 -36
  142. package/scripts/sw-team-stages.sh +10 -20
  143. package/scripts/sw-templates.sh +4 -14
  144. package/scripts/sw-testgen.sh +3 -13
  145. package/scripts/sw-tmux-pipeline.sh +1 -19
  146. package/scripts/sw-tmux-role-color.sh +0 -10
  147. package/scripts/sw-tmux-status.sh +3 -11
  148. package/scripts/sw-tmux.sh +2 -20
  149. package/scripts/sw-trace.sh +1 -19
  150. package/scripts/sw-tracker-github.sh +0 -10
  151. package/scripts/sw-tracker-jira.sh +1 -11
  152. package/scripts/sw-tracker-linear.sh +1 -11
  153. package/scripts/sw-tracker.sh +7 -24
  154. package/scripts/sw-triage.sh +24 -34
  155. package/scripts/sw-upgrade.sh +5 -23
  156. package/scripts/sw-ux.sh +1 -19
  157. package/scripts/sw-webhook.sh +18 -32
  158. package/scripts/sw-widgets.sh +3 -21
  159. package/scripts/sw-worktree.sh +11 -27
  160. package/scripts/update-homebrew-sha.sh +67 -0
  161. package/templates/pipelines/tdd.json +72 -0
  162. package/scripts/sw-pipeline.sh.mock +0 -7
@@ -106,8 +106,45 @@ pipeline_should_skip_stage() {
106
106
  # Returns JSON with classified findings and routing recommendations.
107
107
  # ──────────────────────────────────────────────────────────────────────────────
108
108
  classify_quality_findings() {
109
- local findings_dir="$ARTIFACTS_DIR"
110
- local result_file="$ARTIFACTS_DIR/classified-findings.json"
109
+ local findings_dir="${1:-$ARTIFACTS_DIR}"
110
+ local result_file="$findings_dir/classified-findings.json"
111
+
112
+ # Build combined content for semantic classification
113
+ local content=""
114
+ if [[ -f "$findings_dir/adversarial-review.md" ]]; then
115
+ content="${content}
116
+ --- adversarial-review.md ---
117
+ $(head -500 "$findings_dir/adversarial-review.md" 2>/dev/null)"
118
+ fi
119
+ if [[ -f "$findings_dir/negative-review.md" ]]; then
120
+ content="${content}
121
+ --- negative-review.md ---
122
+ $(head -300 "$findings_dir/negative-review.md" 2>/dev/null)"
123
+ fi
124
+ if [[ -f "$findings_dir/security-audit.log" ]]; then
125
+ content="${content}
126
+ --- security-audit.log ---
127
+ $(cat "$findings_dir/security-audit.log" 2>/dev/null)"
128
+ fi
129
+ if [[ -f "$findings_dir/compound-architecture-validation.json" ]]; then
130
+ content="${content}
131
+ --- compound-architecture-validation.json ---
132
+ $(jq -r '.[] | "\(.severity): \(.message // .description // .)"' "$findings_dir/compound-architecture-validation.json" 2>/dev/null | head -50)"
133
+ fi
134
+
135
+ # Try semantic classification first when Claude is available
136
+ local route=""
137
+ if command -v claude &>/dev/null && [[ "${INTELLIGENCE_ENABLED:-false}" != "false" ]] && [[ -n "$content" ]]; then
138
+ local prompt="Classify these code review findings into exactly ONE primary category. Return ONLY a single word: security, architecture, correctness, performance, testing, documentation, style.
139
+
140
+ Findings:
141
+ $content"
142
+ local category
143
+ category=$(echo "$prompt" | timeout 30 claude -p --model sonnet 2>/dev/null | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
144
+ if [[ "$category" =~ ^(security|architecture|correctness|performance|testing|documentation|style)$ ]]; then
145
+ route="$category"
146
+ fi
147
+ fi
111
148
 
112
149
  # Initialize counters
113
150
  local arch_count=0 security_count=0 correctness_count=0 performance_count=0 testing_count=0 style_count=0
@@ -173,40 +210,53 @@ classify_quality_findings() {
173
210
  fi
174
211
 
175
212
  # ── Determine routing ──
176
- # Priority order: security > architecture > correctness > performance > testing > style
177
- local route="correctness" # default
213
+ # Use semantic classification when available; else fall back to grep-derived priority
178
214
  local needs_backtrack=false
179
215
  local priority_findings=""
180
216
 
181
- if [[ "$security_count" -gt 0 ]]; then
182
- route="security"
183
- priority_findings="security:${security_count}"
184
- fi
217
+ if [[ -z "$route" ]]; then
218
+ # Fallback: grep-based priority order: security > architecture > correctness > performance > testing > style
219
+ route="correctness"
185
220
 
186
- if [[ "$arch_count" -gt 0 ]]; then
187
- if [[ "$route" == "correctness" ]]; then
188
- route="architecture"
189
- needs_backtrack=true
221
+ if [[ "$security_count" -gt 0 ]]; then
222
+ route="security"
223
+ priority_findings="security:${security_count}"
190
224
  fi
191
- priority_findings="${priority_findings:+${priority_findings},}architecture:${arch_count}"
192
- fi
193
225
 
194
- if [[ "$correctness_count" -gt 0 ]]; then
195
- priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
196
- fi
226
+ if [[ "$arch_count" -gt 0 ]]; then
227
+ if [[ "$route" == "correctness" ]]; then
228
+ route="architecture"
229
+ needs_backtrack=true
230
+ fi
231
+ priority_findings="${priority_findings:+${priority_findings},}architecture:${arch_count}"
232
+ fi
197
233
 
198
- if [[ "$performance_count" -gt 0 ]]; then
199
- if [[ "$route" == "correctness" && "$correctness_count" -eq 0 ]]; then
200
- route="performance"
234
+ if [[ "$correctness_count" -gt 0 ]]; then
235
+ priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
201
236
  fi
202
- priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
203
- fi
204
237
 
205
- if [[ "$testing_count" -gt 0 ]]; then
206
- if [[ "$route" == "correctness" && "$correctness_count" -eq 0 && "$performance_count" -eq 0 ]]; then
207
- route="testing"
238
+ if [[ "$performance_count" -gt 0 ]]; then
239
+ if [[ "$route" == "correctness" && "$correctness_count" -eq 0 ]]; then
240
+ route="performance"
241
+ fi
242
+ priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
208
243
  fi
209
- priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
244
+
245
+ if [[ "$testing_count" -gt 0 ]]; then
246
+ if [[ "$route" == "correctness" && "$correctness_count" -eq 0 && "$performance_count" -eq 0 ]]; then
247
+ route="testing"
248
+ fi
249
+ priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
250
+ fi
251
+ else
252
+ # Semantic route: build priority_findings from counts, set needs_backtrack for architecture
253
+ [[ "$route" == "architecture" ]] && needs_backtrack=true
254
+ [[ "$arch_count" -gt 0 ]] && priority_findings="architecture:${arch_count}"
255
+ [[ "$security_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}security:${security_count}"
256
+ [[ "$correctness_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}correctness:${correctness_count}"
257
+ [[ "$performance_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}performance:${performance_count}"
258
+ [[ "$testing_count" -gt 0 ]] && priority_findings="${priority_findings:+${priority_findings},}testing:${testing_count}"
259
+ [[ -z "$priority_findings" ]] && priority_findings="${route}:1"
210
260
  fi
211
261
 
212
262
  # Style findings don't affect routing or count toward failure threshold
@@ -749,7 +799,7 @@ pipeline_record_quality_score() {
749
799
  rm -f "$tmp_score"
750
800
 
751
801
  # Rotate quality scores file to prevent unbounded growth
752
- type rotate_jsonl &>/dev/null 2>&1 && rotate_jsonl "$scores_file" 5000
802
+ type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$scores_file" 5000
753
803
 
754
804
  emit_event "pipeline.quality_score_recorded" \
755
805
  "issue=${ISSUE_NUMBER:-0}" \
@@ -1117,7 +1167,7 @@ stage_compound_quality() {
1117
1167
 
1118
1168
  # Intelligent audit selection
1119
1169
  local audit_plan='{"adversarial":"targeted","architecture":"targeted","simulation":"targeted","security":"targeted","dod":"targeted"}'
1120
- if type pipeline_select_audits &>/dev/null 2>&1; then
1170
+ if type pipeline_select_audits >/dev/null 2>&1; then
1121
1171
  local _selected
1122
1172
  _selected=$(pipeline_select_audits 2>/dev/null) || true
1123
1173
  if [[ -n "$_selected" && "$_selected" != "null" ]]; then
@@ -1205,9 +1255,9 @@ stage_compound_quality() {
1205
1255
 
1206
1256
  # Vitals-driven adaptive cycle limit (preferred)
1207
1257
  local base_max_cycles="$max_cycles"
1208
- if type pipeline_adaptive_limit &>/dev/null 2>&1; then
1258
+ if type pipeline_adaptive_limit >/dev/null 2>&1; then
1209
1259
  local _cq_vitals=""
1210
- if type pipeline_compute_vitals &>/dev/null 2>&1; then
1260
+ if type pipeline_compute_vitals >/dev/null 2>&1; then
1211
1261
  _cq_vitals=$(pipeline_compute_vitals "$STATE_FILE" "$ARTIFACTS_DIR" "${ISSUE_NUMBER:-}" 2>/dev/null) || true
1212
1262
  fi
1213
1263
  local vitals_cq_limit
@@ -1270,7 +1320,7 @@ stage_compound_quality() {
1270
1320
  fi
1271
1321
 
1272
1322
  # 3. Developer Simulation (intelligence module)
1273
- if type simulation_review &>/dev/null 2>&1; then
1323
+ if type simulation_review >/dev/null 2>&1; then
1274
1324
  local sim_enabled
1275
1325
  sim_enabled=$(jq -r '.intelligence.simulation_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
1276
1326
  local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
@@ -1310,7 +1360,7 @@ stage_compound_quality() {
1310
1360
  fi
1311
1361
 
1312
1362
  # 4. Architecture Enforcer (intelligence module)
1313
- if type architecture_validate_changes &>/dev/null 2>&1; then
1363
+ if type architecture_validate_changes >/dev/null 2>&1; then
1314
1364
  local arch_enabled
1315
1365
  arch_enabled=$(jq -r '.intelligence.architecture_enabled // false' "$PIPELINE_CONFIG" 2>/dev/null || echo "false")
1316
1366
  local daemon_cfg="${PROJECT_ROOT}/.claude/daemon-config.json"
@@ -1474,7 +1524,7 @@ All quality checks clean:
1474
1524
 
1475
1525
  # DoD verification on successful pass
1476
1526
  local _dod_pass_rate=100
1477
- if type pipeline_verify_dod &>/dev/null 2>&1; then
1527
+ if type pipeline_verify_dod >/dev/null 2>&1; then
1478
1528
  pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
1479
1529
  if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
1480
1530
  _dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
@@ -1496,7 +1546,7 @@ All quality checks clean:
1496
1546
 
1497
1547
  # DoD verification on successful pass
1498
1548
  local _dod_pass_rate=100
1499
- if type pipeline_verify_dod &>/dev/null 2>&1; then
1549
+ if type pipeline_verify_dod >/dev/null 2>&1; then
1500
1550
  pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
1501
1551
  if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
1502
1552
  _dod_pass_rate=$(jq -r '.pass_rate // 100' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "100")
@@ -1590,7 +1640,7 @@ All quality checks clean:
1590
1640
 
1591
1641
  # DoD verification
1592
1642
  local _dod_pass_rate=0
1593
- if type pipeline_verify_dod &>/dev/null 2>&1; then
1643
+ if type pipeline_verify_dod >/dev/null 2>&1; then
1594
1644
  pipeline_verify_dod "$ARTIFACTS_DIR" 2>/dev/null || true
1595
1645
  if [[ -f "$ARTIFACTS_DIR/dod-verification.json" ]]; then
1596
1646
  _dod_pass_rate=$(jq -r '.pass_rate // 0' "$ARTIFACTS_DIR/dod-verification.json" 2>/dev/null || echo "0")
@@ -10,15 +10,15 @@ quality_check_security() {
10
10
  local tool_found=false
11
11
 
12
12
  # Try npm audit
13
- if [[ -f "package.json" ]] && command -v npm &>/dev/null; then
13
+ if [[ -f "package.json" ]] && command -v npm >/dev/null 2>&1; then
14
14
  tool_found=true
15
15
  npm audit --production 2>&1 | tee "$audit_log" || audit_exit=$?
16
16
  # Try pip-audit
17
- elif [[ -f "requirements.txt" || -f "pyproject.toml" ]] && command -v pip-audit &>/dev/null; then
17
+ elif [[ -f "requirements.txt" || -f "pyproject.toml" ]] && command -v pip-audit >/dev/null 2>&1; then
18
18
  tool_found=true
19
19
  pip-audit 2>&1 | tee "$audit_log" || audit_exit=$?
20
20
  # Try cargo audit
21
- elif [[ -f "Cargo.toml" ]] && command -v cargo-audit &>/dev/null; then
21
+ elif [[ -f "Cargo.toml" ]] && command -v cargo-audit >/dev/null 2>&1; then
22
22
  tool_found=true
23
23
  cargo audit 2>&1 | tee "$audit_log" || audit_exit=$?
24
24
  fi
@@ -170,7 +170,7 @@ quality_check_bundle_size() {
170
170
  mv "$tmp_bundle_hist" "$bundle_history_file" 2>/dev/null || true
171
171
 
172
172
  # Intelligence: identify top dependency bloaters
173
- if type intelligence_search_memory &>/dev/null 2>&1 && [[ -f "package.json" ]] && command -v jq &>/dev/null; then
173
+ if type intelligence_search_memory >/dev/null 2>&1 && [[ -f "package.json" ]] && command -v jq >/dev/null 2>&1; then
174
174
  local dep_sizes=""
175
175
  local deps
176
176
  deps=$(jq -r '.dependencies // {} | keys[]' package.json 2>/dev/null || true)
@@ -237,7 +237,7 @@ quality_check_perf_regression() {
237
237
  if [[ -f "$daemon_cfg" ]]; then
238
238
  intel_enabled=$(jq -r '.intelligence.enabled // false' "$daemon_cfg" 2>/dev/null || echo "false")
239
239
  fi
240
- if [[ "$intel_enabled" == "true" ]] && command -v claude &>/dev/null; then
240
+ if [[ "$intel_enabled" == "true" ]] && command -v claude >/dev/null 2>&1; then
241
241
  local tail_output
242
242
  tail_output=$(tail -30 "$test_log" 2>/dev/null || true)
243
243
  if [[ -n "$tail_output" ]]; then
@@ -378,7 +378,7 @@ quality_check_api_compat() {
378
378
 
379
379
  # Check for breaking changes: removed endpoints, changed methods
380
380
  local removed_endpoints=""
381
- if command -v jq &>/dev/null && [[ "$spec_file" == *.json ]]; then
381
+ if command -v jq >/dev/null 2>&1 && [[ "$spec_file" == *.json ]]; then
382
382
  local old_paths new_paths
383
383
  old_paths=$(echo "$old_spec" | jq -r '.paths | keys[]' 2>/dev/null | sort || true)
384
384
  new_paths=$(jq -r '.paths | keys[]' "$spec_file" 2>/dev/null | sort || true)
@@ -387,7 +387,7 @@ quality_check_api_compat() {
387
387
 
388
388
  # Enhanced schema diff: parameter changes, response schema, auth changes
389
389
  local param_changes="" schema_changes=""
390
- if command -v jq &>/dev/null && [[ "$spec_file" == *.json ]]; then
390
+ if command -v jq >/dev/null 2>&1 && [[ "$spec_file" == *.json ]]; then
391
391
  # Detect parameter changes on existing endpoints
392
392
  local common_paths
393
393
  common_paths=$(comm -12 <(echo "$old_spec" | jq -r '.paths | keys[]' 2>/dev/null | sort) <(jq -r '.paths | keys[]' "$spec_file" 2>/dev/null | sort) 2>/dev/null || true)
@@ -407,7 +407,7 @@ quality_check_api_compat() {
407
407
 
408
408
  # Intelligence: semantic API diff for complex changes
409
409
  local semantic_diff=""
410
- if type intelligence_search_memory &>/dev/null 2>&1 && command -v claude &>/dev/null; then
410
+ if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
411
411
  local spec_git_diff
412
412
  spec_git_diff=$(git diff "${BASE_BRANCH}...HEAD" -- "$spec_file" 2>/dev/null | head -200 || true)
413
413
  if [[ -n "$spec_git_diff" ]]; then
@@ -441,7 +441,7 @@ ${spec_git_diff}" --model haiku < /dev/null 2>/dev/null || true)
441
441
  if [[ -n "$removed_endpoints" || -n "$param_changes" ]]; then
442
442
  local issue_count=0
443
443
  [[ -n "$removed_endpoints" ]] && issue_count=$((issue_count + $(echo "$removed_endpoints" | wc -l | xargs)))
444
- [[ -n "$param_changes" ]] && issue_count=$((issue_count + $(echo "$param_changes" | grep -c '.' || true)))
444
+ [[ -n "$param_changes" ]] && issue_count=$((issue_count + $(echo "$param_changes" | grep -c '.' 2>/dev/null || echo "0")))
445
445
  warn "API breaking changes: ${issue_count} issue(s) found"
446
446
  return 1
447
447
  fi
@@ -470,7 +470,7 @@ quality_check_coverage() {
470
470
  if [[ -f "$daemon_cfg_cov" ]]; then
471
471
  intel_enabled_cov=$(jq -r '.intelligence.enabled // false' "$daemon_cfg_cov" 2>/dev/null || echo "false")
472
472
  fi
473
- if [[ "$intel_enabled_cov" == "true" ]] && command -v claude &>/dev/null; then
473
+ if [[ "$intel_enabled_cov" == "true" ]] && command -v claude >/dev/null 2>&1; then
474
474
  local tail_cov_output
475
475
  tail_cov_output=$(tail -40 "$test_log" 2>/dev/null || true)
476
476
  if [[ -n "$tail_cov_output" ]]; then
@@ -559,7 +559,7 @@ run_adversarial_review() {
559
559
  fi
560
560
 
561
561
  # Delegate to sw-adversarial.sh module when available (uses intelligence cache)
562
- if type adversarial_review &>/dev/null 2>&1; then
562
+ if type adversarial_review >/dev/null 2>&1; then
563
563
  info "Using intelligence-backed adversarial review..."
564
564
  local json_result
565
565
  json_result=$(adversarial_review "$diff_content" "${GOAL:-}" 2>/dev/null || echo "[]")
@@ -605,7 +605,7 @@ run_adversarial_review() {
605
605
 
606
606
  # Inject previous adversarial findings from memory
607
607
  local adv_memory=""
608
- if type intelligence_search_memory &>/dev/null 2>&1; then
608
+ if type intelligence_search_memory >/dev/null 2>&1; then
609
609
  adv_memory=$(intelligence_search_memory "adversarial review security findings for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
610
610
  fi
611
611
 
@@ -676,7 +676,7 @@ $(head -200 "$file" 2>/dev/null || true)
676
676
 
677
677
  # Inject previous negative prompting findings from memory
678
678
  local neg_memory=""
679
- if type intelligence_search_memory &>/dev/null 2>&1; then
679
+ if type intelligence_search_memory >/dev/null 2>&1; then
680
680
  neg_memory=$(intelligence_search_memory "negative prompting findings common concerns for: ${GOAL:-}" "${HOME}/.shipwright/memory" 5 2>/dev/null) || true
681
681
  fi
682
682
 
@@ -853,9 +853,9 @@ run_bash_compat_check() {
853
853
  while IFS= read -r filepath; do
854
854
  [[ -z "$filepath" ]] && continue
855
855
 
856
- # declare -A (associative arrays)
856
+ # declare -A (associative arrays; declare -a is bash 3.2 compatible)
857
857
  local declare_a_count
858
- declare_a_count=$(grep -c 'declare[[:space:]]*-[aA]' "$filepath" 2>/dev/null || true)
858
+ declare_a_count=$(grep -c 'declare[[:space:]]*-A' "$filepath" 2>/dev/null || true)
859
859
  if [[ "$declare_a_count" -gt 0 ]]; then
860
860
  violations=$((violations + declare_a_count))
861
861
  violation_details="${violation_details}${filepath}: declare -A (${declare_a_count} occurrences)
@@ -990,7 +990,7 @@ run_atomic_write_check() {
990
990
 
991
991
  # Check for direct redirection writes (> file) in state/config paths
992
992
  local bad_writes
993
- bad_writes=$(git show "HEAD:$filepath" 2>/dev/null | grep -c 'echo.*>' "$filepath" 2>/dev/null || true)
993
+ bad_writes=$(git show "HEAD:$filepath" 2>/dev/null | grep -c 'echo.*>' 2>/dev/null || echo "0")
994
994
 
995
995
  if [[ "$bad_writes" -gt 0 ]]; then
996
996
  violations=$((violations + bad_writes))
@@ -5,7 +5,7 @@ _PIPELINE_QUALITY_LOADED=1
5
5
 
6
6
  # Policy overrides when config/policy.json exists
7
7
  [[ -f "${SCRIPT_DIR:-}/lib/policy.sh" ]] && source "${SCRIPT_DIR:-}/lib/policy.sh"
8
- if type policy_get &>/dev/null 2>&1; then
8
+ if type policy_get >/dev/null 2>&1; then
9
9
  PIPELINE_COVERAGE_THRESHOLD=$(policy_get ".pipeline.coverage_threshold_percent" "60")
10
10
  PIPELINE_QUALITY_GATE_THRESHOLD=$(policy_get ".pipeline.quality_gate_score_threshold" "70")
11
11
  QUALITY_COVERAGE_THRESHOLD=$(policy_get ".quality.coverage_threshold" "70")