shipwright-cli 3.1.0 → 3.3.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 (283) hide show
  1. package/.claude/agents/code-reviewer.md +2 -0
  2. package/.claude/agents/devops-engineer.md +2 -0
  3. package/.claude/agents/doc-fleet-agent.md +2 -0
  4. package/.claude/agents/pipeline-agent.md +2 -0
  5. package/.claude/agents/shell-script-specialist.md +2 -0
  6. package/.claude/agents/test-specialist.md +2 -0
  7. package/.claude/hooks/agent-crash-capture.sh +32 -0
  8. package/.claude/hooks/post-tool-use.sh +3 -2
  9. package/.claude/hooks/pre-tool-use.sh +35 -3
  10. package/README.md +22 -8
  11. package/claude-code/hooks/config-change.sh +18 -0
  12. package/claude-code/hooks/instructions-reloaded.sh +7 -0
  13. package/claude-code/hooks/worktree-create.sh +25 -0
  14. package/claude-code/hooks/worktree-remove.sh +20 -0
  15. package/config/code-constitution.json +130 -0
  16. package/config/defaults.json +25 -2
  17. package/config/policy.json +1 -1
  18. package/dashboard/middleware/auth.ts +134 -0
  19. package/dashboard/middleware/constants.ts +21 -0
  20. package/dashboard/public/index.html +8 -6
  21. package/dashboard/public/styles.css +176 -97
  22. package/dashboard/routes/auth.ts +38 -0
  23. package/dashboard/server.ts +117 -25
  24. package/dashboard/services/config.ts +26 -0
  25. package/dashboard/services/db.ts +118 -0
  26. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  27. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  28. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  29. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  30. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  31. package/dashboard/src/components/header.ts +8 -7
  32. package/dashboard/src/core/api.ts +5 -0
  33. package/dashboard/src/core/router.ts +1 -0
  34. package/dashboard/src/design/submarine-theme.ts +253 -0
  35. package/dashboard/src/main.ts +2 -0
  36. package/dashboard/src/types/api.ts +12 -1
  37. package/dashboard/src/views/activity.ts +2 -1
  38. package/dashboard/src/views/metrics.ts +69 -1
  39. package/dashboard/src/views/shipyard.ts +39 -0
  40. package/dashboard/types/index.ts +166 -0
  41. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  42. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  43. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  44. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  45. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  46. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  47. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  48. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  49. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  50. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  51. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  52. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  53. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  54. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  55. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  56. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  57. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  58. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  59. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  60. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  61. package/docs/research/RESEARCH_INDEX.md +439 -0
  62. package/docs/research/RESEARCH_SOURCES.md +440 -0
  63. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  64. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  65. package/package.json +2 -2
  66. package/scripts/lib/adaptive-model.sh +427 -0
  67. package/scripts/lib/adaptive-timeout.sh +316 -0
  68. package/scripts/lib/audit-trail.sh +309 -0
  69. package/scripts/lib/auto-recovery.sh +471 -0
  70. package/scripts/lib/bandit-selector.sh +431 -0
  71. package/scripts/lib/bootstrap.sh +104 -2
  72. package/scripts/lib/causal-graph.sh +455 -0
  73. package/scripts/lib/compat.sh +126 -0
  74. package/scripts/lib/compound-audit.sh +337 -0
  75. package/scripts/lib/constitutional.sh +454 -0
  76. package/scripts/lib/context-budget.sh +359 -0
  77. package/scripts/lib/convergence.sh +594 -0
  78. package/scripts/lib/cost-optimizer.sh +634 -0
  79. package/scripts/lib/daemon-adaptive.sh +14 -2
  80. package/scripts/lib/daemon-dispatch.sh +106 -17
  81. package/scripts/lib/daemon-failure.sh +34 -4
  82. package/scripts/lib/daemon-patrol.sh +25 -4
  83. package/scripts/lib/daemon-poll-github.sh +361 -0
  84. package/scripts/lib/daemon-poll-health.sh +299 -0
  85. package/scripts/lib/daemon-poll.sh +27 -611
  86. package/scripts/lib/daemon-state.sh +119 -66
  87. package/scripts/lib/daemon-triage.sh +10 -0
  88. package/scripts/lib/dod-scorecard.sh +442 -0
  89. package/scripts/lib/error-actionability.sh +300 -0
  90. package/scripts/lib/formal-spec.sh +461 -0
  91. package/scripts/lib/helpers.sh +180 -5
  92. package/scripts/lib/intent-analysis.sh +409 -0
  93. package/scripts/lib/loop-convergence.sh +350 -0
  94. package/scripts/lib/loop-iteration.sh +682 -0
  95. package/scripts/lib/loop-progress.sh +48 -0
  96. package/scripts/lib/loop-restart.sh +185 -0
  97. package/scripts/lib/memory-effectiveness.sh +506 -0
  98. package/scripts/lib/mutation-executor.sh +352 -0
  99. package/scripts/lib/outcome-feedback.sh +521 -0
  100. package/scripts/lib/pipeline-cli.sh +336 -0
  101. package/scripts/lib/pipeline-commands.sh +1216 -0
  102. package/scripts/lib/pipeline-detection.sh +101 -3
  103. package/scripts/lib/pipeline-execution.sh +897 -0
  104. package/scripts/lib/pipeline-github.sh +28 -3
  105. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  106. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  107. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  108. package/scripts/lib/pipeline-intelligence.sh +104 -1138
  109. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  110. package/scripts/lib/pipeline-quality-checks.sh +17 -711
  111. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  112. package/scripts/lib/pipeline-stages-build.sh +730 -0
  113. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  114. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  115. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  116. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  117. package/scripts/lib/pipeline-stages.sh +161 -2901
  118. package/scripts/lib/pipeline-state.sh +36 -5
  119. package/scripts/lib/pipeline-util.sh +487 -0
  120. package/scripts/lib/policy-learner.sh +438 -0
  121. package/scripts/lib/process-reward.sh +493 -0
  122. package/scripts/lib/project-detect.sh +649 -0
  123. package/scripts/lib/quality-profile.sh +334 -0
  124. package/scripts/lib/recruit-commands.sh +885 -0
  125. package/scripts/lib/recruit-learning.sh +739 -0
  126. package/scripts/lib/recruit-roles.sh +648 -0
  127. package/scripts/lib/reward-aggregator.sh +458 -0
  128. package/scripts/lib/rl-optimizer.sh +362 -0
  129. package/scripts/lib/root-cause.sh +427 -0
  130. package/scripts/lib/scope-enforcement.sh +445 -0
  131. package/scripts/lib/session-restart.sh +493 -0
  132. package/scripts/lib/skill-memory.sh +300 -0
  133. package/scripts/lib/skill-registry.sh +775 -0
  134. package/scripts/lib/spec-driven.sh +476 -0
  135. package/scripts/lib/test-helpers.sh +18 -7
  136. package/scripts/lib/test-holdout.sh +429 -0
  137. package/scripts/lib/test-optimizer.sh +511 -0
  138. package/scripts/shipwright-file-suggest.sh +45 -0
  139. package/scripts/skills/adversarial-quality.md +61 -0
  140. package/scripts/skills/api-design.md +44 -0
  141. package/scripts/skills/architecture-design.md +50 -0
  142. package/scripts/skills/brainstorming.md +43 -0
  143. package/scripts/skills/data-pipeline.md +44 -0
  144. package/scripts/skills/deploy-safety.md +64 -0
  145. package/scripts/skills/documentation.md +38 -0
  146. package/scripts/skills/frontend-design.md +45 -0
  147. package/scripts/skills/generated/.gitkeep +0 -0
  148. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  149. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  150. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  151. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  152. package/scripts/skills/generated/cli-version-management.md +29 -0
  153. package/scripts/skills/generated/collection-system-validation.md +99 -0
  154. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  155. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  156. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  157. package/scripts/skills/observability.md +79 -0
  158. package/scripts/skills/performance.md +48 -0
  159. package/scripts/skills/pr-quality.md +49 -0
  160. package/scripts/skills/product-thinking.md +43 -0
  161. package/scripts/skills/security-audit.md +49 -0
  162. package/scripts/skills/systematic-debugging.md +40 -0
  163. package/scripts/skills/testing-strategy.md +47 -0
  164. package/scripts/skills/two-stage-review.md +52 -0
  165. package/scripts/skills/validation-thoroughness.md +55 -0
  166. package/scripts/sw +9 -3
  167. package/scripts/sw-activity.sh +9 -8
  168. package/scripts/sw-adaptive.sh +8 -7
  169. package/scripts/sw-adversarial.sh +2 -1
  170. package/scripts/sw-architecture-enforcer.sh +3 -1
  171. package/scripts/sw-auth.sh +12 -2
  172. package/scripts/sw-autonomous.sh +5 -1
  173. package/scripts/sw-changelog.sh +4 -1
  174. package/scripts/sw-checkpoint.sh +2 -1
  175. package/scripts/sw-ci.sh +15 -6
  176. package/scripts/sw-cleanup.sh +4 -26
  177. package/scripts/sw-code-review.sh +45 -20
  178. package/scripts/sw-connect.sh +2 -1
  179. package/scripts/sw-context.sh +2 -1
  180. package/scripts/sw-cost.sh +107 -5
  181. package/scripts/sw-daemon.sh +71 -11
  182. package/scripts/sw-dashboard.sh +3 -1
  183. package/scripts/sw-db.sh +71 -20
  184. package/scripts/sw-decide.sh +8 -2
  185. package/scripts/sw-decompose.sh +360 -17
  186. package/scripts/sw-deps.sh +4 -1
  187. package/scripts/sw-developer-simulation.sh +4 -1
  188. package/scripts/sw-discovery.sh +378 -5
  189. package/scripts/sw-doc-fleet.sh +4 -1
  190. package/scripts/sw-docs-agent.sh +3 -1
  191. package/scripts/sw-docs.sh +2 -1
  192. package/scripts/sw-doctor.sh +453 -2
  193. package/scripts/sw-dora.sh +4 -1
  194. package/scripts/sw-durable.sh +12 -7
  195. package/scripts/sw-e2e-orchestrator.sh +17 -16
  196. package/scripts/sw-eventbus.sh +13 -4
  197. package/scripts/sw-evidence.sh +364 -12
  198. package/scripts/sw-feedback.sh +550 -9
  199. package/scripts/sw-fix.sh +20 -1
  200. package/scripts/sw-fleet-discover.sh +6 -2
  201. package/scripts/sw-fleet-viz.sh +9 -4
  202. package/scripts/sw-fleet.sh +5 -1
  203. package/scripts/sw-github-app.sh +18 -4
  204. package/scripts/sw-github-checks.sh +3 -2
  205. package/scripts/sw-github-deploy.sh +3 -2
  206. package/scripts/sw-github-graphql.sh +18 -7
  207. package/scripts/sw-guild.sh +5 -1
  208. package/scripts/sw-heartbeat.sh +5 -30
  209. package/scripts/sw-hello.sh +67 -0
  210. package/scripts/sw-hygiene.sh +10 -3
  211. package/scripts/sw-incident.sh +273 -5
  212. package/scripts/sw-init.sh +18 -2
  213. package/scripts/sw-instrument.sh +10 -2
  214. package/scripts/sw-intelligence.sh +44 -7
  215. package/scripts/sw-jira.sh +5 -1
  216. package/scripts/sw-launchd.sh +2 -1
  217. package/scripts/sw-linear.sh +4 -1
  218. package/scripts/sw-logs.sh +4 -1
  219. package/scripts/sw-loop.sh +436 -1076
  220. package/scripts/sw-memory.sh +357 -3
  221. package/scripts/sw-mission-control.sh +6 -1
  222. package/scripts/sw-model-router.sh +483 -27
  223. package/scripts/sw-otel.sh +15 -4
  224. package/scripts/sw-oversight.sh +14 -5
  225. package/scripts/sw-patrol-meta.sh +334 -0
  226. package/scripts/sw-pipeline-composer.sh +7 -1
  227. package/scripts/sw-pipeline-vitals.sh +12 -6
  228. package/scripts/sw-pipeline.sh +54 -2653
  229. package/scripts/sw-pm.sh +16 -8
  230. package/scripts/sw-pr-lifecycle.sh +2 -1
  231. package/scripts/sw-predictive.sh +17 -5
  232. package/scripts/sw-prep.sh +185 -2
  233. package/scripts/sw-ps.sh +5 -25
  234. package/scripts/sw-public-dashboard.sh +17 -4
  235. package/scripts/sw-quality.sh +14 -6
  236. package/scripts/sw-reaper.sh +8 -25
  237. package/scripts/sw-recruit.sh +156 -2303
  238. package/scripts/sw-regression.sh +19 -12
  239. package/scripts/sw-release-manager.sh +3 -1
  240. package/scripts/sw-release.sh +4 -1
  241. package/scripts/sw-remote.sh +3 -1
  242. package/scripts/sw-replay.sh +7 -1
  243. package/scripts/sw-retro.sh +158 -1
  244. package/scripts/sw-review-rerun.sh +3 -1
  245. package/scripts/sw-scale.sh +14 -5
  246. package/scripts/sw-security-audit.sh +6 -1
  247. package/scripts/sw-self-optimize.sh +173 -6
  248. package/scripts/sw-session.sh +9 -3
  249. package/scripts/sw-setup.sh +3 -1
  250. package/scripts/sw-stall-detector.sh +406 -0
  251. package/scripts/sw-standup.sh +15 -7
  252. package/scripts/sw-status.sh +3 -1
  253. package/scripts/sw-strategic.sh +14 -6
  254. package/scripts/sw-stream.sh +13 -4
  255. package/scripts/sw-swarm.sh +20 -7
  256. package/scripts/sw-team-stages.sh +13 -6
  257. package/scripts/sw-templates.sh +7 -31
  258. package/scripts/sw-testgen.sh +17 -6
  259. package/scripts/sw-tmux-pipeline.sh +4 -1
  260. package/scripts/sw-tmux-role-color.sh +2 -0
  261. package/scripts/sw-tmux-status.sh +1 -1
  262. package/scripts/sw-tmux.sh +37 -1
  263. package/scripts/sw-trace.sh +3 -1
  264. package/scripts/sw-tracker-github.sh +3 -0
  265. package/scripts/sw-tracker-jira.sh +3 -0
  266. package/scripts/sw-tracker-linear.sh +3 -0
  267. package/scripts/sw-tracker.sh +3 -1
  268. package/scripts/sw-triage.sh +3 -2
  269. package/scripts/sw-upgrade.sh +3 -1
  270. package/scripts/sw-ux.sh +5 -2
  271. package/scripts/sw-webhook.sh +5 -2
  272. package/scripts/sw-widgets.sh +9 -4
  273. package/scripts/sw-worktree.sh +15 -3
  274. package/scripts/test-skill-injection.sh +1233 -0
  275. package/templates/pipelines/autonomous.json +27 -3
  276. package/templates/pipelines/cost-aware.json +34 -8
  277. package/templates/pipelines/deployed.json +12 -0
  278. package/templates/pipelines/enterprise.json +12 -0
  279. package/templates/pipelines/fast.json +6 -0
  280. package/templates/pipelines/full.json +27 -3
  281. package/templates/pipelines/hotfix.json +6 -0
  282. package/templates/pipelines/standard.json +12 -0
  283. package/templates/pipelines/tdd.json +12 -0
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="3.1.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -26,18 +26,20 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
26
26
  fi
27
27
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
28
28
  emit_event() {
29
- local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
29
+ local event_type="$1"; shift; mkdir -p "${PM_STATE_DIR:=${HOME}/.shipwright}"
30
+ # shellcheck disable=SC2155
30
31
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
31
32
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
32
- echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
33
+ echo "${payload}}" >> "${PM_STATE_DIR:=${HOME}/.shipwright}/events.jsonl"
33
34
  }
34
35
  fi
35
36
  # ─── PM History Storage ──────────────────────────────────────────────────────
36
- PM_HISTORY="${HOME}/.shipwright/pm-history.json"
37
+ PM_STATE_DIR="${PM_STATE_DIR:-${HOME}/.shipwright}"
38
+ PM_HISTORY="${PM_STATE_DIR}/pm-history.json"
37
39
 
38
40
  # ─── Ensure PM history file exists ───────────────────────────────────────────
39
41
  ensure_pm_history() {
40
- mkdir -p "${HOME}/.shipwright"
42
+ mkdir -p "${PM_STATE_DIR}"
41
43
  if [[ ! -f "$PM_HISTORY" ]]; then
42
44
  echo '{"decisions":[],"outcomes":[]}' > "$PM_HISTORY"
43
45
  fi
@@ -121,7 +123,8 @@ analyze_issue() {
121
123
  # Count estimated files affected by analyzing body content
122
124
  local file_scope complexity risk estimated_hours
123
125
  local files_mentioned
124
- files_mentioned=$(echo "$body" | grep -o '\b[a-zA-Z0-9_.-]*\.[a-z]*' | sort -u | wc -l || echo "0")
126
+ files_mentioned=$(echo "$body" | grep -o '\b[a-zA-Z0-9_.-]*\.[a-z]*' | sort -u | wc -l || true)
127
+ files_mentioned="${files_mentioned:-0}"
125
128
  files_mentioned=$((files_mentioned + 1)) # At least 1 file
126
129
 
127
130
  # Determine file scope
@@ -203,7 +206,7 @@ analyze_issue() {
203
206
 
204
207
  # ─── recommend_team <analysis_json> ──────────────────────────────────────────
205
208
  # Based on analysis, recommend team composition
206
- # Tries recruit's AI/heuristic team composition first, falls back to hardcoded rules.
209
+ # Tries recruit's AI/heuristic team composition first, falls back to built-in rules.
207
210
  recommend_team() {
208
211
  local analysis="$1"
209
212
 
@@ -253,7 +256,7 @@ recommend_team() {
253
256
  fi
254
257
  fi
255
258
 
256
- # ── Fallback: hardcoded heuristic team composition ──
259
+ # ── Fallback: heuristic team composition ──
257
260
  local complexity risk is_security is_perf file_scope
258
261
  complexity=$(echo "$analysis" | jq -r '.complexity')
259
262
  risk=$(echo "$analysis" | jq -r '.risk')
@@ -494,6 +497,7 @@ cmd_recommend() {
494
497
 
495
498
  # Combine into comprehensive recommendation
496
499
  local recommendation
500
+ # shellcheck disable=SC2046
497
501
  recommendation=$(jq -n \
498
502
  --argjson analysis "$analysis" \
499
503
  --argjson team "$team_rec" \
@@ -512,6 +516,7 @@ cmd_recommend() {
512
516
  ensure_pm_history
513
517
  local tmp_hist
514
518
  tmp_hist=$(mktemp)
519
+ # shellcheck disable=SC2064
515
520
  trap "rm -f '$tmp_hist'" RETURN
516
521
  jq --argjson rec "$recommendation" '.decisions += [$rec]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
517
522
  emit_event "pm.recommend" "issue=${issue_num}"
@@ -540,6 +545,7 @@ cmd_recommend() {
540
545
  ensure_pm_history
541
546
  local tmp_hist
542
547
  tmp_hist=$(mktemp)
548
+ # shellcheck disable=SC2064
543
549
  trap "rm -f '$tmp_hist'" RETURN
544
550
  jq --argjson rec "$recommendation" '.decisions += [$rec]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
545
551
 
@@ -604,6 +610,7 @@ cmd_learn() {
604
610
  # Save to history
605
611
  local tmp_hist
606
612
  tmp_hist=$(mktemp)
613
+ # shellcheck disable=SC2064
607
614
  trap "rm -f '$tmp_hist'" RETURN
608
615
  jq --argjson outcome "$outcome_record" '.outcomes += [$outcome]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
609
616
 
@@ -629,6 +636,7 @@ cmd_history() {
629
636
  local total_decisions success_count fail_count
630
637
  total_decisions=$(jq '.outcomes | length' "$PM_HISTORY")
631
638
  success_count=$(jq '[.outcomes[] | select(.outcome == "success")] | length' "$PM_HISTORY")
639
+ # shellcheck disable=SC2034
632
640
  fail_count=$(jq '[.outcomes[] | select(.outcome == "failure")] | length' "$PM_HISTORY")
633
641
 
634
642
  if [[ "$total_decisions" -gt 0 ]]; then
@@ -6,7 +6,8 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
13
 
@@ -6,7 +6,8 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
13
 
@@ -17,6 +18,8 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
17
18
  # Canonical helpers (colors, output, events)
18
19
  # shellcheck source=lib/helpers.sh
19
20
  [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
21
+ # shellcheck source=lib/config.sh
22
+ [[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
20
23
  # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
21
24
  [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
22
25
  [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
@@ -29,12 +32,14 @@ fi
29
32
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
33
  emit_event() {
31
34
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
35
+ # shellcheck disable=SC2155
32
36
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
37
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
38
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
39
  }
36
40
  fi
37
41
  # ─── Structured Event Log ──────────────────────────────────────────────────
42
+ # shellcheck disable=SC2034
38
43
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
39
44
 
40
45
  # ─── Intelligence Engine (optional) ────────────────────────────────────────
@@ -178,6 +183,7 @@ predictive_confirm_anomaly() {
178
183
  # Find the most recent unconfirmed anomaly for this stage+metric
179
184
  local tmp_file
180
185
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-anomaly-confirm.XXXXXX")
186
+ # shellcheck disable=SC2064
181
187
  trap "rm -f '$tmp_file'" RETURN
182
188
  local found=false
183
189
 
@@ -274,6 +280,7 @@ _predictive_update_alarm_rates() {
274
280
  # Atomic write
275
281
  local tmp_file
276
282
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-anomaly-thresh.XXXXXX")
283
+ # shellcheck disable=SC2064
277
284
  trap "rm -f '$tmp_file'" RETURN
278
285
  jq --arg m "$metric_name" \
279
286
  --argjson crit "$new_critical" \
@@ -384,10 +391,12 @@ Return JSON format:
384
391
  fi
385
392
 
386
393
  # Fallback: heuristic risk assessment
387
- local risk=50
394
+ local default_risk
395
+ default_risk=$(_config_get_int "predictive.default_risk_score" 50 2>/dev/null || echo 50)
396
+ local risk=$default_risk
388
397
  local reason="Default medium risk — no AI analysis available"
389
398
 
390
- # Check for learned keyword weights first, fall back to hardcoded
399
+ # Check for learned keyword weights first, fall back to config defaults
391
400
  local keywords_json
392
401
  keywords_json=$(_predictive_get_risk_keywords)
393
402
 
@@ -416,9 +425,11 @@ Return JSON format:
416
425
  reason="Learned keyword weights: ${matched_keywords%%, }"
417
426
  fi
418
427
  else
419
- # Default hardcoded keyword check
428
+ # Config-driven keyword risk elevation
429
+ local keyword_risk
430
+ keyword_risk=$(_config_get_int "predictive.keyword_risk_score" 70 2>/dev/null || echo 70)
420
431
  if echo "$issue_json" | grep -qiE "refactor|migration|breaking|security|deploy"; then
421
- risk=70
432
+ risk=$keyword_risk
422
433
  reason="Keywords suggest elevated complexity"
423
434
  fi
424
435
  fi
@@ -751,6 +762,7 @@ predict_update_baseline() {
751
762
  # Atomic write
752
763
  local tmp_file
753
764
  tmp_file=$(mktemp)
765
+ # shellcheck disable=SC2064
754
766
  trap "rm -f '$tmp_file'" RETURN
755
767
  jq --arg key "$key" \
756
768
  --argjson val "$new_value" \
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -33,7 +33,8 @@ fi
33
33
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
34
34
  emit_event() {
35
35
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
36
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
36
+ local payload
37
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
37
38
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
38
39
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
39
40
  }
@@ -43,6 +44,7 @@ FORCE=false
43
44
  CHECK_ONLY=false
44
45
  UPDATE_MODE=false
45
46
  WITH_CLAUDE=false
47
+ INTERACTIVE=false
46
48
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
47
49
 
48
50
  # Detection results
@@ -99,6 +101,7 @@ show_help() {
99
101
  echo -e " ${CYAN}--force${RESET} Overwrite existing files"
100
102
  echo -e " ${CYAN}--check${RESET} Audit existing prep (dry run)"
101
103
  echo -e " ${CYAN}--update${RESET} Refresh auto-generated sections only"
104
+ echo -e " ${CYAN}--interactive${RESET} Interactive quality profile dialogue"
102
105
  echo -e " ${CYAN}--with-claude${RESET} Deep analysis using Claude Code (slower, richer)"
103
106
  echo -e " ${CYAN}--help, -h${RESET} Show this help message"
104
107
  echo ""
@@ -129,6 +132,7 @@ for arg in "$@"; do
129
132
  --force) FORCE=true ;;
130
133
  --check) CHECK_ONLY=true ;;
131
134
  --update) UPDATE_MODE=true ;;
135
+ --interactive) INTERACTIVE=true ;;
132
136
  --with-claude) WITH_CLAUDE=true ;;
133
137
  --help|-h) show_help; exit 0 ;;
134
138
  *)
@@ -459,6 +463,7 @@ prep_scan_structure() {
459
463
  for d in docs doc documentation wiki; do
460
464
  [[ -d "$root/$d" ]] && ddirs+=("$d")
461
465
  done
466
+ # shellcheck disable=SC2034
462
467
  DOC_DIRS="${ddirs[*]:-}"
463
468
 
464
469
  # Config files
@@ -499,6 +504,7 @@ prep_scan_structure() {
499
504
  -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | wc -l | tr -d ' ')
500
505
 
501
506
  # Total lines (approximation from source files)
507
+ # shellcheck disable=SC2227,SC2261
502
508
  TOTAL_LINES=$(find "$root" \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \
503
509
  -o -name "*.py" -o -name "*.rb" -o -name "*.go" -o -name "*.rs" -o -name "*.java" \) \
504
510
  -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/vendor/*" \
@@ -572,6 +578,7 @@ prep_extract_patterns() {
572
578
 
573
579
  # ── Middleware ──
574
580
  if grep -rq "app\.use(" "$root/src" "$root/app" "$root/lib" 2>/dev/null; then
581
+ # shellcheck disable=SC2034
575
582
  HAS_MIDDLEWARE=true
576
583
  fi
577
584
 
@@ -767,8 +774,11 @@ ${style_samples}"
767
774
  DB_PATTERNS="$smart_val"
768
775
  fi
769
776
 
777
+ # shellcheck disable=SC2034
770
778
  SEMICOLONS=$(echo "$analysis" | grep "^SEMICOLONS:" | sed 's/^SEMICOLONS:[[:space:]]*//' | head -1)
779
+ # shellcheck disable=SC2034
771
780
  QUOTE_STYLE=$(echo "$analysis" | grep "^QUOTE_STYLE:" | sed 's/^QUOTE_STYLE:[[:space:]]*//' | head -1)
781
+ # shellcheck disable=SC2034
772
782
  INDENT_STYLE=$(echo "$analysis" | grep "^INDENT:" | sed 's/^INDENT:[[:space:]]*//' | head -1)
773
783
 
774
784
  success "Smart detection: ${ARCHITECTURE_PATTERN:-unknown} architecture${FRAMEWORK:+, ${FRAMEWORK}}"
@@ -818,6 +828,7 @@ prep_learn_patterns() {
818
828
  # Write patterns file atomically
819
829
  local tmp_patterns
820
830
  tmp_patterns=$(mktemp)
831
+ # shellcheck disable=SC2064
821
832
  trap "rm -f '$tmp_patterns'" RETURN
822
833
  jq -n \
823
834
  --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
@@ -947,6 +958,24 @@ prep_generate_settings() {
947
958
  success "Generated .claude/settings.json"
948
959
  }
949
960
 
961
+ # ─── prep_generate_managed_mcp ────────────────────────────────────────────────
962
+
963
+ prep_generate_managed_mcp() {
964
+ local filepath="$PROJECT_ROOT/.claude/managed-mcp.json"
965
+ if ! should_write "$filepath"; then return; fi
966
+
967
+ info "Generating .claude/managed-mcp.json..."
968
+
969
+ jq -n '{
970
+ "allowedMcpServers": ["*"],
971
+ "deniedMcpServers": [],
972
+ "note": "Configure MCP server access policies for pipeline agents"
973
+ }' > "$filepath"
974
+
975
+ track_file "$filepath"
976
+ success "Generated .claude/managed-mcp.json"
977
+ }
978
+
950
979
  # ─── prep_generate_hooks ────────────────────────────────────────────────────
951
980
 
952
981
  prep_generate_hooks() {
@@ -1572,6 +1601,156 @@ prep_check() {
1572
1601
  echo ""
1573
1602
  }
1574
1603
 
1604
+ # ─── generate_quality_profile — Interactive quality profile dialogue ────────
1605
+
1606
+ generate_quality_profile() {
1607
+ local profile_path="$PROJECT_ROOT/.claude/quality-profile.json"
1608
+
1609
+ # Load quality-profile library
1610
+ if [[ -f "$SCRIPT_DIR/lib/quality-profile.sh" ]]; then
1611
+ source "$SCRIPT_DIR/lib/quality-profile.sh"
1612
+ else
1613
+ warn "quality-profile.sh library not found"
1614
+ return 1
1615
+ fi
1616
+
1617
+ info "Generating quality profile..."
1618
+
1619
+ # Start with inferred values
1620
+ local inferred
1621
+ inferred=$(qp_infer_from_repo)
1622
+
1623
+ # Generate base profile
1624
+ local base_profile
1625
+ base_profile=$(generate_default_profile)
1626
+
1627
+ # In interactive mode, ask refinement questions
1628
+ if $INTERACTIVE; then
1629
+ echo ""
1630
+ echo -e "${CYAN}${BOLD}Quality Profile Setup${RESET}"
1631
+ echo -e "${DIM}Answer these questions to calibrate quality standards:${RESET}"
1632
+ echo ""
1633
+
1634
+ # Q1: Max PR size
1635
+ local max_pr_lines
1636
+ read -p "Max PR size in lines (default 500): " max_pr_lines
1637
+ max_pr_lines="${max_pr_lines:-500}"
1638
+
1639
+ # Q2: Max files per PR
1640
+ local max_files
1641
+ read -p "Max files per PR (default 15): " max_files
1642
+ max_files="${max_files:-15}"
1643
+
1644
+ # Q3: Test philosophy
1645
+ echo ""
1646
+ echo -e "${DIM}Test philosophy:${RESET}"
1647
+ echo " 1. test_after (default)"
1648
+ echo " 2. tdd"
1649
+ echo " 3. coverage_target"
1650
+ echo " 4. manual"
1651
+ read -p "Choose [1-4]: " test_choice
1652
+
1653
+ local philosophy="test_after"
1654
+ case "$test_choice" in
1655
+ 2) philosophy="tdd" ;;
1656
+ 3) philosophy="coverage_target" ;;
1657
+ 4) philosophy="manual" ;;
1658
+ esac
1659
+
1660
+ # Q4: Architecture pattern
1661
+ echo ""
1662
+ echo -e "${DIM}Architecture pattern:${RESET}"
1663
+ echo " 1. monolith (default)"
1664
+ echo " 2. modular_monolith"
1665
+ echo " 3. microservices"
1666
+ echo " 4. serverless"
1667
+ echo " 5. library"
1668
+ read -p "Choose [1-5]: " arch_choice
1669
+
1670
+ local arch_pattern="monolith"
1671
+ case "$arch_choice" in
1672
+ 2) arch_pattern="modular_monolith" ;;
1673
+ 3) arch_pattern="microservices" ;;
1674
+ 4) arch_pattern="serverless" ;;
1675
+ 5) arch_pattern="library" ;;
1676
+ esac
1677
+
1678
+ # Q5: Never ship rules
1679
+ echo ""
1680
+ read -p "Critical rules to never ship (comma-separated, or press Enter to skip): " never_ship
1681
+
1682
+ # Q6: Focus areas for code review
1683
+ echo ""
1684
+ read -p "Focus areas for review (comma-separated, e.g., 'performance,security'): " focus_areas
1685
+
1686
+ # Q7: Deployment strategy
1687
+ echo ""
1688
+ echo -e "${DIM}Deployment strategy:${RESET}"
1689
+ echo " 1. direct (default)"
1690
+ echo " 2. preview_then_production"
1691
+ echo " 3. staged_rollout"
1692
+ read -p "Choose [1-3]: " deploy_choice
1693
+
1694
+ local deploy_strategy="direct"
1695
+ case "$deploy_choice" in
1696
+ 2) deploy_strategy="preview_then_production" ;;
1697
+ 3) deploy_strategy="staged_rollout" ;;
1698
+ esac
1699
+
1700
+ # Build the updated profile
1701
+ local never_ship_array="[]"
1702
+ if [[ -n "$never_ship" ]]; then
1703
+ never_ship_array=$(echo "$never_ship" | jq -R 'split(",") | map(ltrimstr(" ") | rtrimstr(" "))')
1704
+ fi
1705
+
1706
+ local focus_areas_array="[]"
1707
+ if [[ -n "$focus_areas" ]]; then
1708
+ focus_areas_array=$(echo "$focus_areas" | jq -R 'split(",") | map(ltrimstr(" ") | rtrimstr(" "))')
1709
+ fi
1710
+
1711
+ # Merge updates into base profile
1712
+ base_profile=$(echo "$base_profile" | jq \
1713
+ --arg max_pr "$max_pr_lines" \
1714
+ --arg max_f "$max_files" \
1715
+ --arg phil "$philosophy" \
1716
+ --arg arch "$arch_pattern" \
1717
+ --arg deploy "$deploy_strategy" \
1718
+ --argjson never_ship "$never_ship_array" \
1719
+ --argjson focus "$focus_areas_array" \
1720
+ '.quality.max_pr_lines = ($max_pr | tonumber) |
1721
+ .quality.max_files_per_pr = ($max_f | tonumber) |
1722
+ .quality.never_ship = $never_ship |
1723
+ .testing.philosophy = $phil |
1724
+ .architecture.pattern = $arch |
1725
+ .deployment.strategy = $deploy |
1726
+ .review.focus_areas = $focus'
1727
+ )
1728
+ else
1729
+ # Non-interactive: apply inferred values
1730
+ local test_cmd focus_areas arch_pattern
1731
+ test_cmd=$(echo "$inferred" | jq -r '.test_cmd')
1732
+ arch_pattern=$(echo "$inferred" | jq -r '.framework')
1733
+
1734
+ # Merge inferred test command and architecture
1735
+ if [[ -n "$test_cmd" && "$test_cmd" != "null" && "$test_cmd" != "" ]]; then
1736
+ base_profile=$(echo "$base_profile" | jq --arg cmd "$test_cmd" '.testing.test_cmd = $cmd')
1737
+ fi
1738
+ if [[ -n "$arch_pattern" && "$arch_pattern" != "null" && "$arch_pattern" != "" ]]; then
1739
+ base_profile=$(echo "$base_profile" | jq --arg arch "$arch_pattern" '.architecture.pattern = $arch')
1740
+ fi
1741
+ fi
1742
+
1743
+ # Save the profile
1744
+ if qp_save "$base_profile"; then
1745
+ success "Quality profile saved to .claude/quality-profile.json"
1746
+ track_file ".claude/quality-profile.json"
1747
+ return 0
1748
+ else
1749
+ error "Failed to save quality profile"
1750
+ return 1
1751
+ fi
1752
+ }
1753
+
1575
1754
  # ─── prep_report — Summary output ──────────────────────────────────────────
1576
1755
 
1577
1756
  prep_report() {
@@ -1627,6 +1806,7 @@ main() {
1627
1806
  # Generation
1628
1807
  prep_generate_claude_md
1629
1808
  prep_generate_settings
1809
+ prep_generate_managed_mcp
1630
1810
  prep_generate_hooks
1631
1811
  prep_generate_agents
1632
1812
  prep_generate_architecture
@@ -1634,6 +1814,9 @@ main() {
1634
1814
  prep_generate_dod
1635
1815
  prep_generate_issue_templates
1636
1816
 
1817
+ # Quality profile (auto or interactive)
1818
+ generate_quality_profile
1819
+
1637
1820
  # Deep analysis (optional)
1638
1821
  prep_with_claude
1639
1822
 
package/scripts/sw-ps.sh CHANGED
@@ -5,32 +5,12 @@
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="3.1.0"
8
+ # shellcheck disable=SC2034
9
+ VERSION="3.3.0"
9
10
  set -euo pipefail
10
- trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
-
12
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
-
14
- # Canonical helpers (colors, output, events)
15
- # shellcheck source=lib/helpers.sh
16
- [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
17
- # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
18
- [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
19
- [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
20
- [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
21
- [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
22
- if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
23
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
24
- now_epoch() { date +%s; }
25
- fi
26
- if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
27
- emit_event() {
28
- local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
29
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
30
- while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
31
- echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
32
- }
33
- fi
11
+
12
+ # shellcheck source=lib/bootstrap.sh
13
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/bootstrap.sh"
34
14
  # ─── Format idle time ───────────────────────────────────────────────────────
35
15
  format_idle() {
36
16
  local seconds="$1"
@@ -6,7 +6,8 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.1.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
13
 
@@ -29,7 +30,8 @@ fi
29
30
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
31
  emit_event() {
31
32
  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
+ local payload
34
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
35
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
36
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
37
  }
@@ -79,11 +81,13 @@ sanitize_for_privacy() {
79
81
  gather_pipeline_state() {
80
82
  local privacy="${1:-stages_only}"
81
83
 
84
+ # shellcheck disable=SC2034
82
85
  local state_file="${REPO_DIR}/.claude/pipeline-state.md"
83
86
  local daemon_state="${HOME}/.shipwright/daemon-state.json"
84
87
  local pipeline_artifacts="${REPO_DIR}/.claude/pipeline-artifacts"
85
88
 
86
- local pipeline_data='{
89
+ local pipeline_data
90
+ pipeline_data='{
87
91
  "status":"unknown",
88
92
  "stages":[],
89
93
  "agents":[],
@@ -112,7 +116,8 @@ gather_pipeline_state() {
112
116
  # Read pipeline artifacts if available
113
117
  if [[ -d "$pipeline_artifacts" ]]; then
114
118
  local stage_count
115
- stage_count=$(find "$pipeline_artifacts" -name "*.md" -o -name "*.json" | wc -l || echo "0")
119
+ stage_count=$(find "$pipeline_artifacts" -name "*.md" -o -name "*.json" | wc -l || true)
120
+ stage_count="${stage_count:-0}"
116
121
  pipeline_data=$(jq --arg count "$stage_count" '.artifact_count = $count' <<<"$pipeline_data")
117
122
  fi
118
123
 
@@ -139,6 +144,7 @@ generate_html() {
139
144
 
140
145
  # Escape JSON for embedding in HTML
141
146
  local json_escaped
147
+ # shellcheck disable=SC2034
142
148
  json_escaped=$(echo "$data_json" | sed 's/"/\\"/g' | tr '\n' ' ')
143
149
 
144
150
  cat <<'EOF'
@@ -417,6 +423,7 @@ cmd_export() {
417
423
  # Atomic write
418
424
  local tmp_file
419
425
  tmp_file=$(mktemp)
426
+ # shellcheck disable=SC2064
420
427
  trap "rm -f '$tmp_file'" EXIT
421
428
  echo "$html" > "$tmp_file"
422
429
  mv "$tmp_file" "$output_file"
@@ -452,6 +459,7 @@ cmd_share() {
452
459
  # Append to share links file (atomic)
453
460
  local tmp_file
454
461
  tmp_file=$(mktemp)
462
+ # shellcheck disable=SC2064
455
463
  trap "rm -f '$tmp_file'" EXIT
456
464
  jq ".links += [$link_entry]" "$SHARE_LINKS_FILE" > "$tmp_file"
457
465
  mv "$tmp_file" "$SHARE_LINKS_FILE"
@@ -479,6 +487,7 @@ cmd_revoke() {
479
487
 
480
488
  local tmp_file
481
489
  tmp_file=$(mktemp)
490
+ # shellcheck disable=SC2064
482
491
  trap "rm -f '$tmp_file'" EXIT
483
492
 
484
493
  if jq ".links |= map(select(.token != \"$token\"))" "$SHARE_LINKS_FILE" > "$tmp_file"; then
@@ -544,6 +553,7 @@ cmd_config() {
544
553
  [[ -z "$value" ]] && error "Value required for privacy" && return 1
545
554
  local tmp_file
546
555
  tmp_file=$(mktemp)
556
+ # shellcheck disable=SC2064
547
557
  trap "rm -f '$tmp_file'" EXIT
548
558
  jq ".privacy = \"$value\"" "$SHARE_CONFIG_FILE" > "$tmp_file"
549
559
  mv "$tmp_file" "$SHARE_CONFIG_FILE"
@@ -553,6 +563,7 @@ cmd_config() {
553
563
  [[ -z "$value" ]] && error "Value required for expiry (hours)" && return 1
554
564
  local tmp_file
555
565
  tmp_file=$(mktemp)
566
+ # shellcheck disable=SC2064
556
567
  trap "rm -f '$tmp_file'" EXIT
557
568
  jq ".expiry_hours = $value" "$SHARE_CONFIG_FILE" > "$tmp_file"
558
569
  mv "$tmp_file" "$SHARE_CONFIG_FILE"
@@ -562,6 +573,7 @@ cmd_config() {
562
573
  [[ -z "$value" ]] && error "Value required for domain" && return 1
563
574
  local tmp_file
564
575
  tmp_file=$(mktemp)
576
+ # shellcheck disable=SC2064
565
577
  trap "rm -f '$tmp_file'" EXIT
566
578
  jq ".custom_domain = \"$value\"" "$SHARE_CONFIG_FILE" > "$tmp_file"
567
579
  mv "$tmp_file" "$SHARE_CONFIG_FILE"
@@ -641,6 +653,7 @@ cmd_cleanup() {
641
653
 
642
654
  local tmp_file
643
655
  tmp_file=$(mktemp)
656
+ # shellcheck disable=SC2064
644
657
  trap "rm -f '$tmp_file'" EXIT
645
658
 
646
659
  jq ".links |= map(select((.expires | fromdateiso8601) > $now_epoch_val))" "$SHARE_LINKS_FILE" > "$tmp_file"