shipwright-cli 2.0.0 → 2.1.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 (113) hide show
  1. package/README.md +160 -72
  2. package/completions/_shipwright +59 -7
  3. package/completions/shipwright.bash +24 -4
  4. package/completions/shipwright.fish +80 -2
  5. package/dashboard/server.ts +208 -0
  6. package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
  7. package/docs/tmux-research/TMUX-AUDIT.md +925 -0
  8. package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
  9. package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
  10. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
  11. package/package.json +2 -2
  12. package/scripts/lib/helpers.sh +7 -0
  13. package/scripts/sw +116 -2
  14. package/scripts/sw-activity.sh +1 -1
  15. package/scripts/sw-adaptive.sh +1 -1
  16. package/scripts/sw-adversarial.sh +1 -1
  17. package/scripts/sw-architecture-enforcer.sh +1 -1
  18. package/scripts/sw-auth.sh +1 -1
  19. package/scripts/sw-autonomous.sh +128 -38
  20. package/scripts/sw-changelog.sh +1 -1
  21. package/scripts/sw-checkpoint.sh +1 -1
  22. package/scripts/sw-ci.sh +1 -1
  23. package/scripts/sw-cleanup.sh +1 -1
  24. package/scripts/sw-code-review.sh +62 -1
  25. package/scripts/sw-connect.sh +1 -1
  26. package/scripts/sw-context.sh +1 -1
  27. package/scripts/sw-cost.sh +44 -3
  28. package/scripts/sw-daemon.sh +155 -27
  29. package/scripts/sw-dashboard.sh +1 -1
  30. package/scripts/sw-db.sh +958 -118
  31. package/scripts/sw-decompose.sh +1 -1
  32. package/scripts/sw-deps.sh +1 -1
  33. package/scripts/sw-developer-simulation.sh +1 -1
  34. package/scripts/sw-discovery.sh +1 -1
  35. package/scripts/sw-docs-agent.sh +1 -1
  36. package/scripts/sw-docs.sh +1 -1
  37. package/scripts/sw-doctor.sh +49 -1
  38. package/scripts/sw-dora.sh +1 -1
  39. package/scripts/sw-durable.sh +1 -1
  40. package/scripts/sw-e2e-orchestrator.sh +1 -1
  41. package/scripts/sw-eventbus.sh +1 -1
  42. package/scripts/sw-feedback.sh +23 -15
  43. package/scripts/sw-fix.sh +1 -1
  44. package/scripts/sw-fleet-discover.sh +1 -1
  45. package/scripts/sw-fleet-viz.sh +1 -1
  46. package/scripts/sw-fleet.sh +1 -1
  47. package/scripts/sw-github-app.sh +1 -1
  48. package/scripts/sw-github-checks.sh +4 -4
  49. package/scripts/sw-github-deploy.sh +1 -1
  50. package/scripts/sw-github-graphql.sh +1 -1
  51. package/scripts/sw-guild.sh +1 -1
  52. package/scripts/sw-heartbeat.sh +1 -1
  53. package/scripts/sw-hygiene.sh +1 -1
  54. package/scripts/sw-incident.sh +45 -6
  55. package/scripts/sw-init.sh +150 -24
  56. package/scripts/sw-instrument.sh +1 -1
  57. package/scripts/sw-intelligence.sh +1 -1
  58. package/scripts/sw-jira.sh +1 -1
  59. package/scripts/sw-launchd.sh +1 -1
  60. package/scripts/sw-linear.sh +1 -1
  61. package/scripts/sw-logs.sh +1 -1
  62. package/scripts/sw-loop.sh +204 -19
  63. package/scripts/sw-memory.sh +18 -1
  64. package/scripts/sw-mission-control.sh +1 -1
  65. package/scripts/sw-model-router.sh +1 -1
  66. package/scripts/sw-otel.sh +1 -1
  67. package/scripts/sw-oversight.sh +76 -1
  68. package/scripts/sw-pipeline-composer.sh +1 -1
  69. package/scripts/sw-pipeline-vitals.sh +1 -1
  70. package/scripts/sw-pipeline.sh +261 -12
  71. package/scripts/sw-pm.sh +70 -5
  72. package/scripts/sw-pr-lifecycle.sh +1 -1
  73. package/scripts/sw-predictive.sh +8 -1
  74. package/scripts/sw-prep.sh +1 -1
  75. package/scripts/sw-ps.sh +1 -1
  76. package/scripts/sw-public-dashboard.sh +1 -1
  77. package/scripts/sw-quality.sh +1 -1
  78. package/scripts/sw-reaper.sh +1 -1
  79. package/scripts/sw-recruit.sh +1853 -178
  80. package/scripts/sw-regression.sh +1 -1
  81. package/scripts/sw-release-manager.sh +1 -1
  82. package/scripts/sw-release.sh +1 -1
  83. package/scripts/sw-remote.sh +1 -1
  84. package/scripts/sw-replay.sh +1 -1
  85. package/scripts/sw-retro.sh +1 -1
  86. package/scripts/sw-scale.sh +1 -1
  87. package/scripts/sw-security-audit.sh +1 -1
  88. package/scripts/sw-self-optimize.sh +1 -1
  89. package/scripts/sw-session.sh +1 -1
  90. package/scripts/sw-setup.sh +263 -127
  91. package/scripts/sw-standup.sh +1 -1
  92. package/scripts/sw-status.sh +44 -2
  93. package/scripts/sw-strategic.sh +189 -41
  94. package/scripts/sw-stream.sh +1 -1
  95. package/scripts/sw-swarm.sh +42 -5
  96. package/scripts/sw-team-stages.sh +1 -1
  97. package/scripts/sw-templates.sh +4 -4
  98. package/scripts/sw-testgen.sh +66 -15
  99. package/scripts/sw-tmux-pipeline.sh +1 -1
  100. package/scripts/sw-tmux-role-color.sh +58 -0
  101. package/scripts/sw-tmux-status.sh +128 -0
  102. package/scripts/sw-tmux.sh +1 -1
  103. package/scripts/sw-trace.sh +1 -1
  104. package/scripts/sw-tracker.sh +1 -1
  105. package/scripts/sw-triage.sh +61 -37
  106. package/scripts/sw-upgrade.sh +1 -1
  107. package/scripts/sw-ux.sh +1 -1
  108. package/scripts/sw-webhook.sh +1 -1
  109. package/scripts/sw-widgets.sh +1 -1
  110. package/scripts/sw-worktree.sh +1 -1
  111. package/templates/pipelines/autonomous.json +2 -2
  112. package/tmux/shipwright-overlay.conf +35 -17
  113. package/tmux/tmux.conf +23 -21
@@ -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="1.13.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Handle subcommands ───────────────────────────────────────────────────────
@@ -208,8 +208,12 @@ EOF
208
208
  done
209
209
  echo ""
210
210
 
211
- # Generate test template
211
+ # Generate test template; use Claude for real assertions when available
212
212
  local test_template_file="$TESTGEN_DIR/generated-tests.sh"
213
+ local use_claude="false"
214
+ command -v claude &>/dev/null && use_claude="true"
215
+ [[ "${TESTGEN_USE_CLAUDE:-true}" == "false" ]] && use_claude="false"
216
+
213
217
  {
214
218
  echo "#!/usr/bin/env bash"
215
219
  echo "# Generated tests for $target_script"
@@ -217,24 +221,71 @@ EOF
217
221
  echo "trap 'echo \"ERROR: \$BASH_SOURCE:\$LINENO exited with status \$?\" >&2' ERR"
218
222
  echo ""
219
223
  echo "SCRIPT_DIR=\"\$(cd \"\$(dirname \"\${BASH_SOURCE[0]}\")\" && pwd)\""
224
+ echo "REPO_DIR=\"\$(cd \"\$SCRIPT_DIR/..\" && pwd)\""
225
+ echo ""
226
+ echo "# Helpers: assert equal (or contains) so tests fail when behavior is wrong"
227
+ echo "assert_equal() { local e=\"\$1\" a=\"\$2\"; if [[ \"\$a\" != \"\$e\" ]]; then echo \"Expected: \$e\"; echo \"Actual: \$a\"; exit 1; fi; }"
228
+ echo "assert_contains() { local sub=\"\$1\" full=\"\$2\"; if [[ \"\$full\" != *\"\$sub\"* ]]; then echo \"Expected to contain: \$sub\"; echo \"In: \$full\"; exit 1; fi; }"
220
229
  echo ""
221
- echo "# Test counters"
222
230
  echo "PASS=0"
223
231
  echo "FAIL=0"
224
232
  echo ""
233
+ } > "$test_template_file"
225
234
 
226
- echo "$untested_functions" | while IFS= read -r func; do
227
- [[ -z "$func" ]] && continue
228
- echo "test_${func}() {"
229
- echo " # TODO: Implement test for $func"
230
- echo " # - Test happy path"
231
- echo " # - Test error cases"
232
- echo " # - Test edge cases"
233
- echo " ((PASS++))"
234
- echo "}"
235
+ local func_count=0
236
+ while IFS= read -r func; do
237
+ [[ -z "$func" ]] && continue
238
+ func_count=$((func_count + 1))
239
+ {
240
+ if [[ "$use_claude" == "true" ]]; then
241
+ local func_snippet
242
+ func_snippet=$(awk "/^${func}\(\\)/,/^[a-zA-Z_][a-zA-Z0-9_]*\(\)|^$/" "$target_script" 2>/dev/null | head -40 || true)
243
+ local prompt_file
244
+ prompt_file=$(mktemp "${TMPDIR:-/tmp}/sw-testgen-prompt.XXXXXX")
245
+ {
246
+ 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."
247
+ echo "Function name: ${func}"
248
+ echo "Function body:"
249
+ echo "$func_snippet"
250
+ } > "$prompt_file"
251
+ local claude_out
252
+ # Read prompt through pipe to avoid shell expansion of $vars in function body
253
+ claude_out=$(cat "$prompt_file" | claude -p --max-turns 2 2>/dev/null || true)
254
+ rm -f "$prompt_file"
255
+ if [[ -n "$claude_out" ]]; then
256
+ local code_block
257
+ code_block=$(echo "$claude_out" | sed -n '/^test_'"${func}"'()/,/^}/p' || echo "$claude_out" | sed -n '/^test_/,/^}/p' || true)
258
+ [[ -z "$code_block" ]] && code_block="$claude_out"
259
+ if echo "$code_block" | grep -qE 'assert_equal|assert_contains|\[\[.*\]\]|exit 1'; then
260
+ echo "test_${func}() {"
261
+ echo "$code_block" | sed 's/^test_'"${func}"'()//' | sed 's/^{//' | sed 's/^}//' | head -50
262
+ echo " ((PASS++))"
263
+ echo "}"
264
+ else
265
+ echo "test_${func}() {"
266
+ echo " # Claude-generated; review assertions"
267
+ echo "$code_block" | head -30 | sed 's/^/ /'
268
+ echo " ((PASS++))"
269
+ echo "}"
270
+ fi
271
+ else
272
+ echo "test_${func}() { # TODO: Claude unavailable"
273
+ echo " ((PASS++))"
274
+ echo "}"
275
+ fi
276
+ else
277
+ echo "test_${func}() {"
278
+ echo " # TODO: Implement test for $func"
279
+ echo " ((PASS++))"
280
+ echo "}"
281
+ fi
235
282
  echo ""
236
- done
283
+ } >> "$test_template_file"
284
+ done << EOF
285
+ $untested_functions
286
+ EOF
237
287
 
288
+ {
238
289
  echo "# Run all tests"
239
290
  echo "$untested_functions" | while IFS= read -r func; do
240
291
  [[ -z "$func" ]] && continue
@@ -242,11 +293,11 @@ EOF
242
293
  done
243
294
  echo ""
244
295
  echo "echo \"Results: \$PASS passed, \$FAIL failed\""
245
- } > "$test_template_file"
296
+ } >> "$test_template_file"
246
297
 
247
298
  chmod +x "$test_template_file"
248
299
  success "Generated test template: $test_template_file"
249
- info "Review and implement test logic, then move to test suite"
300
+ [[ "$use_claude" == "true" ]] && info "Used Claude for assertions; review and run tests to validate"
250
301
  }
251
302
 
252
303
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -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.0.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ sw-tmux-role-color.sh — Set pane border color by agent role ║
4
+ # ║ ║
5
+ # ║ Called from a tmux hook (after-select-pane) or manually. ║
6
+ # ║ Reads #{pane_title} and sets the active border color to match the ║
7
+ # ║ agent's role. Falls back to cyan (#00d4ff) for unknown roles. ║
8
+ # ║ ║
9
+ # ║ Role → Color mapping: ║
10
+ # ║ leader/pm → #00d4ff (cyan) — command & control ║
11
+ # ║ builder/dev → #0066ff (blue) — implementation ║
12
+ # ║ reviewer → #f97316 (orange) — scrutiny ║
13
+ # ║ tester → #facc15 (yellow) — validation ║
14
+ # ║ security → #ef4444 (red) — vigilance ║
15
+ # ║ docs/writer → #a78bfa (violet) — documentation ║
16
+ # ║ optimizer → #4ade80 (green) — performance ║
17
+ # ║ researcher → #7c3aed (purple) — exploration ║
18
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
19
+ set -euo pipefail
20
+
21
+ # Get the active pane's title
22
+ PANE_TITLE="$(tmux display-message -p '#{pane_title}' 2>/dev/null || echo "")"
23
+
24
+ # Normalize to lowercase for matching
25
+ TITLE_LOWER="$(echo "$PANE_TITLE" | tr '[:upper:]' '[:lower:]')"
26
+
27
+ # Map role keywords to colors
28
+ COLOR="#00d4ff" # default: cyan
29
+
30
+ case "$TITLE_LOWER" in
31
+ *leader*|*lead*|*pm*|*manager*|*orchestrat*)
32
+ COLOR="#00d4ff" # cyan — command & control
33
+ ;;
34
+ *build*|*dev*|*implement*|*code*|*engineer*)
35
+ COLOR="#0066ff" # blue — implementation
36
+ ;;
37
+ *review*|*audit*|*inspect*|*oversight*)
38
+ COLOR="#f97316" # orange — scrutiny
39
+ ;;
40
+ *test*|*qa*|*validat*|*verify*)
41
+ COLOR="#facc15" # yellow — validation
42
+ ;;
43
+ *secur*|*vuln*|*threat*|*pentest*)
44
+ COLOR="#ef4444" # red — vigilance
45
+ ;;
46
+ *doc*|*writ*|*readme*|*changelog*)
47
+ COLOR="#a78bfa" # violet — documentation
48
+ ;;
49
+ *optim*|*perf*|*speed*|*deploy*)
50
+ COLOR="#4ade80" # green — performance/deploy
51
+ ;;
52
+ *research*|*explor*|*investigat*|*analyz*)
53
+ COLOR="#7c3aed" # purple — exploration
54
+ ;;
55
+ esac
56
+
57
+ # Set the active pane border color
58
+ tmux set -g pane-active-border-style "fg=${COLOR},bg=#1a1a2e" 2>/dev/null || true
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ sw-tmux-status.sh — Status bar widgets for tmux ║
4
+ # ║ ║
5
+ # ║ Called by tmux via #() in status-right. Must be FAST (<100ms). ║
6
+ # ║ Reads pipeline state from .claude/pipeline-state.md and heartbeats ║
7
+ # ║ from ~/.shipwright/heartbeats/. Outputs styled tmux format strings. ║
8
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
9
+ set -euo pipefail
10
+
11
+ # ─── Stage colors (match Shipwright brand palette) ────────────────────────
12
+ # Each pipeline stage gets a distinct color for instant visual recognition
13
+ stage_color() {
14
+ case "${1:-}" in
15
+ intake) echo "#71717a" ;; # muted — gathering
16
+ plan) echo "#7c3aed" ;; # purple — thinking
17
+ design) echo "#7c3aed" ;; # purple — thinking
18
+ build) echo "#0066ff" ;; # blue — working
19
+ test) echo "#facc15" ;; # yellow — validating
20
+ review) echo "#f97316" ;; # orange — scrutinizing
21
+ compound_quality) echo "#f97316" ;; # orange — scrutinizing
22
+ pr) echo "#00d4ff" ;; # cyan — shipping
23
+ merge) echo "#00d4ff" ;; # cyan — shipping
24
+ deploy) echo "#4ade80" ;; # green — deploying
25
+ validate) echo "#4ade80" ;; # green — verifying
26
+ monitor) echo "#4ade80" ;; # green — watching
27
+ *) echo "#71717a" ;; # muted fallback
28
+ esac
29
+ }
30
+
31
+ # ─── Stage icons ──────────────────────────────────────────────────────────
32
+ stage_icon() {
33
+ case "${1:-}" in
34
+ intake) echo "◇" ;;
35
+ plan) echo "◆" ;;
36
+ design) echo "△" ;;
37
+ build) echo "⚙" ;;
38
+ test) echo "⚡" ;;
39
+ review) echo "◎" ;;
40
+ compound_quality) echo "◎" ;;
41
+ pr) echo "↑" ;;
42
+ merge) echo "⊕" ;;
43
+ deploy) echo "▲" ;;
44
+ validate) echo "✦" ;;
45
+ monitor) echo "◉" ;;
46
+ *) echo "·" ;;
47
+ esac
48
+ }
49
+
50
+ # ─── Pipeline stage widget ────────────────────────────────────────────────
51
+ # Reads current pipeline stage from state file, outputs tmux format string
52
+ pipeline_widget() {
53
+ local state_file=".claude/pipeline-state.md"
54
+
55
+ # Try current directory, then walk up to find repo root
56
+ if [[ ! -f "$state_file" ]]; then
57
+ local dir
58
+ dir="$(pwd)"
59
+ while [[ "$dir" != "/" ]]; do
60
+ if [[ -f "$dir/$state_file" ]]; then
61
+ state_file="$dir/$state_file"
62
+ break
63
+ fi
64
+ dir="$(dirname "$dir")"
65
+ done
66
+ fi
67
+
68
+ [[ -f "$state_file" ]] || return 0
69
+
70
+ # Extract current stage — look for "Stage:" or "## Stage:" pattern
71
+ local stage=""
72
+ stage="$(grep -iE '^\*?\*?(current )?stage:?\*?\*?' "$state_file" 2>/dev/null | head -1 | sed 's/.*: *//' | tr -d '*' | tr '[:upper:]' '[:lower:]' | tr -d ' ')" || true
73
+
74
+ [[ -n "$stage" ]] || return 0
75
+
76
+ local color icon
77
+ color="$(stage_color "$stage")"
78
+ icon="$(stage_icon "$stage")"
79
+ local label
80
+ label="$(echo "$stage" | tr '[:lower:]' '[:upper:]')"
81
+
82
+ # Output: colored badge with icon
83
+ echo "#[fg=#1e1e32,bg=${color},bold] ${icon} ${label} #[fg=${color},bg=#1a1a2e]"
84
+ }
85
+
86
+ # ─── Agent count widget ──────────────────────────────────────────────────
87
+ # Shows number of active agents from heartbeat files
88
+ agent_widget() {
89
+ local hb_dir="${HOME}/.shipwright/heartbeats"
90
+ [[ -d "$hb_dir" ]] || return 0
91
+
92
+ local now count=0
93
+ now="$(date +%s)"
94
+
95
+ for hb in "$hb_dir"/*.json; do
96
+ [[ -f "$hb" ]] || continue
97
+ # Heartbeat is alive if updated within last 60 seconds
98
+ local mtime
99
+ if [[ "$(uname)" == "Darwin" ]]; then
100
+ mtime="$(stat -f %m "$hb" 2>/dev/null || echo 0)"
101
+ else
102
+ mtime="$(stat -c %Y "$hb" 2>/dev/null || echo 0)"
103
+ fi
104
+ if (( now - mtime < 60 )); then
105
+ count=$((count + 1))
106
+ fi
107
+ done
108
+
109
+ if [[ $count -gt 0 ]]; then
110
+ echo "#[fg=#1e1e32,bg=#7c3aed,bold] λ${count} #[fg=#7c3aed,bg=#1a1a2e]"
111
+ fi
112
+ }
113
+
114
+ # ─── Dispatch ─────────────────────────────────────────────────────────────
115
+ case "${1:-pipeline}" in
116
+ pipeline) pipeline_widget ;;
117
+ agents) agent_widget ;;
118
+ all)
119
+ # Combine both widgets
120
+ local p a
121
+ p="$(pipeline_widget)"
122
+ a="$(agent_widget)"
123
+ echo "${a}${p}"
124
+ ;;
125
+ *)
126
+ echo ""
127
+ ;;
128
+ esac
@@ -11,7 +11,7 @@
11
11
  # ║ shipwright tmux fix — Auto-fix common issues ║
12
12
  # ║ shipwright tmux reload — Reload tmux config ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="2.0.0"
14
+ VERSION="2.1.0"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17
 
@@ -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.0.0"
9
+ VERSION="2.1.0"
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.0.0"
9
+ VERSION="2.1.0"
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.0.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -403,40 +403,63 @@ cmd_team() {
403
403
  risk=$(echo "$analysis" | jq -r '.risk')
404
404
  effort=$(echo "$analysis" | jq -r '.effort')
405
405
 
406
- # Recommend based on complexity/risk
407
- local template model max_iterations agents
408
- case "${complexity}-${risk}" in
409
- trivial-low|simple-low)
410
- template="fast"
411
- model="haiku"
412
- max_iterations=2
413
- agents=1
414
- ;;
415
- simple-*|moderate-low)
416
- template="standard"
417
- model="sonnet"
418
- max_iterations=5
419
- agents=2
420
- ;;
421
- moderate-*|complex-low)
422
- template="standard"
423
- model="sonnet"
424
- max_iterations=8
425
- agents=3
426
- ;;
427
- complex-*|epic-*)
428
- template="full"
429
- model="opus"
430
- max_iterations=15
431
- agents=4
432
- ;;
433
- *)
434
- template="standard"
435
- model="sonnet"
436
- max_iterations=5
437
- agents=2
438
- ;;
439
- esac
406
+ # ── Try recruit-powered team composition first ──
407
+ local template="" model="" max_iterations="" agents=""
408
+ local recruit_source="heuristic"
409
+ if [[ -x "${SCRIPT_DIR:-}/sw-recruit.sh" ]]; then
410
+ local issue_title
411
+ issue_title=$(gh issue view "$issue" --json title -q '.title' 2>/dev/null || echo "")
412
+ if [[ -n "$issue_title" ]]; then
413
+ local recruit_result
414
+ recruit_result=$(bash "$SCRIPT_DIR/sw-recruit.sh" team --json "$issue_title" 2>/dev/null) || true
415
+ if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team' &>/dev/null 2>&1; then
416
+ model=$(echo "$recruit_result" | jq -r '.model // "sonnet"')
417
+ agents=$(echo "$recruit_result" | jq -r '.agents // 2')
418
+ # Map agent count to template
419
+ if [[ "$agents" -ge 4 ]]; then template="full"; max_iterations=15;
420
+ elif [[ "$agents" -ge 3 ]]; then template="standard"; max_iterations=8;
421
+ elif [[ "$agents" -le 1 ]]; then template="fast"; max_iterations=2;
422
+ else template="standard"; max_iterations=5; fi
423
+ recruit_source="recruit"
424
+ fi
425
+ fi
426
+ fi
427
+
428
+ # ── Fallback: hardcoded complexity/risk mapping ──
429
+ if [[ -z "$template" ]]; then
430
+ case "${complexity}-${risk}" in
431
+ trivial-low|simple-low)
432
+ template="fast"
433
+ model="haiku"
434
+ max_iterations=2
435
+ agents=1
436
+ ;;
437
+ simple-*|moderate-low)
438
+ template="standard"
439
+ model="sonnet"
440
+ max_iterations=5
441
+ agents=2
442
+ ;;
443
+ moderate-*|complex-low)
444
+ template="standard"
445
+ model="sonnet"
446
+ max_iterations=8
447
+ agents=3
448
+ ;;
449
+ complex-*|epic-*)
450
+ template="full"
451
+ model="opus"
452
+ max_iterations=15
453
+ agents=4
454
+ ;;
455
+ *)
456
+ template="standard"
457
+ model="sonnet"
458
+ max_iterations=5
459
+ agents=2
460
+ ;;
461
+ esac
462
+ fi
440
463
 
441
464
  cat << EOF
442
465
  {
@@ -448,12 +471,13 @@ cmd_team() {
448
471
  "pipeline_template": "$template",
449
472
  "model": "$model",
450
473
  "max_iterations": $max_iterations,
451
- "agents": $agents
474
+ "agents": $agents,
475
+ "source": "$recruit_source"
452
476
  }
453
477
  }
454
478
  EOF
455
479
 
456
- emit_event "triage_team_recommended" "issue=$issue" "template=$template" "agents=$agents"
480
+ emit_event "triage_team_recommended" "issue=$issue" "template=$template" "agents=$agents" "source=$recruit_source"
457
481
  }
458
482
 
459
483
  # ─── Subcommand: batch ────────────────────────────────────────────────────
@@ -2,7 +2,7 @@
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
3
  # ║ sw upgrade — Detect and apply updates from the repo ║
4
4
  # ╚═══════════════════════════════════════════════════════════════════════════╝
5
- VERSION="2.0.0"
5
+ VERSION="2.1.0"
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
package/scripts/sw-ux.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.0.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -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.0.0"
9
+ VERSION="2.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
 
12
12
  # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
@@ -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="2.0.0"
11
+ VERSION="2.1.0"
12
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
13
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14
14
 
@@ -5,7 +5,7 @@
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="2.0.0"
8
+ VERSION="2.1.0"
9
9
  set -euo pipefail
10
10
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
11
11
 
@@ -66,8 +66,8 @@
66
66
  "merge_method": "squash",
67
67
  "wait_ci_timeout_s": 600,
68
68
  "auto_delete_branch": true,
69
- "auto_merge": true,
70
- "auto_approve": true
69
+ "auto_merge": false,
70
+ "auto_approve": false
71
71
  }
72
72
  },
73
73
  {
@@ -27,9 +27,10 @@
27
27
  set -g pane-border-status top
28
28
 
29
29
  # Format: pane index, agent name, running command, zoom indicator
30
- # Active pane gets cyan accent; inactive stays muted
30
+ # Active pane: bold + bright Inactive: muted, recessed
31
31
  # Uses #{pane_title} which each agent sets via OSC 2 escape sequence
32
- set -g pane-border-format "#{?pane_active,#[fg=#00d4ff]#[bold],#[fg=#71717a]} #P #{pane_title} #[fg=#333355]│#[default] #{?pane_active,#[fg=#e4e4e7],#[fg=#52525b]}#{pane_current_command} #{?window_zoomed_flag,#[fg=#facc15]#[bold]🔍 ZOOMED ,}"
32
+ # The dot gives a visual "alive" indicator on the active pane
33
+ set -g pane-border-format "#{?pane_active,#[fg=#00d4ff]#[bold] ● #P │ #{pane_title} #[fg=#52525b]│#[fg=#a1a1aa] #{pane_current_command}#{?window_zoomed_flag, #[fg=#facc15]#[bold]⊞ FOCUS,},#[fg=#52525b] #P │ #[fg=#71717a]#{pane_title} #[fg=#333355]│ #[fg=#52525b]#{pane_current_command}}"
33
34
 
34
35
  # ═══════════════════════════════════════════════════════════════════════════
35
36
  # LAYOUT — Optimize for multi-agent pane layouts
@@ -40,19 +41,35 @@ set -g pane-border-format "#{?pane_active,#[fg=#00d4ff]#[bold],#[fg=#71717a]} #P
40
41
  setw -g aggressive-resize on
41
42
 
42
43
  # ═══════════════════════════════════════════════════════════════════════════
43
- # DARK THEME DEFAULTS Ensure all panes get the dark background
44
+ # DARK THEME — Active pane gets a subtle background lift
44
45
  # ═══════════════════════════════════════════════════════════════════════════
45
- # These session-level styles apply to ALL panes, even those created by
46
- # external tools (Claude Code agent spawning, scripts, etc.)
47
- set -g window-style 'bg=#1a1a2e,fg=#e4e4e7'
48
- set -g window-active-style 'bg=#1a1a2e,fg=#e4e4e7'
46
+ # Inactive panes: deep navy, dimmed text recedes visually
47
+ # Active pane: subtle bg lift, softer warm gray text — easy on the eyes
48
+ # NOTE: Claude Code renders its own colors for TUI elements. These fg values
49
+ # only affect plain terminal output (shell prompts, command output, etc.)
50
+ # The bg lift (#1a1a2e → #1e1e36) gives the active pane a "floating" feel.
51
+ set -g window-style 'bg=#1a1a2e,fg=#8888a0'
52
+ set -g window-active-style 'bg=#1e1e36,fg=#b4b4c8'
49
53
 
50
- # Hooks provide belt-and-suspenders coverage: explicitly set per-pane
51
- # colors whenever a pane is created, preventing white flash on creation.
52
- # Also re-enable allow-passthrough per-pane (some tmux versions reset it).
53
- set-hook -g after-split-window "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'"
54
- set-hook -g after-new-window "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'"
55
- set-hook -g after-new-session "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'"
54
+ # Hooks: set pane colors on creation to prevent white flash.
55
+ # New panes start with inactive colors; active pane lift applies automatically.
56
+ set-hook -g after-split-window "select-pane -P 'bg=#1a1a2e,fg=#8888a0'"
57
+ set-hook -g after-new-window "select-pane -P 'bg=#1a1a2e,fg=#8888a0'"
58
+ set-hook -g after-new-session "select-pane -P 'bg=#1a1a2e,fg=#8888a0'"
59
+
60
+ # ═══════════════════════════════════════════════════════════════════════════
61
+ # ROLE-COLORED BORDERS — Active pane border reflects agent role
62
+ # ═══════════════════════════════════════════════════════════════════════════
63
+ # When you switch panes, a hook reads the pane title and sets the active
64
+ # border color to match the agent's role:
65
+ # leader/pm → cyan (#00d4ff) builder/dev → blue (#0066ff)
66
+ # reviewer → orange (#f97316) tester → yellow (#facc15)
67
+ # security → red (#ef4444) docs → violet (#a78bfa)
68
+ # optimizer → green (#4ade80) researcher → purple (#7c3aed)
69
+ #
70
+ # The hook script must exist; -q makes source-file silent if missing.
71
+ # Falls back to cyan for non-agent panes (plain shell, vim, etc.)
72
+ set-hook -g pane-focus-in "run-shell -b 'bash ~/.shipwright/scripts/sw-tmux-role-color.sh 2>/dev/null || true'"
56
73
 
57
74
  # ═══════════════════════════════════════════════════════════════════════════
58
75
  # SAFETY — Confirm before killing agent panes
@@ -88,13 +105,14 @@ bind M-t setw synchronize-panes \; display-message "Team sync #{?synchronize-pan
88
105
  bind M-l next-layout
89
106
 
90
107
  # prefix + M-s → capture current pane's full scrollback to file
91
- bind M-s run-shell "tmux capture-pane -pS - > /tmp/claude-pane-#{pane_title}-$(date +%s).txt && tmux display-message 'Captured #{pane_title} to /tmp/'"
108
+ bind M-s run-shell "tmux capture-pane -pS - > /tmp/claude-pane-#{pane_title}-\$(date +%%s).txt && tmux display-message 'Captured #{pane_title} to /tmp/'"
92
109
 
93
110
  # prefix + M-a → capture ALL panes in current window (full scrollback each)
111
+ # NOTE: All $() must be escaped as \$() so they run at keypress time, not config load
94
112
  bind M-a run-shell "\
95
- for pane_id in $(tmux list-panes -F '##{pane_id}'); do \
96
- title=$(tmux display-message -t \$pane_id -p '##{pane_title}'); \
97
- tmux capture-pane -t \$pane_id -pS - > /tmp/claude-pane-\${title:-\$pane_id}-$(date +%s).txt; \
113
+ for pane_id in \$(tmux list-panes -F '#{pane_id}'); do \
114
+ title=\$(tmux display-message -t \$pane_id -p '#{pane_title}'); \
115
+ tmux capture-pane -t \$pane_id -pS - > /tmp/claude-pane-\${title:-\$pane_id}-\$(date +%%s).txt; \
98
116
  done && tmux display-message 'Captured all panes to /tmp/'"
99
117
 
100
118
  # ═══════════════════════════════════════════════════════════════════════════