shipwright-cli 3.2.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 (279) 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 +4 -4
  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/dashboard/middleware/auth.ts +134 -0
  17. package/dashboard/middleware/constants.ts +21 -0
  18. package/dashboard/public/index.html +2 -6
  19. package/dashboard/public/styles.css +100 -97
  20. package/dashboard/routes/auth.ts +38 -0
  21. package/dashboard/server.ts +66 -25
  22. package/dashboard/services/config.ts +26 -0
  23. package/dashboard/services/db.ts +118 -0
  24. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  25. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  26. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  27. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  28. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  29. package/dashboard/src/components/header.ts +8 -7
  30. package/dashboard/src/core/router.ts +1 -0
  31. package/dashboard/src/design/submarine-theme.ts +253 -0
  32. package/dashboard/src/main.ts +2 -0
  33. package/dashboard/src/types/api.ts +2 -1
  34. package/dashboard/src/views/activity.ts +2 -1
  35. package/dashboard/src/views/shipyard.ts +39 -0
  36. package/dashboard/types/index.ts +166 -0
  37. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  38. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  39. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  40. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  41. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  42. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  43. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  44. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  45. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  46. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  47. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  48. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  49. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  50. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  51. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  52. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  53. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  54. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  55. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  56. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  57. package/docs/research/RESEARCH_INDEX.md +439 -0
  58. package/docs/research/RESEARCH_SOURCES.md +440 -0
  59. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  60. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  61. package/package.json +2 -2
  62. package/scripts/lib/adaptive-model.sh +427 -0
  63. package/scripts/lib/adaptive-timeout.sh +316 -0
  64. package/scripts/lib/audit-trail.sh +309 -0
  65. package/scripts/lib/auto-recovery.sh +471 -0
  66. package/scripts/lib/bandit-selector.sh +431 -0
  67. package/scripts/lib/bootstrap.sh +104 -2
  68. package/scripts/lib/causal-graph.sh +455 -0
  69. package/scripts/lib/compat.sh +126 -0
  70. package/scripts/lib/compound-audit.sh +337 -0
  71. package/scripts/lib/constitutional.sh +454 -0
  72. package/scripts/lib/context-budget.sh +359 -0
  73. package/scripts/lib/convergence.sh +594 -0
  74. package/scripts/lib/cost-optimizer.sh +634 -0
  75. package/scripts/lib/daemon-adaptive.sh +10 -0
  76. package/scripts/lib/daemon-dispatch.sh +106 -17
  77. package/scripts/lib/daemon-failure.sh +34 -4
  78. package/scripts/lib/daemon-patrol.sh +23 -2
  79. package/scripts/lib/daemon-poll-github.sh +361 -0
  80. package/scripts/lib/daemon-poll-health.sh +299 -0
  81. package/scripts/lib/daemon-poll.sh +27 -611
  82. package/scripts/lib/daemon-state.sh +112 -66
  83. package/scripts/lib/daemon-triage.sh +10 -0
  84. package/scripts/lib/dod-scorecard.sh +442 -0
  85. package/scripts/lib/error-actionability.sh +300 -0
  86. package/scripts/lib/formal-spec.sh +461 -0
  87. package/scripts/lib/helpers.sh +177 -4
  88. package/scripts/lib/intent-analysis.sh +409 -0
  89. package/scripts/lib/loop-convergence.sh +350 -0
  90. package/scripts/lib/loop-iteration.sh +682 -0
  91. package/scripts/lib/loop-progress.sh +48 -0
  92. package/scripts/lib/loop-restart.sh +185 -0
  93. package/scripts/lib/memory-effectiveness.sh +506 -0
  94. package/scripts/lib/mutation-executor.sh +352 -0
  95. package/scripts/lib/outcome-feedback.sh +521 -0
  96. package/scripts/lib/pipeline-cli.sh +336 -0
  97. package/scripts/lib/pipeline-commands.sh +1216 -0
  98. package/scripts/lib/pipeline-detection.sh +100 -2
  99. package/scripts/lib/pipeline-execution.sh +897 -0
  100. package/scripts/lib/pipeline-github.sh +28 -3
  101. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  102. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  103. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  104. package/scripts/lib/pipeline-intelligence.sh +100 -1136
  105. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  106. package/scripts/lib/pipeline-quality-checks.sh +17 -715
  107. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  108. package/scripts/lib/pipeline-stages-build.sh +730 -0
  109. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  110. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  111. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  112. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  113. package/scripts/lib/pipeline-stages.sh +59 -2929
  114. package/scripts/lib/pipeline-state.sh +36 -5
  115. package/scripts/lib/pipeline-util.sh +487 -0
  116. package/scripts/lib/policy-learner.sh +438 -0
  117. package/scripts/lib/process-reward.sh +493 -0
  118. package/scripts/lib/project-detect.sh +649 -0
  119. package/scripts/lib/quality-profile.sh +334 -0
  120. package/scripts/lib/recruit-commands.sh +885 -0
  121. package/scripts/lib/recruit-learning.sh +739 -0
  122. package/scripts/lib/recruit-roles.sh +648 -0
  123. package/scripts/lib/reward-aggregator.sh +458 -0
  124. package/scripts/lib/rl-optimizer.sh +362 -0
  125. package/scripts/lib/root-cause.sh +427 -0
  126. package/scripts/lib/scope-enforcement.sh +445 -0
  127. package/scripts/lib/session-restart.sh +493 -0
  128. package/scripts/lib/skill-memory.sh +300 -0
  129. package/scripts/lib/skill-registry.sh +775 -0
  130. package/scripts/lib/spec-driven.sh +476 -0
  131. package/scripts/lib/test-helpers.sh +18 -7
  132. package/scripts/lib/test-holdout.sh +429 -0
  133. package/scripts/lib/test-optimizer.sh +511 -0
  134. package/scripts/shipwright-file-suggest.sh +45 -0
  135. package/scripts/skills/adversarial-quality.md +61 -0
  136. package/scripts/skills/api-design.md +44 -0
  137. package/scripts/skills/architecture-design.md +50 -0
  138. package/scripts/skills/brainstorming.md +43 -0
  139. package/scripts/skills/data-pipeline.md +44 -0
  140. package/scripts/skills/deploy-safety.md +64 -0
  141. package/scripts/skills/documentation.md +38 -0
  142. package/scripts/skills/frontend-design.md +45 -0
  143. package/scripts/skills/generated/.gitkeep +0 -0
  144. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  145. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  146. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  147. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  148. package/scripts/skills/generated/cli-version-management.md +29 -0
  149. package/scripts/skills/generated/collection-system-validation.md +99 -0
  150. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  151. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  152. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  153. package/scripts/skills/observability.md +79 -0
  154. package/scripts/skills/performance.md +48 -0
  155. package/scripts/skills/pr-quality.md +49 -0
  156. package/scripts/skills/product-thinking.md +43 -0
  157. package/scripts/skills/security-audit.md +49 -0
  158. package/scripts/skills/systematic-debugging.md +40 -0
  159. package/scripts/skills/testing-strategy.md +47 -0
  160. package/scripts/skills/two-stage-review.md +52 -0
  161. package/scripts/skills/validation-thoroughness.md +55 -0
  162. package/scripts/sw +9 -3
  163. package/scripts/sw-activity.sh +9 -2
  164. package/scripts/sw-adaptive.sh +2 -1
  165. package/scripts/sw-adversarial.sh +2 -1
  166. package/scripts/sw-architecture-enforcer.sh +3 -1
  167. package/scripts/sw-auth.sh +12 -2
  168. package/scripts/sw-autonomous.sh +5 -1
  169. package/scripts/sw-changelog.sh +4 -1
  170. package/scripts/sw-checkpoint.sh +2 -1
  171. package/scripts/sw-ci.sh +5 -1
  172. package/scripts/sw-cleanup.sh +4 -26
  173. package/scripts/sw-code-review.sh +10 -4
  174. package/scripts/sw-connect.sh +2 -1
  175. package/scripts/sw-context.sh +2 -1
  176. package/scripts/sw-cost.sh +48 -3
  177. package/scripts/sw-daemon.sh +66 -9
  178. package/scripts/sw-dashboard.sh +3 -1
  179. package/scripts/sw-db.sh +59 -16
  180. package/scripts/sw-decide.sh +8 -2
  181. package/scripts/sw-decompose.sh +360 -17
  182. package/scripts/sw-deps.sh +4 -1
  183. package/scripts/sw-developer-simulation.sh +4 -1
  184. package/scripts/sw-discovery.sh +325 -2
  185. package/scripts/sw-doc-fleet.sh +4 -1
  186. package/scripts/sw-docs-agent.sh +3 -1
  187. package/scripts/sw-docs.sh +2 -1
  188. package/scripts/sw-doctor.sh +453 -2
  189. package/scripts/sw-dora.sh +4 -1
  190. package/scripts/sw-durable.sh +4 -3
  191. package/scripts/sw-e2e-orchestrator.sh +17 -16
  192. package/scripts/sw-eventbus.sh +7 -1
  193. package/scripts/sw-evidence.sh +364 -12
  194. package/scripts/sw-feedback.sh +550 -9
  195. package/scripts/sw-fix.sh +20 -1
  196. package/scripts/sw-fleet-discover.sh +6 -2
  197. package/scripts/sw-fleet-viz.sh +4 -1
  198. package/scripts/sw-fleet.sh +5 -1
  199. package/scripts/sw-github-app.sh +16 -3
  200. package/scripts/sw-github-checks.sh +3 -2
  201. package/scripts/sw-github-deploy.sh +3 -2
  202. package/scripts/sw-github-graphql.sh +18 -7
  203. package/scripts/sw-guild.sh +5 -1
  204. package/scripts/sw-heartbeat.sh +5 -30
  205. package/scripts/sw-hello.sh +67 -0
  206. package/scripts/sw-hygiene.sh +6 -1
  207. package/scripts/sw-incident.sh +265 -1
  208. package/scripts/sw-init.sh +18 -2
  209. package/scripts/sw-instrument.sh +10 -2
  210. package/scripts/sw-intelligence.sh +42 -6
  211. package/scripts/sw-jira.sh +5 -1
  212. package/scripts/sw-launchd.sh +2 -1
  213. package/scripts/sw-linear.sh +4 -1
  214. package/scripts/sw-logs.sh +4 -1
  215. package/scripts/sw-loop.sh +432 -1128
  216. package/scripts/sw-memory.sh +356 -2
  217. package/scripts/sw-mission-control.sh +6 -1
  218. package/scripts/sw-model-router.sh +481 -26
  219. package/scripts/sw-otel.sh +13 -4
  220. package/scripts/sw-oversight.sh +14 -5
  221. package/scripts/sw-patrol-meta.sh +334 -0
  222. package/scripts/sw-pipeline-composer.sh +5 -1
  223. package/scripts/sw-pipeline-vitals.sh +2 -1
  224. package/scripts/sw-pipeline.sh +53 -2664
  225. package/scripts/sw-pm.sh +12 -5
  226. package/scripts/sw-pr-lifecycle.sh +2 -1
  227. package/scripts/sw-predictive.sh +7 -1
  228. package/scripts/sw-prep.sh +185 -2
  229. package/scripts/sw-ps.sh +5 -25
  230. package/scripts/sw-public-dashboard.sh +15 -3
  231. package/scripts/sw-quality.sh +2 -1
  232. package/scripts/sw-reaper.sh +8 -25
  233. package/scripts/sw-recruit.sh +156 -2303
  234. package/scripts/sw-regression.sh +19 -12
  235. package/scripts/sw-release-manager.sh +3 -1
  236. package/scripts/sw-release.sh +4 -1
  237. package/scripts/sw-remote.sh +3 -1
  238. package/scripts/sw-replay.sh +7 -1
  239. package/scripts/sw-retro.sh +158 -1
  240. package/scripts/sw-review-rerun.sh +3 -1
  241. package/scripts/sw-scale.sh +10 -3
  242. package/scripts/sw-security-audit.sh +6 -1
  243. package/scripts/sw-self-optimize.sh +6 -3
  244. package/scripts/sw-session.sh +9 -3
  245. package/scripts/sw-setup.sh +3 -1
  246. package/scripts/sw-stall-detector.sh +406 -0
  247. package/scripts/sw-standup.sh +15 -7
  248. package/scripts/sw-status.sh +3 -1
  249. package/scripts/sw-strategic.sh +4 -1
  250. package/scripts/sw-stream.sh +7 -1
  251. package/scripts/sw-swarm.sh +18 -6
  252. package/scripts/sw-team-stages.sh +13 -6
  253. package/scripts/sw-templates.sh +5 -29
  254. package/scripts/sw-testgen.sh +7 -1
  255. package/scripts/sw-tmux-pipeline.sh +4 -1
  256. package/scripts/sw-tmux-role-color.sh +2 -0
  257. package/scripts/sw-tmux-status.sh +1 -1
  258. package/scripts/sw-tmux.sh +3 -1
  259. package/scripts/sw-trace.sh +3 -1
  260. package/scripts/sw-tracker-github.sh +3 -0
  261. package/scripts/sw-tracker-jira.sh +3 -0
  262. package/scripts/sw-tracker-linear.sh +3 -0
  263. package/scripts/sw-tracker.sh +3 -1
  264. package/scripts/sw-triage.sh +2 -1
  265. package/scripts/sw-upgrade.sh +3 -1
  266. package/scripts/sw-ux.sh +5 -2
  267. package/scripts/sw-webhook.sh +3 -1
  268. package/scripts/sw-widgets.sh +3 -1
  269. package/scripts/sw-worktree.sh +15 -3
  270. package/scripts/test-skill-injection.sh +1233 -0
  271. package/templates/pipelines/autonomous.json +27 -3
  272. package/templates/pipelines/cost-aware.json +34 -8
  273. package/templates/pipelines/deployed.json +12 -0
  274. package/templates/pipelines/enterprise.json +12 -0
  275. package/templates/pipelines/fast.json +6 -0
  276. package/templates/pipelines/full.json +27 -3
  277. package/templates/pipelines/hotfix.json +6 -0
  278. package/templates/pipelines/standard.json +12 -0
  279. package/templates/pipelines/tdd.json +12 -0
@@ -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.2.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
 
@@ -28,7 +29,8 @@ fi
28
29
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
29
30
  emit_event() {
30
31
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
31
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
32
+ local payload
33
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
32
34
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
33
35
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
34
36
  }
@@ -38,6 +40,7 @@ SWARM_DIR="${HOME}/.shipwright/swarm"
38
40
  REGISTRY_FILE="${SWARM_DIR}/registry.json"
39
41
  CONFIG_FILE="${SWARM_DIR}/config.json"
40
42
  METRICS_FILE="${SWARM_DIR}/metrics.jsonl"
43
+ # shellcheck disable=SC2034
41
44
  HEALTH_LOG="${SWARM_DIR}/health.jsonl"
42
45
 
43
46
  # ─── Ensure directories exist ──────────────────────────────────────────────
@@ -50,6 +53,7 @@ init_config() {
50
53
  if [[ ! -f "$CONFIG_FILE" ]]; then
51
54
  local tmp_file
52
55
  tmp_file=$(mktemp)
56
+ # shellcheck disable=SC2064
53
57
  trap "rm -f '$tmp_file'" RETURN
54
58
  cat > "$tmp_file" << 'JSON'
55
59
  {
@@ -76,6 +80,7 @@ init_registry() {
76
80
  if [[ ! -f "$REGISTRY_FILE" ]]; then
77
81
  local tmp_file
78
82
  tmp_file=$(mktemp)
83
+ # shellcheck disable=SC2064
79
84
  trap "rm -f '$tmp_file'" RETURN
80
85
  cat > "$tmp_file" << 'JSON'
81
86
  {
@@ -165,6 +170,7 @@ cmd_spawn() {
165
170
  # Add agent to registry
166
171
  local tmp_file
167
172
  tmp_file=$(mktemp)
173
+ # shellcheck disable=SC2064
168
174
  trap "rm -f '$tmp_file'" RETURN
169
175
 
170
176
  jq --arg agent_id "$agent_id" \
@@ -173,7 +179,7 @@ cmd_spawn() {
173
179
  id: $agent_id,
174
180
  type: $agent_type,
175
181
  status: "active",
176
- spawned_at: "'$(now_iso)'",
182
+ spawned_at: "'"$(now_iso)"'",
177
183
  current_task: null,
178
184
  task_started_at: null,
179
185
  success_count: 0,
@@ -181,8 +187,8 @@ cmd_spawn() {
181
187
  avg_completion_time: 0,
182
188
  quality_score: 100,
183
189
  resource_usage: {cpu: 0, memory: 0},
184
- last_heartbeat: "'$(now_iso)'"
185
- }] | .active_count += 1 | .last_updated = "'$(now_iso)'"' \
190
+ last_heartbeat: "'"$(now_iso)"'"
191
+ }] | .active_count += 1 | .last_updated = "'"$(now_iso)"'"' \
186
192
  "$REGISTRY_FILE" > "$tmp_file" && [[ -s "$tmp_file" ]] && \
187
193
  mv "$tmp_file" "$REGISTRY_FILE" || { rm -f "$tmp_file"; error "Failed to update registry"; return 1; }
188
194
  record_metric "$agent_id" "spawn" "1" "$agent_type"
@@ -247,10 +253,12 @@ cmd_retire() {
247
253
  # Mark as retiring / remove from registry
248
254
  local tmp_file
249
255
  tmp_file=$(mktemp)
256
+ # shellcheck disable=SC2064
250
257
  trap "rm -f '$tmp_file'" RETURN
251
258
 
259
+ # shellcheck disable=SC2046
252
260
  jq --arg aid "$agent_id" \
253
- '.agents |= map(select(.id != $aid)) | .active_count = ([.agents[] | select(.status == "active")] | length) | .last_updated = "'$(now_iso)'"' \
261
+ '.agents |= map(select(.id != $aid)) | .active_count = ([.agents[] | select(.status == "active")] | length) | .last_updated = "'"$(now_iso)"'"' \
254
262
  "$REGISTRY_FILE" > "$tmp_file" && [[ -s "$tmp_file" ]] && \
255
263
  mv "$tmp_file" "$REGISTRY_FILE" || { rm -f "$tmp_file"; error "Failed to update registry"; return 1; }
256
264
  record_metric "$agent_id" "retire" "1" "graceful_shutdown"
@@ -334,9 +342,11 @@ cmd_health() {
334
342
 
335
343
  # ─── Swarm spawn helper (for cmd_scale) ───────────────────────────────────
336
344
  swarm_spawn_agent() {
345
+ # shellcheck disable=SC2034
337
346
  local role="${1:-builder}"
338
347
  local count="${2:-1}"
339
348
  local i
349
+ # shellcheck disable=SC2034
340
350
  for i in $(seq 1 "$count"); do
341
351
  cmd_spawn "standard" 2>/dev/null || true
342
352
  done
@@ -384,6 +394,7 @@ cmd_scale() {
384
394
  local agent_id
385
395
  agent_id=$(jq -r '.agents[0].id // empty' "$REGISTRY_FILE" 2>/dev/null)
386
396
  if [[ -n "$agent_id" ]]; then
397
+ # shellcheck disable=SC2034
387
398
  cmd_retire "$agent_id" 2>/dev/null && retired=1 || true
388
399
  fi
389
400
  fi
@@ -527,6 +538,7 @@ cmd_config() {
527
538
 
528
539
  local tmp_file
529
540
  tmp_file=$(mktemp)
541
+ # shellcheck disable=SC2064
530
542
  trap "rm -f '$tmp_file'" RETURN
531
543
 
532
544
  jq --arg key "$key" --arg value "$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.2.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -29,12 +29,14 @@ fi
29
29
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
30
30
  emit_event() {
31
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\""
32
+ local payload
33
+ payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
33
34
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
34
35
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
35
36
  }
36
37
  fi
37
38
  # ─── Structured Event Log ──────────────────────────────────────────────────
39
+ # shellcheck disable=SC2034
38
40
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
39
41
 
40
42
  # ─── Team Configuration ─────────────────────────────────────────────────────
@@ -102,6 +104,7 @@ cmd_compose() {
102
104
  local spec_count
103
105
  spec_count=$(echo "$specialists" | wc -w)
104
106
 
107
+ # shellcheck disable=SC2046
105
108
  team_json=$(jq -n \
106
109
  --arg stage "$stage" \
107
110
  --arg complexity "$complexity" \
@@ -111,7 +114,7 @@ cmd_compose() {
111
114
  '{
112
115
  stage: $stage,
113
116
  complexity: $complexity,
114
- created_at: "'$(now_iso)'",
117
+ created_at: "'"$(now_iso)"'",
115
118
  leader: $leader,
116
119
  specialists: ($specialists | split(" ")),
117
120
  specialist_count: $spec_count,
@@ -139,6 +142,7 @@ cmd_delegate() {
139
142
  # Read hotspots from intelligence cache if available
140
143
  local hotspots=""
141
144
  if [[ -f "$INTELLIGENCE_CACHE" ]]; then
145
+ # shellcheck disable=SC2034
142
146
  hotspots=$(jq -r '.hotspots[]? // empty' "$INTELLIGENCE_CACHE" 2>/dev/null | head -10 || true)
143
147
  fi
144
148
 
@@ -160,6 +164,7 @@ cmd_delegate() {
160
164
  [[ -z "$file" ]] && continue
161
165
  local spec_idx=$((file_count % specialist_count))
162
166
  local task_json
167
+ # shellcheck disable=SC2046
163
168
  task_json=$(jq -n \
164
169
  --arg file "$file" \
165
170
  --arg spec_idx "$spec_idx" \
@@ -168,7 +173,7 @@ cmd_delegate() {
168
173
  file: $file,
169
174
  specialist_idx: ($spec_idx | tonumber),
170
175
  status: $status,
171
- assigned_at: "'$(now_iso)'",
176
+ assigned_at: "'"$(now_iso)"'",
172
177
  result: null
173
178
  }')
174
179
  tasks=$(echo "$tasks" | jq ". += [$task_json]")
@@ -303,6 +308,7 @@ cmd_vote() {
303
308
  fi
304
309
 
305
310
  local result
311
+ # shellcheck disable=SC2046
306
312
  result=$(jq -n \
307
313
  --argjson approve_count "$approve_count" \
308
314
  --argjson reject_count "$reject_count" \
@@ -315,7 +321,7 @@ cmd_vote() {
315
321
  neutral: $neutral_count,
316
322
  total: $total,
317
323
  consensus: $consensus,
318
- decided_at: "'$(now_iso)'"
324
+ decided_at: "'"$(now_iso)"'"
319
325
  }')
320
326
 
321
327
  echo "$result"
@@ -370,6 +376,7 @@ cmd_aggregate() {
370
376
 
371
377
  # Build aggregated output
372
378
  local aggregated
379
+ # shellcheck disable=SC2046
373
380
  aggregated=$(jq -n \
374
381
  --arg stage "$stage" \
375
382
  --argjson results "$results" \
@@ -382,7 +389,7 @@ cmd_aggregate() {
382
389
  failure_count: $failure_count,
383
390
  success_rate: (if ($results | length) > 0 then ($success_count / ($results | length) * 100) | floor else 0 end),
384
391
  results: $results,
385
- aggregated_at: "'$(now_iso)'"
392
+ aggregated_at: "'"$(now_iso)"'"
386
393
  }')
387
394
 
388
395
  echo "$aggregated"
@@ -5,36 +5,12 @@
5
5
  # ║ Templates define reusable agent team configurations (roles, layout, ║
6
6
  # ║ focus areas) that shipwright session --template can use to scaffold teams. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.2.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
- # ─── Cross-platform compatibility ──────────────────────────────────────────
15
- # shellcheck source=lib/compat.sh
16
- [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
17
-
18
- # Canonical helpers (colors, output, events)
19
- # shellcheck source=lib/helpers.sh
20
- [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
21
- # Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
22
- [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
23
- [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
24
- [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
25
- [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
26
- if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
27
- now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
28
- now_epoch() { date +%s; }
29
- fi
30
- if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
31
- emit_event() {
32
- local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
33
- local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
34
- while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
35
- echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
36
- }
37
- fi
11
+
12
+ # shellcheck source=lib/bootstrap.sh
13
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/bootstrap.sh"
38
14
  # ─── Template Discovery ─────────────────────────────────────────────────────
39
15
  REPO_TEMPLATES_DIR="$(cd "$SCRIPT_DIR/../tmux/templates" 2>/dev/null && pwd)" || REPO_TEMPLATES_DIR=""
40
16
  USER_TEMPLATES_DIR="${HOME}/.shipwright/templates"
@@ -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.2.0"
9
+ VERSION="3.3.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -33,6 +33,7 @@ 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
+ # shellcheck disable=SC2155
36
37
  local 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"
@@ -40,11 +41,14 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
40
41
  fi
41
42
  # ─── Configuration ───────────────────────────────────────────────────────────
42
43
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
44
+ # shellcheck disable=SC2034
43
45
  SCRIPTS_DIR="$PROJECT_ROOT/scripts"
44
46
  TEST_SUITES_DIR="$PROJECT_ROOT/scripts"
45
47
  COVERAGE_THRESHOLD=70
46
48
  TESTGEN_DIR="${TESTGEN_DIR:-.claude/testgen}"
49
+ # shellcheck disable=SC2034
47
50
  COVERAGE_DB="$TESTGEN_DIR/coverage.json"
51
+ # shellcheck disable=SC2034
48
52
  QUALITY_DB="$TESTGEN_DIR/quality.json"
49
53
  REGRESSION_DB="$TESTGEN_DIR/regressions.json"
50
54
 
@@ -154,6 +158,7 @@ EOF
154
158
 
155
159
  generate_tests() {
156
160
  local target_script="${1:-.}"
161
+ # shellcheck disable=SC2034
157
162
  local threshold="${2:-$COVERAGE_THRESHOLD}"
158
163
 
159
164
  mkdir -p "$TESTGEN_DIR"
@@ -233,6 +238,7 @@ EOF
233
238
  func_snippet=$(awk "/^${func}\(\\)/,/^[a-zA-Z_][a-zA-Z0-9_]*\(\)|^$/" "$target_script" 2>/dev/null | head -40 || true)
234
239
  local prompt_file
235
240
  prompt_file=$(mktemp "${TMPDIR:-/tmp}/sw-testgen-prompt.XXXXXX")
241
+ # shellcheck disable=SC2064
236
242
  trap "rm -f '$prompt_file'" RETURN
237
243
  {
238
244
  echo "Generate a bash test function for the following shell function. Use real assertions (assert_equal, assert_contains, or test exit code). Test happy path and at least one edge or error case. Output only the bash function body."
@@ -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.2.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
 
@@ -100,6 +101,7 @@ cmd_spawn() {
100
101
 
101
102
  local tmp_file
102
103
  tmp_file=$(mktemp)
104
+ # shellcheck disable=SC2064
103
105
  trap "rm -f '$tmp_file'" RETURN
104
106
  cat > "$tmp_file" << EOF
105
107
  {
@@ -293,6 +295,7 @@ cmd_list() {
293
295
  [[ -z "$line" ]] && continue
294
296
 
295
297
  # Parse window line: "0: pipeline-42 (1 panes)"
298
+ # shellcheck disable=SC2034
296
299
  local window_num window_name status
297
300
  window_num=$(echo "$line" | cut -d: -f1)
298
301
  window_name=$(echo "$line" | cut -d' ' -f2)
@@ -34,6 +34,7 @@ fi
34
34
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
35
35
  emit_event() {
36
36
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
37
+ # shellcheck disable=SC2155
37
38
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
38
39
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
39
40
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -48,6 +49,7 @@ TITLE_LOWER="$(echo "$PANE_TITLE" | tr '[:upper:]' '[:lower:]')"
48
49
  # Map role keywords to colors
49
50
  COLOR="#00d4ff" # default: cyan
50
51
 
52
+ # shellcheck disable=SC2221,SC2222
51
53
  case "$TITLE_LOWER" in
52
54
  *leader*|*lead*|*pm*|*manager*|*orchestrat*)
53
55
  COLOR="#00d4ff" # cyan — command & control
@@ -26,6 +26,7 @@ fi
26
26
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
27
27
  emit_event() {
28
28
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
29
+ # shellcheck disable=SC2155
29
30
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
30
31
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
31
32
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -140,7 +141,6 @@ case "${1:-pipeline}" in
140
141
  agents) agent_widget ;;
141
142
  all)
142
143
  # Combine both widgets
143
- local p a
144
144
  p="$(pipeline_widget)"
145
145
  a="$(agent_widget)"
146
146
  echo "${a}${p}"
@@ -11,7 +11,8 @@
11
11
  # ║ shipwright tmux fix — Auto-fix common issues ║
12
12
  # ║ shipwright tmux reload — Reload tmux config ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="3.2.0"
14
+ # shellcheck disable=SC2034
15
+ VERSION="3.3.0"
15
16
  set -euo pipefail
16
17
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
18
 
@@ -354,6 +355,7 @@ tmux_install() {
354
355
  cp "$REPO_DIR/tmux/tmux.conf" "$HOME/.tmux.conf"
355
356
  success "Installed ~/.tmux.conf"
356
357
  else
358
+ # shellcheck disable=SC2088
357
359
  info "~/.tmux.conf exists — run 'shipwright init' to update"
358
360
  fi
359
361
  fi
@@ -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.2.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
 
@@ -112,6 +113,7 @@ trace_show() {
112
113
  echo -e "${BOLD}FEATURE BRANCH${RESET}"
113
114
 
114
115
  local feature_branch
116
+ # shellcheck disable=SC2034
115
117
  feature_branch="feat/issue-${issue}"
116
118
 
117
119
  # Check if worktree exists
@@ -23,6 +23,7 @@ fi
23
23
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
24
24
  emit_event() {
25
25
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
26
+ # shellcheck disable=SC2155
26
27
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
27
28
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
28
29
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -54,6 +55,7 @@ provider_discover_issues() {
54
55
  fi
55
56
 
56
57
  # Fetch as JSON: number, title, labels, state
58
+ # shellcheck disable=SC2054
57
59
  gh_args+=(--json number,title,labels,state)
58
60
 
59
61
  local response
@@ -198,6 +200,7 @@ provider_create_issue() {
198
200
  provider_notify() {
199
201
  local event="$1"
200
202
  local gh_issue="${2:-}"
203
+ # shellcheck disable=SC2034
201
204
  local detail="${3:-}"
202
205
 
203
206
  # GitHub is the native provider — no external sync needed
@@ -23,6 +23,7 @@ fi
23
23
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
24
24
  emit_event() {
25
25
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
26
+ # shellcheck disable=SC2155
26
27
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
27
28
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
28
29
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -95,7 +96,9 @@ provider_discover_statuses() {
95
96
  mkdir -p "$cache_dir"
96
97
  local tmp_cache
97
98
  tmp_cache=$(mktemp)
99
+ # shellcheck disable=SC2064
98
100
  trap "rm -f '$tmp_cache'" RETURN
101
+ # shellcheck disable=SC1010
99
102
  jq -n \
100
103
  --arg ts "$(date +%s)" \
101
104
  --arg in_progress "${discovered_in_progress:-}" \
@@ -23,6 +23,7 @@ fi
23
23
  if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
24
24
  emit_event() {
25
25
  local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
26
+ # shellcheck disable=SC2155
26
27
  local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
27
28
  while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
28
29
  echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
@@ -105,7 +106,9 @@ provider_discover_statuses() {
105
106
  mkdir -p "$cache_dir"
106
107
  local tmp_cache
107
108
  tmp_cache=$(mktemp)
109
+ # shellcheck disable=SC2064
108
110
  trap "rm -f '$tmp_cache'" RETURN
111
+ # shellcheck disable=SC1010
109
112
  jq -n \
110
113
  --arg ts "$(date +%s)" \
111
114
  --arg backlog "${discovered_backlog:-}" \
@@ -6,8 +6,10 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="3.2.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ # shellcheck disable=SC2034
11
13
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
14
 
13
15
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -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.2.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
 
@@ -2,7 +2,8 @@
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
3
  # ║ sw upgrade — Detect and apply updates from the repo ║
4
4
  # ╚═══════════════════════════════════════════════════════════════════════════╝
5
- VERSION="3.2.0"
5
+ # shellcheck disable=SC2034
6
+ VERSION="3.3.0"
6
7
  set -euo pipefail
7
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
9
 
@@ -217,6 +218,7 @@ read_manifest_checksum() {
217
218
 
218
219
  write_manifest() {
219
220
  mkdir -p "$MANIFEST_DIR"
221
+ # shellcheck disable=SC2155
220
222
  local json='{\n "schema": 1,\n "version": "1.1.0",\n "installed_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",\n "repo_path": "'"$REPO_PATH"'",\n "files": {'
221
223
  local first=true
222
224
 
package/scripts/sw-ux.sh CHANGED
@@ -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.2.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
 
12
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -27,10 +28,12 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
27
28
  now_epoch() { date +%s; }
28
29
  fi
29
30
  # ─── Structured Event Log ──────────────────────────────────────────────────
31
+ # shellcheck disable=SC2034
30
32
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
31
33
 
32
34
  # ─── UX Configuration ──────────────────────────────────────────────────────
33
35
  UX_CONFIG_GLOBAL="${HOME}/.shipwright/ux-config.json"
36
+ # shellcheck disable=SC2034
34
37
  UX_CONFIG_REPO="./.claude/ux-config.json"
35
38
 
36
39
  # Detect accessibility modes
@@ -324,7 +327,7 @@ table_header() {
324
327
 
325
328
  # Print separator
326
329
  for w in "${widths[@]}"; do
327
- printf '%*s' "$w" | tr ' ' '─'
330
+ printf '%*s' "$w" "" | tr ' ' '─'
328
331
  done
329
332
  echo ""
330
333
  }
@@ -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.2.0"
9
+ # shellcheck disable=SC2034
10
+ VERSION="3.3.0"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
 
12
13
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -235,6 +236,7 @@ webhook_server_bash() {
235
236
  {
236
237
  read -r -u 3 request_line || break
237
238
  local method path protocol
239
+ # shellcheck disable=SC2034
238
240
  read -r method path protocol <<< "$request_line"
239
241
 
240
242
  # Read headers until blank line (Bash 3.2 compatible — no associative arrays)
@@ -8,7 +8,7 @@
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
11
- VERSION="3.2.0"
11
+ VERSION="3.3.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14
14
 
@@ -34,6 +34,7 @@ CONFIG_DIR="${HOME}/.shipwright"
34
34
  CONFIG_FILE="${CONFIG_DIR}/widgets-config.json"
35
35
  EVENTS_FILE="${CONFIG_DIR}/events.jsonl"
36
36
  PIPELINE_STATE="${REPO_DIR}/.claude/pipeline-state.md"
37
+ # shellcheck disable=SC2034
37
38
  COSTS_FILE="${CONFIG_DIR}/costs.json"
38
39
 
39
40
  # ─── Helpers ───────────────────────────────────────────────────────────────
@@ -260,6 +261,7 @@ cmd_slack() {
260
261
 
261
262
  # Build Slack message
262
263
  local message_json
264
+ # shellcheck disable=SC2046
263
265
  message_json=$(jq -n \
264
266
  --arg channel "$channel" \
265
267
  --arg status "$status" \
@@ -5,7 +5,8 @@
5
5
  # ║ Each agent gets its own worktree so parallel agents don't clobber ║
6
6
  # ║ each other's files. Worktrees live in .worktrees/ relative to root. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="3.2.0"
8
+ # shellcheck disable=SC2034
9
+ VERSION="3.3.0"
9
10
  set -euo pipefail
10
11
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
12
 
@@ -213,19 +214,30 @@ worktree_remove() {
213
214
  local worktree_path="$WORKTREE_DIR/$name"
214
215
  local branch="loop/$name"
215
216
 
217
+ # Log disk usage before removal
218
+ local size_before_mb=0
219
+ if [[ -d "$worktree_path" ]]; then
220
+ size_before_mb=$(du -sm "$worktree_path" 2>/dev/null | awk '{print $1}')
221
+ fi
222
+
216
223
  if [[ -d "$worktree_path" ]]; then
217
224
  git worktree remove "$worktree_path" --force 2>/dev/null || {
218
225
  warn "Could not cleanly remove worktree $name, forcing..."
226
+ # Validate path is under WORKTREE_DIR before rm -rf
219
227
  if [[ -n "$worktree_path" && "$worktree_path" == "$WORKTREE_DIR/"* ]]; then
220
228
  rm -rf "$worktree_path"
229
+ else
230
+ error "Worktree path invalid (not under $WORKTREE_DIR): $worktree_path"
231
+ return 1
221
232
  fi
222
- git worktree prune 2>/dev/null || true
223
233
  }
234
+ # Always run prune to clean up .git/config
235
+ git worktree prune 2>/dev/null || true
224
236
  fi
225
237
 
226
238
  git branch -D "$branch" 2>/dev/null || true
227
239
 
228
- success "Removed worktree: ${BOLD}$name${RESET}"
240
+ success "Removed worktree: ${BOLD}$name${RESET}${DIM} (freed ${size_before_mb}MB)${RESET}"
229
241
  }
230
242
 
231
243
  worktree_cleanup() {