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
package/scripts/sw-fix.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 ──────────────────────────────────────────
@@ -227,7 +227,7 @@ fix_start() {
227
227
  # Validate repos exist
228
228
  for repo in "${REPOS[@]}"; do
229
229
  local expanded
230
- expanded=$(eval echo "$repo")
230
+ expanded="${repo/#\~/$HOME}"
231
231
  if [[ ! -d "$expanded" ]]; then
232
232
  error "Repo directory not found: $expanded"
233
233
  exit 1
@@ -267,7 +267,7 @@ fix_start() {
267
267
  info "Dry run — would execute:"
268
268
  for repo in "${REPOS[@]}"; do
269
269
  local expanded
270
- expanded=$(eval echo "$repo")
270
+ expanded="${repo/#\~/$HOME}"
271
271
  local rname
272
272
  rname=$(basename "$expanded")
273
273
  echo -e " ${DIM}cd $expanded && git checkout -b $branch_name${RESET}"
@@ -281,7 +281,7 @@ fix_start() {
281
281
  local repos_json="[]"
282
282
  for repo in "${REPOS[@]}"; do
283
283
  local expanded
284
- expanded=$(eval echo "$repo")
284
+ expanded="${repo/#\~/$HOME}"
285
285
  local rname
286
286
  rname=$(basename "$expanded")
287
287
  repos_json=$(echo "$repos_json" | jq --arg name "$rname" --arg path "$expanded" \
@@ -291,6 +291,7 @@ fix_start() {
291
291
  # Atomic write initial state
292
292
  local tmp_state
293
293
  tmp_state=$(mktemp)
294
+ trap 'rm -f "$tmp_state"' RETURN
294
295
  jq -n \
295
296
  --arg goal "$GOAL" \
296
297
  --arg branch "$branch_name" \
@@ -311,7 +312,7 @@ fix_start() {
311
312
 
312
313
  for repo in "${REPOS[@]}"; do
313
314
  local expanded
314
- expanded=$(eval echo "$repo")
315
+ expanded="${repo/#\~/$HOME}"
315
316
  local rname
316
317
  rname=$(basename "$expanded")
317
318
 
@@ -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
 
@@ -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
 
@@ -90,6 +90,7 @@ cmd_setup() {
90
90
  # Create config atomically
91
91
  local tmp_config
92
92
  tmp_config=$(mktemp)
93
+ trap "rm -f '$tmp_config'" RETURN
93
94
  jq -n \
94
95
  --arg app_id "$app_id" \
95
96
  --arg key_path "$key_path" \
@@ -194,6 +195,7 @@ _cache_token() {
194
195
 
195
196
  local tmp_tokens
196
197
  tmp_tokens=$(mktemp)
198
+ trap "rm -f '$tmp_tokens'" RETURN
197
199
 
198
200
  if [[ -f "$TOKENS_FILE" ]]; then
199
201
  jq ".tokens += [{\"installation_id\":$installation_id,\"token\":\"$token\",\"expires_at\":\"$expires_at\"}]" \
@@ -399,9 +401,10 @@ cmd_manifest() {
399
401
  manifest=$(jq -n \
400
402
  --arg name "$app_name" \
401
403
  --arg webhook_url "$webhook_url" \
404
+ --arg github_url "$(_sw_github_url)" \
402
405
  '{
403
406
  name: $name,
404
- url: "https://github.com/sethdford/shipwright",
407
+ url: $github_url,
405
408
  hook_attributes: {
406
409
  url: $webhook_url
407
410
  },
@@ -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="${SCRIPT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
11
11
  REPO_DIR="${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
 
@@ -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
 
12
12
  # ─── Cross-platform compatibility ──────────────────────────────────────────
@@ -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 ──────────────────────────────────────────
@@ -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
 
@@ -8,9 +8,10 @@
8
8
  # ║ ║
9
9
  # ║ --deploy Detect platform and generate deployed.json template ║
10
10
  # ╚═══════════════════════════════════════════════════════════════════════════╝
11
- VERSION="2.2.2"
11
+ VERSION="2.3.1"
12
12
  set -euo pipefail
13
13
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
14
+ trap 'rm -f "${tmp:-}"' EXIT
14
15
 
15
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
17
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
@@ -305,6 +306,59 @@ if [[ $_verify_fail -eq 0 ]]; then
305
306
  success "Verified: tmux config, overlay, TPM, and plugins all deployed"
306
307
  fi
307
308
 
309
+ # ─── CLI Bootstrap (symlinks + PATH) ─────────────────────────────────────────
310
+ # Install sw/shipwright/cct symlinks so the CLI works from anywhere
311
+ BIN_DIR="$HOME/.local/bin"
312
+ mkdir -p "$BIN_DIR"
313
+
314
+ SW_SRC="$SCRIPT_DIR/sw"
315
+ if [[ -f "$SW_SRC" ]]; then
316
+ _cli_changed=false
317
+ for _cmd in sw shipwright cct; do
318
+ _dest="$BIN_DIR/$_cmd"
319
+ if [[ -L "$_dest" ]] && [[ "$(readlink "$_dest")" == "$SW_SRC" ]]; then
320
+ continue
321
+ fi
322
+ ln -sf "$SW_SRC" "$_dest"
323
+ _cli_changed=true
324
+ done
325
+ if [[ "$_cli_changed" == "true" ]]; then
326
+ success "CLI symlinks: sw, shipwright, cct → $BIN_DIR"
327
+ else
328
+ success "CLI symlinks already correct"
329
+ fi
330
+ fi
331
+
332
+ # Ensure ~/.local/bin is in PATH via shell profile
333
+ if ! echo "$PATH" | tr ':' '\n' | grep -qxF "$BIN_DIR"; then
334
+ _login_shell="$(basename "${SHELL:-/bin/zsh}")"
335
+ case "$_login_shell" in
336
+ zsh) _rc="$HOME/.zshrc" ;;
337
+ bash)
338
+ if [[ -f "$HOME/.bash_profile" ]]; then _rc="$HOME/.bash_profile"
339
+ else _rc="$HOME/.bashrc"; fi
340
+ ;;
341
+ *) _rc="$HOME/.profile" ;;
342
+ esac
343
+
344
+ _marker="# Added by Shipwright"
345
+ _line='export PATH="$HOME/.local/bin:$PATH"'
346
+
347
+ if [[ -f "$_rc" ]] && grep -qF "$_marker" "$_rc" 2>/dev/null; then
348
+ info "PATH already configured in $_rc"
349
+ else
350
+ if [[ -f "$_rc" ]]; then
351
+ printf '\n%s\n%s\n' "$_marker" "$_line" >> "$_rc"
352
+ else
353
+ printf '%s\n%s\n' "$_marker" "$_line" > "$_rc"
354
+ fi
355
+ success "Added ~/.local/bin to PATH in $_rc"
356
+ fi
357
+ export PATH="$BIN_DIR:$PATH"
358
+ else
359
+ success "~/.local/bin already in PATH"
360
+ fi
361
+
308
362
  # ─── Team Templates ──────────────────────────────────────────────────────────
309
363
  SHIPWRIGHT_DIR="$HOME/.shipwright"
310
364
  TEMPLATES_SRC="$REPO_DIR/tmux/templates"
@@ -338,18 +392,17 @@ fi
338
392
 
339
393
  # ─── Shell Completions ────────────────────────────────────────────────────────
340
394
  # Detect shell type and install completions to the correct location
395
+ # Detect the user's login shell (not the script's running shell).
396
+ # This script runs in bash, so $BASH_VERSION is always set — check $SHELL first.
341
397
  SHELL_TYPE=""
342
- if [[ -n "${ZSH_VERSION:-}" ]]; then
398
+ if [[ "${SHELL:-}" == *"zsh"* ]]; then
399
+ SHELL_TYPE="zsh"
400
+ elif [[ "${SHELL:-}" == *"bash"* ]]; then
401
+ SHELL_TYPE="bash"
402
+ elif [[ -n "${ZSH_VERSION:-}" ]]; then
343
403
  SHELL_TYPE="zsh"
344
404
  elif [[ -n "${BASH_VERSION:-}" ]]; then
345
405
  SHELL_TYPE="bash"
346
- else
347
- # Try to detect from $SHELL env var
348
- if [[ "$SHELL" == *"zsh"* ]]; then
349
- SHELL_TYPE="zsh"
350
- elif [[ "$SHELL" == *"bash"* ]]; then
351
- SHELL_TYPE="bash"
352
- fi
353
406
  fi
354
407
 
355
408
  COMPLETIONS_SRC="$REPO_DIR/completions"
@@ -375,6 +428,10 @@ elif [[ "$SHELL_TYPE" == "zsh" ]]; then
375
428
  } >> "$HOME/.zshrc"
376
429
  info "Added ~/.zsh/completions to fpath in ~/.zshrc"
377
430
  fi
431
+ # Ensure compinit is present (needed for completions to work)
432
+ if ! grep -q "compinit" "$HOME/.zshrc" 2>/dev/null; then
433
+ echo "autoload -Uz compinit && compinit" >> "$HOME/.zshrc"
434
+ fi
378
435
  else
379
436
  # Create minimal .zshrc with fpath
380
437
  {
@@ -585,6 +642,22 @@ if [[ "$SKIP_CLAUDE_MD" == "false" && -f "$CLAUDE_MD_SRC" ]]; then
585
642
  fi
586
643
  fi
587
644
 
645
+ # ─── GitHub CLI Authentication ────────────────────────────────────────────────
646
+ # gh auth is required for daemon, pipeline, PR creation, and issue management
647
+ if command -v gh &>/dev/null; then
648
+ if gh auth status &>/dev/null 2>&1; then
649
+ success "GitHub CLI authenticated"
650
+ else
651
+ warn "GitHub CLI installed but not authenticated"
652
+ echo -e " ${DIM}Required for: daemon, pipeline, PR creation, issue management${RESET}"
653
+ echo -e " ${DIM}Run: ${RESET}${BOLD}gh auth login${RESET}"
654
+ fi
655
+ else
656
+ warn "GitHub CLI (gh) not installed"
657
+ echo -e " ${DIM}Required for: daemon, pipeline, PR creation, issue management${RESET}"
658
+ echo -e " ${DIM}Install: ${RESET}${BOLD}brew install gh && gh auth login${RESET}"
659
+ fi
660
+
588
661
  # ─── Reload tmux if inside a session ──────────────────────────────────────────
589
662
  if [[ -n "${TMUX:-}" ]]; then
590
663
  if tmux source-file "$HOME/.tmux.conf" 2>/dev/null; then
@@ -595,6 +668,36 @@ if [[ -n "${TMUX:-}" ]]; then
595
668
  fi
596
669
  fi
597
670
 
671
+ # ─── Bun (required for dashboard) ──────────────────────────────────────────
672
+ if command -v bun &>/dev/null || [[ -x "$HOME/.bun/bin/bun" ]]; then
673
+ _bun_cmd="bun"
674
+ [[ -x "$HOME/.bun/bin/bun" ]] && _bun_cmd="$HOME/.bun/bin/bun"
675
+ success "Bun $($_bun_cmd --version 2>/dev/null || echo "installed") — dashboard ready"
676
+ else
677
+ info "Installing Bun (required for ${BOLD}shipwright dashboard${RESET})..."
678
+ if curl -fsSL https://bun.sh/install | bash 2>/dev/null; then
679
+ export PATH="$HOME/.bun/bin:$PATH"
680
+ success "Bun installed — dashboard ready"
681
+ else
682
+ warn "Could not install Bun automatically"
683
+ echo -e " ${DIM}Install manually: curl -fsSL https://bun.sh/install | bash${RESET}"
684
+ echo -e " ${DIM}The dashboard requires Bun to run: shipwright dashboard${RESET}"
685
+ fi
686
+ fi
687
+
688
+ # ─── Dashboard Files ──────────────────────────────────────────────────────
689
+ # Copy dashboard files to the install location for non-source installs
690
+ DASHBOARD_SRC="$REPO_DIR/dashboard"
691
+ DASHBOARD_DEST="$HOME/.local/share/shipwright/dashboard"
692
+ if [[ -f "$DASHBOARD_SRC/server.ts" ]]; then
693
+ mkdir -p "$DASHBOARD_DEST/public"
694
+ cp "$DASHBOARD_SRC/server.ts" "$DASHBOARD_DEST/server.ts"
695
+ for _f in "$DASHBOARD_SRC/public"/*; do
696
+ [[ -f "$_f" ]] && cp "$_f" "$DASHBOARD_DEST/public/$(basename "$_f")"
697
+ done
698
+ success "Dashboard files installed → ~/.local/share/shipwright/dashboard/"
699
+ fi
700
+
598
701
  # ─── Validation ───────────────────────────────────────────────────────────────
599
702
  echo ""
600
703
  echo -e "${CYAN}${BOLD}Running doctor...${RESET}"
@@ -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
 
@@ -200,6 +200,7 @@ cmd_start() {
200
200
  local run_file="${INSTRUMENT_ACTIVE}/${run_id}.json"
201
201
  local tmp_file
202
202
  tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
203
+ trap "rm -f '$tmp_file'" RETURN
203
204
 
204
205
  # Get repo info if not provided
205
206
  if [[ "$repo" == "." ]]; then
@@ -266,6 +267,7 @@ cmd_record() {
266
267
 
267
268
  local tmp_file
268
269
  tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
270
+ trap "rm -f '$tmp_file'" RETURN
269
271
 
270
272
  # Parse value as number if it's numeric
271
273
  local value_json
@@ -328,6 +330,7 @@ cmd_stage_start() {
328
330
 
329
331
  local tmp_file
330
332
  tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
333
+ trap "rm -f '$tmp_file'" RETURN
331
334
 
332
335
  jq \
333
336
  --arg stage "$stage" \
@@ -373,6 +376,7 @@ cmd_stage_end() {
373
376
 
374
377
  local tmp_file
375
378
  tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
379
+ trap "rm -f '$tmp_file'" RETURN
376
380
 
377
381
  jq \
378
382
  --arg stage "$stage" \
@@ -422,6 +426,7 @@ cmd_finish() {
422
426
 
423
427
  local tmp_file
424
428
  tmp_file="$(mktemp "${INSTRUMENT_ACTIVE}/.tmp.XXXXXX")"
429
+ trap "rm -f '$tmp_file'" RETURN
425
430
 
426
431
  # Update run record with finish data
427
432
  jq \
@@ -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="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
12
12
 
@@ -75,6 +75,7 @@ _intelligence_track_cache_access() {
75
75
 
76
76
  local tmp_file
77
77
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-cache-stats.XXXXXX")
78
+ trap "rm -f '$tmp_file'" RETURN
78
79
  if [[ "$hit_or_miss" == "hit" ]]; then
79
80
  jq '.hits += 1 | .total += 1' "$CACHE_STATS_FILE" > "$tmp_file" && mv "$tmp_file" "$CACHE_STATS_FILE" || rm -f "$tmp_file"
80
81
  else
@@ -118,6 +119,7 @@ _intelligence_adjust_cache_ttl() {
118
119
  if [[ "$new_ttl" != "$current_ttl" ]]; then
119
120
  local tmp_file
120
121
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-cache-ttl.XXXXXX")
122
+ trap "rm -f '$tmp_file'" RETURN
121
123
  jq -n \
122
124
  --argjson ttl "$new_ttl" \
123
125
  --argjson miss_rate "$miss_rate" \
@@ -134,6 +136,7 @@ _intelligence_adjust_cache_ttl() {
134
136
  # Reset stats for next window
135
137
  local tmp_reset
136
138
  tmp_reset=$(mktemp "${TMPDIR:-/tmp}/sw-cache-reset.XXXXXX")
139
+ trap "rm -f '$tmp_reset'" RETURN
137
140
  echo '{"hits":0,"misses":0,"total":0}' > "$tmp_reset" && mv "$tmp_reset" "$CACHE_STATS_FILE" || rm -f "$tmp_reset"
138
141
  }
139
142
 
@@ -212,6 +215,7 @@ _intelligence_cache_set() {
212
215
 
213
216
  local tmp_file
214
217
  tmp_file=$(mktemp "${TMPDIR:-/tmp}/sw-intel-cache.XXXXXX")
218
+ trap "rm -f '$tmp_file'" RETURN
215
219
  jq --arg h "$hash" \
216
220
  --argjson result "$result" \
217
221
  --argjson ts "$now" \
@@ -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
 
@@ -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
 
@@ -48,12 +48,14 @@ RESET="${RESET:-\033[0m}"
48
48
  CONFIG_DIR="${HOME}/.shipwright"
49
49
  LINEAR_CONFIG="${CONFIG_DIR}/linear-config.json"
50
50
 
51
- # Linear Status IDs (Sethdford team)
52
- STATUS_BACKLOG="147eb91d-0428-457b-bdcb-0875b847b061"
53
- STATUS_TODO="f89d423b-9cad-4e60-aec9-422b64b78a4b"
54
- STATUS_IN_PROGRESS="7ed39c42-434d-4239-86f3-ffa24dbf1275"
55
- STATUS_IN_REVIEW="ea38a4f2-f0ee-4e0b-ae45-7e5aad45ef53"
56
- STATUS_DONE="dc2430cf-0713-40c9-a8c6-889413b626e7"
51
+ # Linear Status IDs loaded from config, with env var overrides
52
+ # Configure via: shipwright tracker init --provider linear
53
+ # Or set env vars: LINEAR_STATUS_BACKLOG, LINEAR_STATUS_TODO, etc.
54
+ STATUS_BACKLOG="${LINEAR_STATUS_BACKLOG:-}"
55
+ STATUS_TODO="${LINEAR_STATUS_TODO:-}"
56
+ STATUS_IN_PROGRESS="${LINEAR_STATUS_IN_PROGRESS:-}"
57
+ STATUS_IN_REVIEW="${LINEAR_STATUS_IN_REVIEW:-}"
58
+ STATUS_DONE="${LINEAR_STATUS_DONE:-}"
57
59
 
58
60
  LINEAR_API="https://api.linear.app/graphql"
59
61
 
@@ -65,8 +67,17 @@ load_config() {
65
67
  fi
66
68
 
67
69
  LINEAR_API_KEY="${LINEAR_API_KEY:-}"
68
- LINEAR_TEAM_ID="${LINEAR_TEAM_ID:-83deb533-69d2-43ef-bc58-eadb6e72a8f2}"
69
- LINEAR_PROJECT_ID="${LINEAR_PROJECT_ID:-b262d625-5bbe-47bd-9f89-df27c45eba8b}"
70
+ LINEAR_TEAM_ID="${LINEAR_TEAM_ID:-$(jq -r '.team_id // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
71
+ LINEAR_PROJECT_ID="${LINEAR_PROJECT_ID:-$(jq -r '.project_id // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
72
+
73
+ # Load status IDs from config if not set via env
74
+ if [[ -f "$LINEAR_CONFIG" ]]; then
75
+ STATUS_BACKLOG="${STATUS_BACKLOG:-$(jq -r '.status_ids.backlog // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
76
+ STATUS_TODO="${STATUS_TODO:-$(jq -r '.status_ids.todo // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
77
+ STATUS_IN_PROGRESS="${STATUS_IN_PROGRESS:-$(jq -r '.status_ids.in_progress // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
78
+ STATUS_IN_REVIEW="${STATUS_IN_REVIEW:-$(jq -r '.status_ids.in_review // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
79
+ STATUS_DONE="${STATUS_DONE:-$(jq -r '.status_ids.done // empty' "$LINEAR_CONFIG" 2>/dev/null || true)}"
80
+ fi
70
81
  }
71
82
 
72
83
  check_api_key() {
@@ -4,7 +4,7 @@
4
4
  # ║ ║
5
5
  # ║ Captures tmux pane scrollback and provides log browsing/search. ║
6
6
  # ╚═══════════════════════════════════════════════════════════════════════════╝
7
- VERSION="2.2.2"
7
+ VERSION="2.3.1"
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10
 
@@ -71,7 +71,7 @@ MAX_RESTARTS=0
71
71
  SESSION_RESTART=false
72
72
  RESTART_COUNT=0
73
73
  REPO_OVERRIDE=""
74
- VERSION="2.2.2"
74
+ VERSION="2.3.1"
75
75
 
76
76
  # ─── Token Tracking ─────────────────────────────────────────────────────────
77
77
  LOOP_INPUT_TOKENS=0
@@ -531,6 +531,7 @@ write_loop_tokens() {
531
531
  fi
532
532
  local tmp_file
533
533
  tmp_file=$(mktemp "${token_file}.XXXXXX" 2>/dev/null || mktemp)
534
+ trap "rm -f '$tmp_file'" RETURN
534
535
  cat > "$tmp_file" <<TOKJSON
535
536
  {"input_tokens":${LOOP_INPUT_TOKENS},"output_tokens":${LOOP_OUTPUT_TOKENS},"cost_usd":${cost_usd},"iterations":${ITERATION:-0}}
536
537
  TOKJSON
@@ -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="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
12
12
 
@@ -150,6 +150,7 @@ memory_capture_pipeline() {
150
150
  # Record review patterns to global memory for cross-repo learning
151
151
  local tmp_global
152
152
  tmp_global=$(mktemp)
153
+ trap "rm -f '$tmp_global'" RETURN
153
154
  jq --arg repo "$repo" \
154
155
  --arg ts "$captured_at" \
155
156
  --argjson bugs "${bug_count:-0}" \
@@ -211,6 +212,7 @@ memory_capture_failure() {
211
212
  fi
212
213
  local tmp_file
213
214
  tmp_file=$(mktemp "${failures_file}.tmp.XXXXXX")
215
+ trap "rm -f '$tmp_file'" EXIT
214
216
 
215
217
  if [[ "$existing_idx" != "-1" && "$existing_idx" != "null" ]]; then
216
218
  # Update existing entry
@@ -277,6 +279,7 @@ memory_record_fix_outcome() {
277
279
  fi
278
280
  local tmp_file
279
281
  tmp_file=$(mktemp "${failures_file}.tmp.XXXXXX")
282
+ trap "rm -f '$tmp_file'" EXIT
280
283
 
281
284
  jq --argjson idx "$match_idx" \
282
285
  --argjson app "$applied_inc" \
@@ -442,6 +445,7 @@ _memory_aggregate_global() {
442
445
  # Add to global, cap at 100 entries
443
446
  local tmp_global
444
447
  tmp_global=$(mktemp "${global_file}.tmp.XXXXXX")
448
+ trap "rm -f '$tmp_global'" RETURN
445
449
  jq --arg p "$pattern" \
446
450
  --arg ts "$(now_iso)" \
447
451
  --arg cat "general" \
@@ -593,6 +597,7 @@ Return JSON only, no markdown fences, no explanation."
593
597
  # Update the most recent failure entry with root_cause, fix, category
594
598
  local tmp_file
595
599
  tmp_file=$(mktemp)
600
+ trap "rm -f '$tmp_file'" RETURN
596
601
  jq --arg rc "$root_cause" \
597
602
  --arg fix "$fix" \
598
603
  --arg cat "$category" \
@@ -622,6 +627,7 @@ memory_capture_pattern() {
622
627
 
623
628
  local tmp_file
624
629
  tmp_file=$(mktemp)
630
+ trap "rm -f '$tmp_file'" RETURN
625
631
 
626
632
  case "$pattern_type" in
627
633
  project)
@@ -1112,6 +1118,7 @@ memory_update_metrics() {
1112
1118
  # Update baseline using atomic write
1113
1119
  local tmp_file
1114
1120
  tmp_file=$(mktemp)
1121
+ trap "rm -f '$tmp_file'" RETURN
1115
1122
  jq --arg m "$metric_name" \
1116
1123
  --argjson v "$value" \
1117
1124
  --arg ts "$(now_iso)" \
@@ -1135,6 +1142,7 @@ memory_capture_decision() {
1135
1142
 
1136
1143
  local tmp_file
1137
1144
  tmp_file=$(mktemp)
1145
+ trap "rm -f '$tmp_file'" RETURN
1138
1146
  jq --arg type "$dec_type" \
1139
1147
  --arg summary "$summary" \
1140
1148
  --arg detail "$detail" \
@@ -1454,6 +1462,7 @@ memory_import() {
1454
1462
  # Extract and write each section
1455
1463
  local tmp_file
1456
1464
  tmp_file=$(mktemp)
1465
+ trap "rm -f '$tmp_file'" RETURN
1457
1466
 
1458
1467
  jq '.patterns // {}' "$import_file" > "$tmp_file" && mv "$tmp_file" "$mem_dir/patterns.json"
1459
1468
  jq '.failures // {"failures":[]}' "$import_file" > "$tmp_file" && mv "$tmp_file" "$mem_dir/failures.json"
@@ -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