shipwright-cli 2.2.2 → 2.3.1

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 (151) hide show
  1. package/README.md +12 -11
  2. package/dashboard/public/index.html +224 -8
  3. package/dashboard/public/styles.css +1078 -4
  4. package/dashboard/server.ts +1100 -15
  5. package/dashboard/src/canvas/interactions.ts +74 -0
  6. package/dashboard/src/canvas/layout.ts +85 -0
  7. package/dashboard/src/canvas/overlays.ts +117 -0
  8. package/dashboard/src/canvas/particles.ts +105 -0
  9. package/dashboard/src/canvas/renderer.ts +191 -0
  10. package/dashboard/src/components/charts/bar.ts +54 -0
  11. package/dashboard/src/components/charts/donut.ts +25 -0
  12. package/dashboard/src/components/charts/pipeline-rail.ts +105 -0
  13. package/dashboard/src/components/charts/sparkline.ts +82 -0
  14. package/dashboard/src/components/header.ts +616 -0
  15. package/dashboard/src/components/modal.ts +413 -0
  16. package/dashboard/src/components/terminal.ts +144 -0
  17. package/dashboard/src/core/api.test.ts +362 -0
  18. package/dashboard/src/core/api.ts +381 -0
  19. package/dashboard/src/core/helpers.ts +118 -0
  20. package/dashboard/src/core/router.test.ts +266 -0
  21. package/dashboard/src/core/router.ts +190 -0
  22. package/dashboard/src/core/sse.ts +38 -0
  23. package/dashboard/src/core/state.test.ts +235 -0
  24. package/dashboard/src/core/state.ts +150 -0
  25. package/dashboard/src/core/ws.test.ts +216 -0
  26. package/dashboard/src/core/ws.ts +143 -0
  27. package/dashboard/src/design/icons.test.ts +105 -0
  28. package/dashboard/src/design/icons.ts +131 -0
  29. package/dashboard/src/design/tokens.test.ts +204 -0
  30. package/dashboard/src/design/tokens.ts +160 -0
  31. package/dashboard/src/main.ts +68 -0
  32. package/dashboard/src/types/api.ts +337 -0
  33. package/dashboard/src/views/activity.ts +185 -0
  34. package/dashboard/src/views/agent-cockpit.ts +236 -0
  35. package/dashboard/src/views/agents.ts +72 -0
  36. package/dashboard/src/views/fleet-map.ts +299 -0
  37. package/dashboard/src/views/insights.ts +298 -0
  38. package/dashboard/src/views/machines.ts +162 -0
  39. package/dashboard/src/views/metrics.ts +420 -0
  40. package/dashboard/src/views/overview.ts +409 -0
  41. package/dashboard/src/views/pipeline-theater.ts +219 -0
  42. package/dashboard/src/views/pipelines.ts +595 -0
  43. package/dashboard/src/views/team.ts +362 -0
  44. package/dashboard/src/views/timeline.ts +389 -0
  45. package/dashboard/tsconfig.json +21 -0
  46. package/dashboard/vitest.config.ts +27 -0
  47. package/docs/AGI-WHATS-NEXT.md +15 -15
  48. package/package.json +16 -2
  49. package/scripts/lib/helpers.sh +30 -0
  50. package/scripts/lib/pipeline-quality-checks.sh +1 -1
  51. package/scripts/lib/pipeline-stages.sh +59 -0
  52. package/scripts/sw +86 -167
  53. package/scripts/sw-activity.sh +1 -1
  54. package/scripts/sw-adaptive.sh +1 -1
  55. package/scripts/sw-adversarial.sh +1 -1
  56. package/scripts/sw-architecture-enforcer.sh +1 -1
  57. package/scripts/sw-auth.sh +14 -6
  58. package/scripts/sw-autonomous.sh +230 -13
  59. package/scripts/sw-changelog.sh +2 -2
  60. package/scripts/sw-checkpoint.sh +1 -1
  61. package/scripts/sw-ci.sh +1 -1
  62. package/scripts/sw-cleanup.sh +1 -1
  63. package/scripts/sw-code-review.sh +1 -1
  64. package/scripts/sw-connect.sh +1 -1
  65. package/scripts/sw-context.sh +1 -1
  66. package/scripts/sw-cost.sh +1 -1
  67. package/scripts/sw-daemon.sh +2 -2
  68. package/scripts/sw-dashboard.sh +1 -1
  69. package/scripts/sw-db.sh +1 -1
  70. package/scripts/sw-decompose.sh +1 -1
  71. package/scripts/sw-deps.sh +1 -1
  72. package/scripts/sw-developer-simulation.sh +1 -1
  73. package/scripts/sw-discovery.sh +1 -1
  74. package/scripts/sw-doc-fleet.sh +1 -1
  75. package/scripts/sw-docs-agent.sh +1 -1
  76. package/scripts/sw-docs.sh +1 -1
  77. package/scripts/sw-doctor.sh +8 -1
  78. package/scripts/sw-dora.sh +1 -1
  79. package/scripts/sw-durable.sh +1 -1
  80. package/scripts/sw-e2e-orchestrator.sh +1 -1
  81. package/scripts/sw-eventbus.sh +1 -1
  82. package/scripts/sw-feedback.sh +1 -1
  83. package/scripts/sw-fix.sh +6 -5
  84. package/scripts/sw-fleet-discover.sh +1 -1
  85. package/scripts/sw-fleet-viz.sh +1 -1
  86. package/scripts/sw-fleet.sh +1 -1
  87. package/scripts/sw-github-app.sh +5 -2
  88. package/scripts/sw-github-checks.sh +1 -1
  89. package/scripts/sw-github-deploy.sh +1 -1
  90. package/scripts/sw-github-graphql.sh +1 -1
  91. package/scripts/sw-guild.sh +1 -1
  92. package/scripts/sw-heartbeat.sh +1 -1
  93. package/scripts/sw-hygiene.sh +1 -1
  94. package/scripts/sw-incident.sh +1 -1
  95. package/scripts/sw-init.sh +112 -9
  96. package/scripts/sw-instrument.sh +6 -1
  97. package/scripts/sw-intelligence.sh +5 -1
  98. package/scripts/sw-jira.sh +1 -1
  99. package/scripts/sw-launchd.sh +1 -1
  100. package/scripts/sw-linear.sh +20 -9
  101. package/scripts/sw-logs.sh +1 -1
  102. package/scripts/sw-loop.sh +2 -1
  103. package/scripts/sw-memory.sh +10 -1
  104. package/scripts/sw-mission-control.sh +1 -1
  105. package/scripts/sw-model-router.sh +4 -1
  106. package/scripts/sw-otel.sh +1 -1
  107. package/scripts/sw-oversight.sh +1 -1
  108. package/scripts/sw-pipeline-composer.sh +3 -1
  109. package/scripts/sw-pipeline-vitals.sh +4 -6
  110. package/scripts/sw-pipeline.sh +4 -1
  111. package/scripts/sw-pm.sh +5 -2
  112. package/scripts/sw-pr-lifecycle.sh +1 -1
  113. package/scripts/sw-predictive.sh +4 -1
  114. package/scripts/sw-prep.sh +3 -2
  115. package/scripts/sw-ps.sh +1 -1
  116. package/scripts/sw-public-dashboard.sh +10 -4
  117. package/scripts/sw-quality.sh +1 -1
  118. package/scripts/sw-reaper.sh +1 -1
  119. package/scripts/sw-recruit.sh +16 -0
  120. package/scripts/sw-regression.sh +2 -1
  121. package/scripts/sw-release-manager.sh +1 -1
  122. package/scripts/sw-release.sh +7 -5
  123. package/scripts/sw-remote.sh +1 -1
  124. package/scripts/sw-replay.sh +1 -1
  125. package/scripts/sw-retro.sh +4 -1
  126. package/scripts/sw-scale.sh +4 -1
  127. package/scripts/sw-security-audit.sh +1 -1
  128. package/scripts/sw-self-optimize.sh +113 -1
  129. package/scripts/sw-session.sh +1 -1
  130. package/scripts/sw-setup.sh +1 -1
  131. package/scripts/sw-standup.sh +2 -1
  132. package/scripts/sw-status.sh +1 -1
  133. package/scripts/sw-strategic.sh +2 -1
  134. package/scripts/sw-stream.sh +1 -1
  135. package/scripts/sw-swarm.sh +6 -1
  136. package/scripts/sw-team-stages.sh +1 -1
  137. package/scripts/sw-templates.sh +1 -1
  138. package/scripts/sw-testgen.sh +3 -2
  139. package/scripts/sw-tmux-pipeline.sh +2 -1
  140. package/scripts/sw-tmux.sh +1 -1
  141. package/scripts/sw-trace.sh +1 -1
  142. package/scripts/sw-tracker-jira.sh +1 -0
  143. package/scripts/sw-tracker-linear.sh +1 -0
  144. package/scripts/sw-tracker.sh +1 -1
  145. package/scripts/sw-triage.sh +198 -11
  146. package/scripts/sw-upgrade.sh +1 -1
  147. package/scripts/sw-ux.sh +1 -1
  148. package/scripts/sw-webhook.sh +1 -1
  149. package/scripts/sw-widgets.sh +2 -2
  150. package/scripts/sw-worktree.sh +1 -1
  151. package/dashboard/public/app.js +0 -4422
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.2.2"
10
+ VERSION="2.3.1"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -204,6 +204,7 @@ set_config() {
204
204
  # Use jq to safely update the config
205
205
  local tmp_config
206
206
  tmp_config=$(mktemp)
207
+ trap "rm -f '$tmp_config'" RETURN
207
208
 
208
209
  if [[ "$value" == "true" ]] || [[ "$value" == "false" ]]; then
209
210
  jq ".${key} = ${value}" "$MODEL_ROUTING_CONFIG" > "$tmp_config"
@@ -336,6 +337,7 @@ configure_ab_test() {
336
337
 
337
338
  local tmp_config
338
339
  tmp_config=$(mktemp)
340
+ trap "rm -f '$tmp_config'" RETURN
339
341
 
340
342
  jq ".a_b_test = {\"enabled\": true, \"percentage\": $percentage, \"variant\": \"$variant\"}" \
341
343
  "$MODEL_ROUTING_CONFIG" > "$tmp_config"
@@ -521,6 +523,7 @@ main() {
521
523
  if command -v jq &>/dev/null; then
522
524
  local tmp_config
523
525
  tmp_config=$(mktemp)
526
+ trap "rm -f '$tmp_config'" RETURN
524
527
  jq ".a_b_test.enabled = false" "$MODEL_ROUTING_CONFIG" > "$tmp_config"
525
528
  mv "$tmp_config" "$MODEL_ROUTING_CONFIG"
526
529
  success "Disabled A/B testing"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -7,7 +7,7 @@
7
7
  set -euo pipefail
8
8
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
9
 
10
- VERSION="2.2.2"
10
+ VERSION="2.3.1"
11
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
13
13
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -124,6 +124,7 @@ composer_create_pipeline() {
124
124
  # Atomic write
125
125
  local tmp_file
126
126
  tmp_file=$(mktemp "${output_file}.XXXXXX")
127
+ trap "rm -f '$tmp_file'" RETURN
127
128
  echo "$composed" | jq '.' > "$tmp_file"
128
129
  mv "$tmp_file" "$output_file"
129
130
 
@@ -152,6 +153,7 @@ composer_create_pipeline() {
152
153
  info "Using fallback template: standard" >&2
153
154
  local tmp_file
154
155
  tmp_file=$(mktemp "${output_file}.XXXXXX")
156
+ trap "rm -f '$tmp_file'" RETURN
155
157
  cp "$fallback_template" "$tmp_file"
156
158
  mv "$tmp_file" "$output_file"
157
159
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -316,13 +316,11 @@ _compute_error_maturity() {
316
316
  # ─── File Locking Helpers ──────────────────────────────────────────────────
317
317
  _vitals_acquire_lock() {
318
318
  local lockfile="$1.lock"
319
- local fd=200
320
- eval "exec $fd>\"$lockfile\""
321
- flock -w 5 "$fd" || { warn "Vitals lock timeout"; return 1; }
319
+ exec 200>"$lockfile"
320
+ flock -w 5 200 || { warn "Vitals lock timeout"; return 1; }
322
321
  }
323
322
  _vitals_release_lock() {
324
- local fd=200
325
- flock -u "$fd" 2>/dev/null || true
323
+ flock -u 200 2>/dev/null || true
326
324
  }
327
325
 
328
326
  # ═══════════════════════════════════════════════════════════════════════════
@@ -11,7 +11,7 @@ unset CLAUDECODE 2>/dev/null || true
11
11
  # Ignore SIGHUP so tmux attach/detach doesn't kill long-running plan/design/review stages
12
12
  trap '' HUP
13
13
 
14
- VERSION="2.2.2"
14
+ VERSION="2.3.1"
15
15
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
16
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
17
17
 
@@ -850,6 +850,7 @@ Reply with ONLY the classification word, nothing else." --model haiku < /dev/nul
850
850
  mkdir -p "$class_dir" 2>/dev/null || true
851
851
  local tmp_class
852
852
  tmp_class="$(mktemp)"
853
+ trap "rm -f '$tmp_class'" RETURN
853
854
  if [[ -f "$class_history" ]]; then
854
855
  jq --arg sig "$error_sig" --arg cls "$classification" --arg canon "$canonical_category" --arg stage "$stage_id" \
855
856
  '.[$sig] = {"classification": $cls, "canonical": $canon, "stage": $stage, "recorded_at": now}' \
@@ -1269,6 +1270,7 @@ run_pipeline() {
1269
1270
  # Remove this stage from the skip file
1270
1271
  local tmp_skip
1271
1272
  tmp_skip="$(mktemp)"
1273
+ trap "rm -f '$tmp_skip'" RETURN
1272
1274
  grep -vx "$id" "$ARTIFACTS_DIR/skip-stage.txt" > "$tmp_skip" 2>/dev/null || true
1273
1275
  mv "$tmp_skip" "$ARTIFACTS_DIR/skip-stage.txt"
1274
1276
  continue
@@ -1600,6 +1602,7 @@ pipeline_post_completion_cleanup() {
1600
1602
  # Reset status to idle (preserves the file for reference but unblocks new runs)
1601
1603
  local tmp_state
1602
1604
  tmp_state=$(mktemp)
1605
+ trap "rm -f '$tmp_state'" RETURN
1603
1606
  sed 's/^status: .*/status: idle/' "$STATE_FILE" > "$tmp_state" 2>/dev/null || true
1604
1607
  mv "$tmp_state" "$STATE_FILE"
1605
1608
  fi
package/scripts/sw-pm.sh CHANGED
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -77,7 +77,7 @@ show_help() {
77
77
  echo -e " ${DIM}shipwright pm history${RESET} # Show past decisions"
78
78
  echo -e " ${DIM}shipwright pm history --pattern${RESET} # Show success patterns"
79
79
  echo ""
80
- echo -e "${DIM}Docs: https://sethdford.github.io/shipwright | GitHub: https://github.com/sethdford/shipwright${RESET}"
80
+ echo -e "${DIM}Docs: $(_sw_docs_url) | GitHub: $(_sw_github_url)${RESET}"
81
81
  }
82
82
 
83
83
  # ─── analyze_issue <issue_num> ───────────────────────────────────────────────
@@ -522,6 +522,7 @@ cmd_recommend() {
522
522
  ensure_pm_history
523
523
  local tmp_hist
524
524
  tmp_hist=$(mktemp)
525
+ trap "rm -f '$tmp_hist'" RETURN
525
526
  jq --argjson rec "$recommendation" '.decisions += [$rec]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
526
527
  emit_event "pm.recommend" "issue=${issue_num}"
527
528
  return 0
@@ -549,6 +550,7 @@ cmd_recommend() {
549
550
  ensure_pm_history
550
551
  local tmp_hist
551
552
  tmp_hist=$(mktemp)
553
+ trap "rm -f '$tmp_hist'" RETURN
552
554
  jq --argjson rec "$recommendation" '.decisions += [$rec]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
553
555
 
554
556
  success "Recommendation saved to history"
@@ -612,6 +614,7 @@ cmd_learn() {
612
614
  # Save to history
613
615
  local tmp_hist
614
616
  tmp_hist=$(mktemp)
617
+ trap "rm -f '$tmp_hist'" RETURN
615
618
  jq --argjson outcome "$outcome_record" '.outcomes += [$outcome]' "$PM_HISTORY" > "$tmp_hist" && mv "$tmp_hist" "$PM_HISTORY"
616
619
 
617
620
  success "Recorded ${outcome} outcome for issue #${issue_num}"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -188,6 +188,7 @@ predictive_confirm_anomaly() {
188
188
  # Find the most recent unconfirmed anomaly for this stage+metric
189
189
  local tmp_file
190
190
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-anomaly-confirm.XXXXXX")
191
+ trap "rm -f '$tmp_file'" RETURN
191
192
  local found=false
192
193
 
193
194
  # Process file in reverse to find most recent unconfirmed
@@ -283,6 +284,7 @@ _predictive_update_alarm_rates() {
283
284
  # Atomic write
284
285
  local tmp_file
285
286
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-anomaly-thresh.XXXXXX")
287
+ trap "rm -f '$tmp_file'" RETURN
286
288
  jq --arg m "$metric_name" \
287
289
  --argjson crit "$new_critical" \
288
290
  --argjson warn "$new_warning" \
@@ -755,6 +757,7 @@ predict_update_baseline() {
755
757
  # Atomic write
756
758
  local tmp_file
757
759
  tmp_file=$(mktemp)
760
+ trap "rm -f '$tmp_file'" RETURN
758
761
  jq --arg key "$key" \
759
762
  --argjson val "$new_value" \
760
763
  --argjson cnt "$new_count" \
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -129,7 +129,7 @@ show_help() {
129
129
  echo -e " ${DIM}.claude/hooks/*.sh${RESET} Pre/post action hooks"
130
130
  echo -e " ${DIM}.github/ISSUE_TEMPLATE/agent-task.md${RESET} Agent task template"
131
131
  echo ""
132
- echo -e "${DIM}Docs: https://sethdford.github.io/shipwright | GitHub: https://github.com/sethdford/shipwright${RESET}"
132
+ echo -e "${DIM}Docs: $(_sw_docs_url) | GitHub: $(_sw_github_url)${RESET}"
133
133
  }
134
134
 
135
135
  # ─── CLI Argument Parsing ───────────────────────────────────────────────────
@@ -828,6 +828,7 @@ prep_learn_patterns() {
828
828
  # Write patterns file atomically
829
829
  local tmp_patterns
830
830
  tmp_patterns=$(mktemp)
831
+ trap "rm -f '$tmp_patterns'" RETURN
831
832
  jq -n \
832
833
  --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
833
834
  --arg lang "${LANG_DETECTED:-}" \
package/scripts/sw-ps.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # ║ Displays a table of agents running in claude-* tmux windows with ║
6
6
  # ║ PID, status, idle time, and pane references. ║
7
7
  # ╚═══════════════════════════════════════════════════════════════════════════╝
8
- VERSION="2.2.2"
8
+ VERSION="2.3.1"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -323,7 +323,7 @@ generate_html() {
323
323
  </div>
324
324
 
325
325
  <div class="footer">
326
- <p>Generated by <a href="https://github.com/sethdford/shipwright">Shipwright</a> v1.13.0</p>
326
+ <p>Generated by <a href="GITHUB_URL_PLACEHOLDER">Shipwright</a> v1.13.0</p>
327
327
  <p style="margin-top: 8px; color: #555;">Dashboard auto-refreshes every 30s when served from dashboard server</p>
328
328
  <p style="margin-top: 8px;" id="footer-timestamp">Generated: —</p>
329
329
  </div>
@@ -384,7 +384,12 @@ generate_html() {
384
384
  const newDoc = parser.parseFromString(html, 'text/html');
385
385
  const newScript = newDoc.querySelector('script');
386
386
  if (newScript) {
387
- eval(newScript.textContent);
387
+ // Use DOM script insertion instead of eval: script comes from same-origin
388
+ // fetch of this page; DOM insertion executes in global scope without eval
389
+ const s = document.createElement('script');
390
+ s.textContent = newScript.textContent;
391
+ document.body.appendChild(s);
392
+ s.remove();
388
393
  renderDashboard();
389
394
  }
390
395
  })
@@ -417,6 +422,7 @@ cmd_export() {
417
422
  html="${html//TITLE_PLACEHOLDER/$title}"
418
423
  html="${html//PRIVACY_PLACEHOLDER/$privacy}"
419
424
  html="${html//\{DATA_PLACEHOLDER\}/$state_data}"
425
+ html="${html//GITHUB_URL_PLACEHOLDER/$(_sw_github_url)}"
420
426
 
421
427
  # Atomic write
422
428
  local tmp_file
@@ -736,7 +742,7 @@ ${BOLD}SHARE LINKS${RESET}
736
742
  Share links require a running dashboard server to serve the public endpoint.
737
743
  By default, requires dashboard to serve at: https://your-domain.com/public-dashboard/<token>
738
744
 
739
- ${DIM}Docs: https://sethdford.github.io/shipwright${RESET}
745
+ ${DIM}Docs: $(_sw_docs_url)${RESET}
740
746
  EOF
741
747
  }
742
748
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -11,7 +11,7 @@
11
11
  # ║ shipwright reaper --watch Continuous loop (default: 5s) ║
12
12
  # ║ shipwright reaper --dry-run Preview what would be reaped ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="2.2.2"
14
+ VERSION="2.3.1"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17
 
@@ -286,6 +286,7 @@ EOF
286
286
  )
287
287
  local _tmp_roles
288
288
  _tmp_roles=$(mktemp)
289
+ trap "rm -f '$_tmp_roles'" RETURN
289
290
  if echo "$roles_json" | jq '.' > "$_tmp_roles" 2>/dev/null && [[ -s "$_tmp_roles" ]]; then
290
291
  mv "$_tmp_roles" "$ROLES_DB"
291
292
  else
@@ -426,6 +427,7 @@ _recruit_record_match() {
426
427
  if [[ "$current_lines" -gt "$max_history" ]]; then
427
428
  local tmp_trunc
428
429
  tmp_trunc=$(mktemp)
430
+ trap "rm -f '$tmp_trunc'" RETURN
429
431
  tail -n "$max_history" "$MATCH_HISTORY" > "$tmp_trunc" && _recruit_locked_write "$MATCH_HISTORY" "$tmp_trunc" || rm -f "$tmp_trunc"
430
432
  fi
431
433
 
@@ -492,6 +494,7 @@ Return JSON only."
492
494
  # Persist to roles DB
493
495
  local tmp_file
494
496
  tmp_file=$(mktemp)
497
+ trap "rm -f '$tmp_file'" RETURN
495
498
  if jq --arg key "$role_key" --argjson role "$(echo "$result" | jq 'del(.key)')" '.[$key] = $role' "$ROLES_DB" > "$tmp_file"; then
496
499
  _recruit_locked_write "$ROLES_DB" "$tmp_file"
497
500
  else
@@ -544,6 +547,7 @@ Return JSON only."
544
547
 
545
548
  local tmp_file
546
549
  tmp_file=$(mktemp)
550
+ trap "rm -f '$tmp_file'" RETURN
547
551
  if jq --arg key "$role_key" --argjson role "$role_json" '.[$key] = $role' "$ROLES_DB" > "$tmp_file"; then
548
552
  _recruit_locked_write "$ROLES_DB" "$tmp_file"
549
553
  else
@@ -622,6 +626,7 @@ cmd_record_outcome() {
622
626
 
623
627
  local tmp_file
624
628
  tmp_file=$(mktemp)
629
+ trap "rm -f '$tmp_file'" RETURN
625
630
  jq --arg id "$agent_id" \
626
631
  --argjson tc "$tasks_completed" \
627
632
  --argjson sc "$success_count" \
@@ -659,6 +664,7 @@ cmd_record_outcome() {
659
664
  if [[ -f "$MATCH_HISTORY" ]]; then
660
665
  local tmp_mh
661
666
  tmp_mh=$(mktemp)
667
+ trap "rm -f '$tmp_mh'" RETURN
662
668
  # Find the most recent match for this agent_id with null outcome, and backfill
663
669
  awk -v agent="$agent_id" -v outcome="$outcome" '
664
670
  BEGIN { found = 0 }
@@ -753,6 +759,7 @@ _recruit_track_role_usage() {
753
759
 
754
760
  local tmp_file
755
761
  tmp_file=$(mktemp)
762
+ trap "rm -f '$tmp_file'" RETURN
756
763
  jq --arg role "$role" --arg event "$event" --arg ts "$(now_iso)" '
757
764
  .[$role] = (.[$role] // {matches: 0, successes: 0, failures: 0, last_used: ""}) |
758
765
  .[$role].last_used = $ts |
@@ -1221,6 +1228,7 @@ _recruit_meta_learning_check() {
1221
1228
 
1222
1229
  local tmp_file
1223
1230
  tmp_file=$(mktemp)
1231
+ trap "rm -f '$tmp_file'" RETURN
1224
1232
  jq --argjson corr "$correction" '
1225
1233
  .corrections = ((.corrections // []) + [$corr] | .[-100:])
1226
1234
  ' "$META_LEARNING_DB" > "$tmp_file" && _recruit_locked_write "$META_LEARNING_DB" "$tmp_file" || rm -f "$tmp_file"
@@ -1267,6 +1275,7 @@ _recruit_reflect() {
1267
1275
  # Track accuracy trend
1268
1276
  local tmp_file
1269
1277
  tmp_file=$(mktemp)
1278
+ trap "rm -f '$tmp_file'" RETURN
1270
1279
  jq --argjson acc "$accuracy" --arg ts "$(now_iso)" '
1271
1280
  .accuracy_trend = ((.accuracy_trend // []) + [{accuracy: $acc, ts: $ts}] | .[-50:]) |
1272
1281
  .last_reflection = $ts
@@ -1355,6 +1364,7 @@ _recruit_meta_validate_self_tune() {
1355
1364
  # Reset heuristics to empty (forces fallback to keyword_match defaults)
1356
1365
  local tmp_heur
1357
1366
  tmp_heur=$(mktemp)
1367
+ trap "rm -f '$tmp_heur'" RETURN
1358
1368
  echo '{"keyword_weights": {}, "meta_reverted_at": "'"$(now_iso)"'", "revert_reason": "accuracy_below_floor"}' > "$tmp_heur"
1359
1369
  _recruit_locked_write "$HEURISTICS_DB" "$tmp_heur" || rm -f "$tmp_heur"
1360
1370
  emit_event "recruit_meta_revert" "accuracy=${current_accuracy}" "floor=${accuracy_floor}" "reason=declining_below_floor"
@@ -1454,6 +1464,7 @@ Return JSON only."
1454
1464
 
1455
1465
  local tmp_file
1456
1466
  tmp_file=$(mktemp)
1467
+ trap "rm -f '$tmp_file'" RETURN
1457
1468
  jq --arg key "$role_key" --argjson role "$role_json" '.[$key] = $role' "$ROLES_DB" > "$tmp_file" && _recruit_locked_write "$ROLES_DB" "$tmp_file" || rm -f "$tmp_file"
1458
1469
 
1459
1470
  # Update heuristics with trigger keywords
@@ -1462,6 +1473,7 @@ Return JSON only."
1462
1473
  if [[ -n "$keywords" ]]; then
1463
1474
  local heur_tmp
1464
1475
  heur_tmp=$(mktemp)
1476
+ trap "rm -f '$heur_tmp'" RETURN
1465
1477
  while IFS= read -r kw; do
1466
1478
  [[ -z "$kw" ]] && continue
1467
1479
  jq --arg kw "$kw" --arg role "$role_key" \
@@ -1588,6 +1600,7 @@ Return JSON only."
1588
1600
  # Save the LLM-generated mind profile
1589
1601
  local tmp_file
1590
1602
  tmp_file=$(mktemp)
1603
+ trap "rm -f '$tmp_file'" RETURN
1591
1604
  jq --arg id "$agent_id" --argjson mind "$result" '.[$id] = ($mind + {updated: (now | todate)})' "$AGENT_MINDS_DB" > "$tmp_file" && _recruit_locked_write "$AGENT_MINDS_DB" "$tmp_file" || rm -f "$tmp_file"
1592
1605
 
1593
1606
  success "Mind profile generated:"
@@ -1629,6 +1642,7 @@ Return JSON only."
1629
1642
 
1630
1643
  local tmp_file
1631
1644
  tmp_file=$(mktemp)
1645
+ trap "rm -f '$tmp_file'" RETURN
1632
1646
  jq --arg id "$agent_id" --argjson mind "$mind_json" '.[$id] = $mind' "$AGENT_MINDS_DB" > "$tmp_file" && _recruit_locked_write "$AGENT_MINDS_DB" "$tmp_file" || rm -f "$tmp_file"
1633
1647
 
1634
1648
  local strengths_display="none detected"
@@ -1815,6 +1829,7 @@ cmd_self_tune() {
1815
1829
 
1816
1830
  local tmp_heuristics
1817
1831
  tmp_heuristics=$(mktemp)
1832
+ trap "rm -f '$tmp_heuristics'" RETURN
1818
1833
  cp "$HEURISTICS_DB" "$tmp_heuristics"
1819
1834
 
1820
1835
  local i=0
@@ -2561,6 +2576,7 @@ cmd_audit() {
2561
2576
  if [[ -f "$META_LEARNING_DB" ]]; then
2562
2577
  local tmp_audit
2563
2578
  tmp_audit=$(mktemp)
2579
+ trap "rm -f '$tmp_audit'" RETURN
2564
2580
  jq --argjson score "$score" --arg ts "$(now_iso)" --argjson fails "$fail_count" '
2565
2581
  .audit_trend = ((.audit_trend // []) + [{score: $score, ts: $ts, failures: $fails}] | .[-50:])
2566
2582
  ' "$META_LEARNING_DB" > "$tmp_audit" && _recruit_locked_write "$META_LEARNING_DB" "$tmp_audit" || rm -f "$tmp_audit"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -198,6 +198,7 @@ cmd_baseline() {
198
198
 
199
199
  local tmp_file
200
200
  tmp_file=$(mktemp "${baseline_file}.tmp.XXXXXX")
201
+ trap "rm -f '$tmp_file'" RETURN
201
202
 
202
203
  echo "$metrics" > "$tmp_file"
203
204
  mv "$tmp_file" "$baseline_file"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -5,8 +5,9 @@
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+ trap 'rm -f "${tmp_file:-}"' EXIT
8
9
 
9
- VERSION="2.2.2"
10
+ VERSION="2.3.1"
10
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
12
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
13
 
@@ -258,7 +259,7 @@ generate_changelog_md() {
258
259
  echo ""
259
260
  echo "$breaking_commits" | while IFS='|' read -r hash subject; do
260
261
  [[ -z "$hash" ]] && continue
261
- echo "- $subject ([\`$hash\`](https://github.com/sethdford/shipwright/commit/$hash))"
262
+ echo "- $subject ([\`$hash\`]($(_sw_github_url)/commit/$hash))"
262
263
  done
263
264
  echo ""
264
265
  fi
@@ -269,7 +270,7 @@ generate_changelog_md() {
269
270
  echo ""
270
271
  echo "$features_commits" | while IFS='|' read -r hash subject; do
271
272
  [[ -z "$hash" ]] && continue
272
- echo "- $subject ([\`$hash\`](https://github.com/sethdford/shipwright/commit/$hash))"
273
+ echo "- $subject ([\`$hash\`]($(_sw_github_url)/commit/$hash))"
273
274
  done
274
275
  echo ""
275
276
  fi
@@ -280,7 +281,7 @@ generate_changelog_md() {
280
281
  echo ""
281
282
  echo "$fixes_commits" | while IFS='|' read -r hash subject; do
282
283
  [[ -z "$hash" ]] && continue
283
- echo "- $subject ([\`$hash\`](https://github.com/sethdford/shipwright/commit/$hash))"
284
+ echo "- $subject ([\`$hash\`]($(_sw_github_url)/commit/$hash))"
284
285
  done
285
286
  echo ""
286
287
  fi
@@ -291,7 +292,7 @@ generate_changelog_md() {
291
292
  echo ""
292
293
  echo "$docs_commits" | while IFS='|' read -r hash subject; do
293
294
  [[ -z "$hash" ]] && continue
294
- echo "- $subject ([\`$hash\`](https://github.com/sethdford/shipwright/commit/$hash))"
295
+ echo "- $subject ([\`$hash\`]($(_sw_github_url)/commit/$hash))"
295
296
  done
296
297
  echo ""
297
298
  fi
@@ -346,6 +347,7 @@ update_version_in_files() {
346
347
  # This is shell-safe: VERSION="1.11.0" → VERSION="1.12.0"
347
348
  local tmp_file
348
349
  tmp_file=$(mktemp)
350
+ trap "rm -f '$tmp_file'" RETURN
349
351
  sed 's/^VERSION="[^"]*"$/VERSION="'"$version_num"'"/' "$file" > "$tmp_file"
350
352
  mv "$tmp_file" "$file"
351
353
  success "Updated VERSION in $(basename "$file")"
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
12
12
 
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -465,6 +465,9 @@ cmd_run() {
465
465
  # Generate report
466
466
  generate_retro_report "$from_date" "$to_date" "$analysis" "$agent_perf" "$velocity"
467
467
 
468
+ # Feed retro into self-optimization loop (|| true to avoid breaking retro if optimization unavailable)
469
+ [[ -f "$SCRIPT_DIR/sw-self-optimize.sh" ]] && "$SCRIPT_DIR/sw-self-optimize.sh" ingest-retro || true
470
+
468
471
  # Offer to create issues
469
472
  if command -v gh &>/dev/null; then
470
473
  echo ""
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Dependency check ─────────────────────────────────────────────────────────
@@ -64,6 +64,7 @@ init_rules() {
64
64
  if [[ ! -f "$SCALE_RULES_FILE" ]]; then
65
65
  local tmp_file
66
66
  tmp_file=$(mktemp)
67
+ trap "rm -f '$tmp_file'" RETURN
67
68
  cat > "$tmp_file" << 'JSON'
68
69
  {
69
70
  "iteration_threshold": 3,
@@ -105,6 +106,7 @@ in_cooldown() {
105
106
  update_scale_state() {
106
107
  local tmp_file
107
108
  tmp_file=$(mktemp)
109
+ trap "rm -f '$tmp_file'" RETURN
108
110
 
109
111
  if [[ -f "$SCALE_STATE_FILE" ]]; then
110
112
  # Update existing state
@@ -241,6 +243,7 @@ cmd_rules() {
241
243
 
242
244
  local tmp_file
243
245
  tmp_file=$(mktemp)
246
+ trap "rm -f '$tmp_file'" RETURN
244
247
 
245
248
  jq --arg key "$key" --arg value "$value" \
246
249
  'if ($value | test("^[0-9]+$")) then
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="2.2.2"
9
+ VERSION="2.3.1"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12