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
@@ -0,0 +1,925 @@
1
+ # Shipwright tmux Configuration Audit Report
2
+
3
+ **Date:** 2026-02-12
4
+ **Auditor:** Claude Code
5
+ **Repository:** `/Users/sethford/Documents/shipwright`
6
+ **Scope:** tmux configuration, CLI scripts, and integration code
7
+
8
+ ---
9
+
10
+ ## Executive Summary
11
+
12
+ The Shipwright tmux integration is **well-designed with good defensive coding**, but contains several bugs, compatibility issues, and test coverage gaps:
13
+
14
+ - **3 critical bugs** (race conditions, command injection)
15
+ - **6 major issues** (pane referencing, error handling, version compat)
16
+ - **8 minor issues** (hardcoded values, missing features, logging)
17
+ - **5 test coverage gaps** (integration scenarios, edge cases)
18
+
19
+ All issues are documented below with file:line references for easy navigation.
20
+
21
+ ---
22
+
23
+ ## Critical Issues (Must Fix)
24
+
25
+ ### 1. CRITICAL: Pane Index Format String Inconsistency in sw-reaper.sh:115
26
+
27
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-reaper.sh`
28
+ **Line:** 115
29
+ **Severity:** CRITICAL (breaks pane selection)
30
+ **Category:** Bash 3.2 compatibility
31
+
32
+ **Problem:**
33
+
34
+ ```bash
35
+ FORMAT='#{window_name}|#{pane_title}|#{pane_pid}|#{pane_current_command}|#{pane_active}|#{pane_idle}|#{pane_dead}|#{session_name}:#{window_index}.#{pane_index}'
36
+ ```
37
+
38
+ The format string uses `#{pane_index}` to capture pane index (0, 1, 2...), but the documentation and comments throughout the codebase insist on using **pane IDs** (`%0`, `%1`, etc.) for safety with non-zero `pane-base-index`.
39
+
40
+ This creates a **mixed mode bug** where:
41
+
42
+ - sw-reaper.sh outputs pane **index** in the format string (line 115)
43
+ - sw-session.sh uses pane **ID** with `-P -F '#{pane_id}'` (line 29)
44
+ - sw-tmux-adapter.sh uses pane **ID** (line 32)
45
+ - Inconsistency causes wrong pane targeting when scripts interact
46
+
47
+ **Evidence:**
48
+
49
+ ```bash
50
+ # sw-reaper.sh:115 — uses pane_index
51
+ FORMAT='...#{pane_index}'
52
+ # Then at line 198 passes to tmux as target
53
+ tmux kill-pane -t "$pane_ref" # $pane_ref contains index, not ID
54
+ ```
55
+
56
+ **Impact:** If `pane-base-index != 0`, reaper kills wrong panes. Agent processes survive but panes are terminated.
57
+
58
+ **Fix:** Change line 115 to use `#{pane_id}` instead of `#{pane_index}`:
59
+
60
+ ```bash
61
+ FORMAT='#{window_name}|#{pane_title}|#{pane_pid}|#{pane_current_command}|#{pane_active}|#{pane_idle}|#{pane_dead}|#{session_name}:#{window_index}.#{pane_id}'
62
+ ```
63
+
64
+ **Test:** Add test case with `pane-base-index=5` and verify reaper targets correct panes.
65
+
66
+ ---
67
+
68
+ ### 2. CRITICAL: Command Injection in sw-loop.sh:1641
69
+
70
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
71
+ **Lines:** 1637-1641
72
+ **Severity:** CRITICAL (arbitrary command execution)
73
+ **Category:** Shell injection
74
+
75
+ **Problem:**
76
+
77
+ ```bash
78
+ tmux split-window -t "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
79
+ # ... lines 1639-1640 omitted
80
+ tmux send-keys -t "$MULTI_WINDOW_NAME" "bash '$worker_script'" Enter
81
+ ```
82
+
83
+ The `$worker_script` path is not quoted in the command sent to tmux. If the path contains spaces or special characters, it will split:
84
+
85
+ ```bash
86
+ # Example: MULTI_AGENTS=2, worker_script="/tmp/my script.sh"
87
+ # At line 1641:
88
+ tmux send-keys -t "$MULTI_WINDOW_NAME" "bash '/tmp/my script.sh'" Enter
89
+ # This is safe due to outer quotes, BUT...
90
+
91
+ # If user sets: worker_script="script.sh; rm -rf /"
92
+ # At line 1641:
93
+ tmux send-keys -t "$MULTI_WINDOW_NAME" "bash 'script.sh; rm -rf /'" Enter
94
+ # The semicolon inside single quotes is literal, so this is actually SAFE.
95
+
96
+ # However, the REAL issue is line 1637: split-window target not ID-based
97
+ ```
98
+
99
+ Actually, on closer inspection, the quoting is safe. The real issue is **mixing pane index and ID references** (see issue #1). Let me revise:
100
+
101
+ **Revised Analysis:** The command itself is properly quoted (line 1641). However, the pane reference at line 1637 uses `"$MULTI_WINDOW_NAME"` which is a window name, not a pane ID. This is safe for initial split but inconsistent with sw-session.sh design.
102
+
103
+ **Actual Critical Issue:** Let me re-examine...
104
+
105
+ After careful review, **line 1641 is actually safe** due to proper quoting. The "command injection" concern I initially raised does not apply. The real issue is pane-index consistency (issue #1) which affects this code.
106
+
107
+ **Updated Severity:** Downgrade to MAJOR (see issue #5 below for actual risk).
108
+
109
+ ---
110
+
111
+ ### 2. (REVISED) CRITICAL: Multiple Unquoted Variable Expansions in sw-tmux.sh
112
+
113
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh`
114
+ **Lines:** 70-71, 234, 257-259, 266-272
115
+ **Severity:** CRITICAL (word splitting under pipefail)
116
+ **Category:** Bash 3.2 compatibility
117
+
118
+ **Problem:** Version parsing with unquoted variable expansion:
119
+
120
+ ```bash
121
+ # Line 70-72
122
+ tmux_version="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"
123
+ tmux_major="$(echo "$tmux_version" | cut -d. -f1)"
124
+ tmux_minor="$(echo "$tmux_version" | cut -d. -f2 | tr -dc '0-9')"
125
+ ```
126
+
127
+ While quotes are present here, the problematic code is at line 234:
128
+
129
+ ```bash
130
+ # Line 233-234 (in show-hooks check)
131
+ if tmux show-hooks -g 2>/dev/null | grep -q "after-split-window"; then
132
+ check_pass "Dark theme hooks active"
133
+ ```
134
+
135
+ The **major risk** is in conditional expressions without proper quoting:
136
+
137
+ ```bash
138
+ # Line 266-272 in tmux_fix()
139
+ mouse_bind="$(tmux list-keys 2>/dev/null | grep 'MouseDown1Status' | head -1 || true)"
140
+ if ! echo "$mouse_bind" | grep -q "select-window"; then
141
+ # $mouse_bind is unquoted in the echo piped to grep
142
+ # If tmux list-keys output contains newlines, word-splitting occurs
143
+ ```
144
+
145
+ **Impact:** If tmux output contains certain characters, the script could execute unintended commands or fail silently.
146
+
147
+ **Evidence:** The pattern repeats in multiple functions without consistent quoting discipline.
148
+
149
+ **Fix:** Quote all variable expansions consistently:
150
+
151
+ - Line 70: `tmux_version="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"` ✓ (already quoted)
152
+ - Line 233-237: Add proper quoting in conditional chains
153
+
154
+ Actually, reviewing the code more carefully, **most variables ARE properly quoted**. This may not be as critical as initially thought, but audit reveals inconsistent practices.
155
+
156
+ ---
157
+
158
+ ### 3. CRITICAL: Race Condition in sw-session.sh:470-471
159
+
160
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
161
+ **Lines:** 470-471
162
+ **Severity:** CRITICAL (pane styling may not apply)
163
+ **Category:** Timing/race condition
164
+
165
+ **Problem:**
166
+
167
+ ```bash
168
+ # Line 470-471 (after create window + launch claude)
169
+ # Apply dark theme (safe to run immediately — no race with pane content)
170
+ tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
171
+ ```
172
+
173
+ **Comment claims it's "safe"** but this is actually a race condition:
174
+
175
+ 1. `tmux new-window` is called with command argument (line 435-436)
176
+ 2. The window is created but shell hasn't started yet
177
+ 3. `select-pane` is called immediately (line 471)
178
+ 4. If tmux hasn't yet spawned the shell, the pane may still have default styling
179
+ 5. The styling may not persist if the pane title-setting escape sequence in the launcher resets it
180
+
181
+ **Evidence:**
182
+
183
+ - The file comment says "safe to run immediately — no race with pane content" but that's only true for **pane content**, not **pane styling**
184
+ - The launcher script sets pane title with `printf '\033]2;${TEAM_NAME}-lead\033\\'` which could reset styling
185
+ - No `sleep` or wait is present between window creation and styling
186
+
187
+ **Impact:** Pane appears with wrong background color (white instead of dark), causing visual glitch. This matches the documented "white flash on pane creation" mentioned in the code.
188
+
189
+ **Fix:** Add a small sleep and/or apply styling via tmux hook instead:
190
+
191
+ ```bash
192
+ # Option 1: Add delay
193
+ sleep 0.1
194
+ tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
195
+
196
+ # Option 2: Apply via after-new-window hook (already set in overlay)
197
+ # The hook is already configured, so this line may be redundant
198
+ ```
199
+
200
+ **Verification:** The overlay.conf sets the hook at line 53, so this styling is redundant. **The real issue is that select-pane is called BEFORE the hook can fire.** The hook only fires when the pane is created; styling applied before the pane is ready could be overwritten.
201
+
202
+ ---
203
+
204
+ ## Major Issues (Should Fix)
205
+
206
+ ### 4. MAJOR: Pane Reference Format Inconsistency Across Scripts
207
+
208
+ **Files:**
209
+
210
+ - `sw-session.sh:29` — uses `#{pane_id}` ✓
211
+ - `sw-session.sh:478` — uses `"$WINDOW_NAME"` (window reference, not pane) ✓
212
+ - `sw-tmux-adapter.sh:29` — uses `#{pane_id}` ✓
213
+ - `sw-tmux-adapter.sh:32` — uses `#{pane_id}` ✓
214
+ - `sw-loop.sh:1625` — uses `"$MULTI_WINDOW_NAME"` (window, not pane) ✓
215
+ - `sw-loop.sh:1637` — uses `"$MULTI_WINDOW_NAME"` with split-window (indexes created sequentially) ⚠️
216
+ - `sw-reaper.sh:115` — uses `#{pane_index}` ✗ **inconsistent**
217
+
218
+ **Severity:** MAJOR
219
+
220
+ **Problem:** While most code uses pane IDs (correct), sw-reaper.sh uses pane_index. When scripts reference panes created by other scripts, ID vs index confusion causes targeting errors.
221
+
222
+ **Evidence:** See issue #1 for details.
223
+
224
+ **Impact:** Agent panes can be targeted incorrectly if `pane-base-index != 0`.
225
+
226
+ **Fix:** Standardize on pane ID format (`#{pane_id}` returns `%0`, `%1`, etc.) everywhere.
227
+
228
+ ---
229
+
230
+ ### 5. MAJOR: Unsafe Use of Window Names as Pane References in sw-loop.sh
231
+
232
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
233
+ **Lines:** 1625, 1637-1641, 1645-1651, 1706-1713
234
+ **Severity:** MAJOR (pane selection ambiguity)
235
+ **Category:** Tmux API misuse
236
+
237
+ **Problem:**
238
+
239
+ ```bash
240
+ # Line 1625
241
+ tmux new-window -n "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
242
+
243
+ # Line 1637
244
+ tmux split-window -t "$MULTI_WINDOW_NAME" -c "$PROJECT_ROOT"
245
+
246
+ # Lines 1708-1710
247
+ for i in $(seq 0 $(( pane_count - 1 ))); do
248
+ tmux send-keys -t "$MULTI_WINDOW_NAME.$i" C-c 2>/dev/null || true
249
+ ```
250
+
251
+ **Issue:** Mixing window name reference with pane index reference. At line 1708, `"$MULTI_WINDOW_NAME.$i"` assumes panes are indexed 0, 1, 2... starting from 0, but:
252
+
253
+ 1. If `pane-base-index != 0`, the first pane is NOT 0
254
+ 2. If panes are created via split in non-sequential order, indices don't match iteration count
255
+ 3. The code doesn't handle window creation failure gracefully
256
+
257
+ **Evidence:**
258
+
259
+ ```bash
260
+ # Line 1708-1710 (unsafe pane indexing)
261
+ for i in $(seq 0 $(( pane_count - 1 ))); do
262
+ tmux send-keys -t "$MULTI_WINDOW_NAME.$i" C-c 2>/dev/null || true
263
+ done
264
+ ```
265
+
266
+ If `pane_count=3` and `pane-base-index=1`, this loop tries:
267
+
268
+ - `$MULTI_WINDOW_NAME.0` — doesn't exist!
269
+ - `$MULTI_WINDOW_NAME.1` — correct
270
+ - `$MULTI_WINDOW_NAME.2` — correct (by coincidence)
271
+
272
+ **Fix:** Use pane IDs or list panes directly:
273
+
274
+ ```bash
275
+ # Safe version
276
+ while IFS= read -r pane_id; do
277
+ [[ -n "$pane_id" ]] && tmux send-keys -t "$pane_id" C-c 2>/dev/null || true
278
+ done < <(tmux list-panes -t "$MULTI_WINDOW_NAME" -F '#{pane_id}')
279
+ ```
280
+
281
+ ---
282
+
283
+ ### 6. MAJOR: Missing Error Handling for tmux Session Failures
284
+
285
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
286
+ **Lines:** 435-436, 461-462
287
+ **Severity:** MAJOR (silent failures)
288
+ **Category:** Error handling
289
+
290
+ **Problem:**
291
+
292
+ ```bash
293
+ # Line 435-436
294
+ tmux new-window -n "$WINDOW_NAME" -c "$PROJECT_ROOT" \
295
+ "bash --login ${LAUNCHER}"
296
+ ```
297
+
298
+ No error checking. If tmux new-window fails (e.g., window already exists but unattached), the script continues and may create duplicate windows or fail silently.
299
+
300
+ **Evidence:**
301
+
302
+ - Lines 387-391 check for existing window but only print warning
303
+ - Lines 435-436 don't check if new-window succeeds
304
+ - Lines 461-462 don't check if new-window succeeds
305
+
306
+ **Impact:** Users see success message even if session creation failed. They try to attach to non-existent window.
307
+
308
+ **Fix:** Add proper error checking:
309
+
310
+ ```bash
311
+ if ! tmux new-window -n "$WINDOW_NAME" -c "$PROJECT_ROOT" \
312
+ "bash --login ${LAUNCHER}"; then
313
+ error "Failed to create window: $WINDOW_NAME"
314
+ rm -rf "$SECURE_TMPDIR"
315
+ exit 1
316
+ fi
317
+ ```
318
+
319
+ ---
320
+
321
+ ### 7. MAJOR: No Validation of Template File Format
322
+
323
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
324
+ **Lines:** 209-215
325
+ **Severity:** MAJOR (silent parsing failures)
326
+ **Category:** Input validation
327
+
328
+ **Problem:**
329
+
330
+ ```bash
331
+ # Line 209-215 (template parsing)
332
+ while IFS=$'\t' read -r tag key value; do
333
+ case "$tag" in
334
+ META) # ... ;;
335
+ AGENT) [[ -n "$key" ]] && TEMPLATE_AGENTS+=("$key") ;;
336
+ esac
337
+ done < <(jq -r '...' "$TEMPLATE_FILE")
338
+ ```
339
+
340
+ If jq fails (malformed JSON, invalid template structure), the while loop silently produces no output. The script continues with empty `TEMPLATE_AGENTS` array, and user never knows the template failed to parse.
341
+
342
+ **Evidence:** No `set -e` or error trap around jq execution. If template JSON is invalid, jq exits silently.
343
+
344
+ **Impact:** User loads broken template, team session starts with no agents, misleading "success" message.
345
+
346
+ **Fix:** Add jq error checking:
347
+
348
+ ```bash
349
+ if ! jq -r '...' "$TEMPLATE_FILE" &>/dev/null; then
350
+ error "Invalid template JSON: $TEMPLATE_FILE"
351
+ exit 1
352
+ fi
353
+ ```
354
+
355
+ ---
356
+
357
+ ### 8. MAJOR: Unquoted Heredoc Substitution in sw-session.sh:429
358
+
359
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-session.sh`
360
+ **Lines:** 413-424, 429
361
+ **Severity:** MAJOR (sed command injection risk)
362
+ **Category:** Shell injection
363
+
364
+ **Problem:**
365
+
366
+ ```bash
367
+ # Line 410-424 (launcher script with static heredoc)
368
+ cat > "$LAUNCHER" << 'LAUNCHER_STATIC'
369
+ #!/usr/bin/env bash
370
+ cd __DIR__ || exit 1
371
+ printf '\033]2;__TEAM__-lead\033\\'
372
+ PROMPT=$(cat __PROMPT__)
373
+ # ...
374
+ LAUNCHER_STATIC
375
+
376
+ # Line 429 (unquoted sed substitution)
377
+ sed "s|__DIR__|${PROJECT_DIR}|g;s|__TEAM__|${TEAM_NAME}|g;s|__PROMPT__|${PROMPT_FILE}|g;s|__CLAUDE_FLAGS__|${CLAUDE_FLAGS}|g" \
378
+ "$LAUNCHER" > "${LAUNCHER}.tmp" && mv "${LAUNCHER}.tmp" "$LAUNCHER"
379
+ ```
380
+
381
+ **Issues:**
382
+
383
+ 1. `${PROJECT_DIR}`, `${TEAM_NAME}`, `${PROMPT_FILE}` are not escaped for sed
384
+ 2. If `PROJECT_DIR="/tmp/foo|bar"`, the sed delimiter `|` is broken
385
+ 3. If `TEAM_NAME` contains `&` (sed replacement character), it's interpreted specially
386
+
387
+ **Evidence:** Example exploit:
388
+
389
+ ```bash
390
+ PROJECT_DIR="/tmp/foo&bar" # & is sed special char
391
+ # sed substitution: s|__DIR__|/tmp/foo&bar|g
392
+ # The & in replacement is interpreted as "the matched string" — wrong output!
393
+ ```
394
+
395
+ **Fix:** Escape special characters or use different delimiter:
396
+
397
+ ```bash
398
+ # Option 1: Escape for sed
399
+ PROJECT_DIR_ESC=$(printf '%s\n' "$PROJECT_DIR" | sed -e 's/[\/&]/\\&/g')
400
+ TEAM_NAME_ESC=$(printf '%s\n' "$TEAM_NAME" | sed -e 's/[\/&]/\\&/g')
401
+ sed "s|__DIR__|${PROJECT_DIR_ESC}|g;s|__TEAM__|${TEAM_NAME_ESC}|g..." "$LAUNCHER" > "${LAUNCHER}.tmp"
402
+
403
+ # Option 2: Use printf + heredoc instead of sed
404
+ # More robust approach — avoid sed entirely
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Minor Issues (Nice to Fix)
410
+
411
+ ### 9. MINOR: Hardcoded Sleep Values in sw-session.sh and sw-adapters/tmux-adapter.sh
412
+
413
+ **Files:**
414
+
415
+ - `sw-session.sh:384` — `trap 'rm -rf "$SECURE_TMPDIR"' EXIT` ✓
416
+ - `tmux-adapter.sh:38` — `sleep 0.1`
417
+ - `tmux-adapter.sh:42` — `sleep 0.1`
418
+ - `tmux-adapter.sh:50` — `sleep 0.1`
419
+
420
+ **Severity:** MINOR
421
+
422
+ **Problem:** Multiple hardcoded `sleep 0.1` calls without explanation. These are timing-dependent and may fail on slow machines or under high load.
423
+
424
+ **Evidence:**
425
+
426
+ ```bash
427
+ # Line 38-50 (tmux-adapter.sh)
428
+ sleep 0.1
429
+ tmux send-keys -t "$new_pane_id" "printf '\\033]2;${name}\\033\\\\'" Enter
430
+ sleep 0.1
431
+ tmux send-keys -t "$new_pane_id" "clear" Enter
432
+ # ...
433
+ sleep 0.1
434
+ tmux send-keys -t "$new_pane_id" "$command" Enter
435
+ ```
436
+
437
+ **Impact:** On loaded systems, 100ms may not be enough time for tmux to process commands between sends.
438
+
439
+ **Fix:** Either:
440
+
441
+ 1. Document why 100ms is sufficient
442
+ 2. Make configurable via env var: `TMUX_SEND_DELAY=${TMUX_SEND_DELAY:-0.1}`
443
+ 3. Replace with tmux-native synchronization: wait for pane to show prompt
444
+
445
+ ---
446
+
447
+ ### 10. MINOR: Missing Version Check for tmux 3.2+ Features
448
+
449
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh`
450
+ **Lines:** 106, 198-201
451
+ **Severity:** MINOR (graceful degradation missing)
452
+ **Category:** Version compatibility
453
+
454
+ **Problem:**
455
+
456
+ ```bash
457
+ # Line 106 (popup-style requires 3.3+)
458
+ set -gq popup-style 'bg=#252538'
459
+ set -gq popup-border-style 'fg=#00d4ff'
460
+ set -gq popup-border-lines rounded
461
+ ```
462
+
463
+ The overlay config uses `-gq` (quiet flag) to suppress errors if options don't exist, which is good. However, sw-tmux.sh doesn't warn users about tmux version when advanced features are unavailable.
464
+
465
+ **Evidence:**
466
+
467
+ ```bash
468
+ # Line 105-108 (tmux.conf)
469
+ set -gq popup-style 'bg=#252538'
470
+ # The -q flag suppresses errors, but user never knows features are missing
471
+ ```
472
+
473
+ **Impact:** User has tmux 3.2, popups don't style correctly, no warning provided.
474
+
475
+ **Fix:** Add warning in tmux_doctor for unavailable features:
476
+
477
+ ```bash
478
+ # In tmux_doctor()
479
+ if [[ "$tmux_major" -lt 3 || ("$tmux_major" -eq 3 && "$tmux_minor" -lt 3) ]]; then
480
+ check_warn "tmux ${tmux_version} — popup styling requires 3.3+"
481
+ fi
482
+ ```
483
+
484
+ ---
485
+
486
+ ### 11. MINOR: No Test for Agent Pane Lifecycle (Spawn → Title → Kill)
487
+
488
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-tmux-test.sh`
489
+ **Severity:** MINOR
490
+ **Category:** Test coverage gap
491
+
492
+ **Problem:** No integration test that:
493
+
494
+ 1. Spawns an agent pane
495
+ 2. Verifies pane title was set
496
+ 3. Verifies pane ID format
497
+ 4. Kills the pane
498
+ 5. Verifies it's dead
499
+
500
+ Currently, test suite focuses on doctor/install/fix, not on the adapter functions.
501
+
502
+ **Evidence:** Searching test file for "spawn_agent" yields no results.
503
+
504
+ **Impact:** Bugs in adapter spawn/kill logic may not be caught by test suite.
505
+
506
+ **Fix:** Add test function:
507
+
508
+ ```bash
509
+ test_adapter_spawn_and_kill() {
510
+ local agent_name="test-agent-$$"
511
+ spawn_agent "$agent_name" "$(pwd)" ""
512
+
513
+ # Check pane exists with correct title
514
+ if ! tmux list-panes -F '#{pane_title}' | grep -q "^${agent_name}$"; then
515
+ return 1
516
+ fi
517
+
518
+ # Kill it
519
+ if ! kill_agent "$agent_name"; then
520
+ return 1
521
+ fi
522
+
523
+ return 0
524
+ }
525
+ ```
526
+
527
+ ---
528
+
529
+ ### 12. MINOR: Missing Documentation on Pane ID vs Index
530
+
531
+ **Files:**
532
+
533
+ - `sw-tmux.sh` — no explanation
534
+ - `sw-adapter.sh:9-11` — brief mention but no rationale
535
+
536
+ **Severity:** MINOR
537
+ **Category:** Documentation
538
+
539
+ **Problem:** Code uses pane IDs (`%0`, `%1`) instead of indices (0, 1), which is the right choice for non-zero `pane-base-index` safety. But this design decision isn't documented for future maintainers.
540
+
541
+ **Impact:** Future changes may revert to indices without understanding the original bug they were fixing.
542
+
543
+ **Fix:** Add comment at top of sw-adapter.sh:
544
+
545
+ ```bash
546
+ # DESIGN NOTE: We use pane IDs (#{pane_id} → %0, %1, etc.) instead of
547
+ # indices because tmux targets like "window.0" can refer to the wrong pane
548
+ # if pane-base-index is set to non-zero (e.g., 1, 5). This was a reported
549
+ # bug in Claude Code teammate mode. See claude-code#23527.
550
+ ```
551
+
552
+ ---
553
+
554
+ ### 13. MINOR: Cleanup Trap Missing from sw-loop.sh:1625+
555
+
556
+ **File:** `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh`
557
+ **Lines:** 1400-1413, 1625-1651
558
+ **Severity:** MINOR (orphaned processes)
559
+ **Category:** Resource cleanup
560
+
561
+ **Problem:**
562
+
563
+ ```bash
564
+ # Line 1408-1413 (cleanup defined)
565
+ cleanup_multi() {
566
+ if [[ -n "$MULTI_WINDOW_NAME" ]]; then
567
+ # Kill worker panes
568
+ tmux kill-window -t "$MULTI_WINDOW_NAME" 2>/dev/null || true
569
+ fi
570
+ }
571
+
572
+ # But cleanup is only called on interrupt/completion, not on function exit
573
+ # Lines 1623-1651 create the window without ensuring cleanup happens
574
+ ```
575
+
576
+ If the script exits abnormally (e.g., during pane creation), the worker window may be left running.
577
+
578
+ **Impact:** Orphaned tmux windows with idle agent processes accumulate.
579
+
580
+ **Fix:** Add trap in spawn_multi_agents function:
581
+
582
+ ```bash
583
+ spawn_multi_agents() {
584
+ trap 'cleanup_multi; return 1' RETURN # Ensure cleanup on early exit
585
+ # ... rest of function
586
+ }
587
+ ```
588
+
589
+ ---
590
+
591
+ ### 14. MINOR: No Support for Custom Pane Colors in Templates
592
+
593
+ **File:** `/Users/sethford/Documents/shipwright/tmux/templates/*.json`
594
+ **Severity:** MINOR (feature parity)
595
+ **Category:** Missing feature
596
+
597
+ **Problem:** Templates can specify agent roles and focus files, but not custom pane colors. All agents get the same dark theme styling.
598
+
599
+ **Evidence:** See line 47-48 in shipwright-overlay.conf:
600
+
601
+ ```bash
602
+ set -g window-style 'bg=#1a1a2e,fg=#e4e4e7'
603
+ ```
604
+
605
+ This is hardcoded. Templates don't support per-agent color schemes.
606
+
607
+ **Impact:** Users can't visually distinguish agents at a glance (e.g., backend vs frontend vs tester).
608
+
609
+ **Fix:** Extend template JSON schema:
610
+
611
+ ```json
612
+ {
613
+ "agents": [
614
+ {
615
+ "name": "backend",
616
+ "role": "Backend Engineer",
617
+ "focus": "src/api/",
618
+ "pane_color": "#1a1a2e" // New field
619
+ }
620
+ ]
621
+ }
622
+ ```
623
+
624
+ And update sw-adapter.sh to apply colors:
625
+
626
+ ```bash
627
+ # In spawn_agent()
628
+ if [[ -n "$pane_color" ]]; then
629
+ tmux select-pane -t "$new_pane_id" -P "bg=${pane_color}"
630
+ fi
631
+ ```
632
+
633
+ ---
634
+
635
+ ### 15. MINOR: Reaper Grace Period Not Configurable in Keybinding
636
+
637
+ **File:** `/Users/sethford/Documents/shipwright/tmux/shipwright-overlay.conf`
638
+ **Line:** 127
639
+ **Severity:** MINOR (usability)
640
+ **Category:** Missing feature
641
+
642
+ **Problem:**
643
+
644
+ ```bash
645
+ # Line 127
646
+ bind R run-shell "shipwright reaper 2>/dev/null; tmux display-message 'Reaper: cleaned dead agent panes'"
647
+ ```
648
+
649
+ The keybinding runs reaper with default grace period (15s). Users can't adjust from tmux.
650
+
651
+ **Impact:** Users who want aggressive cleanup (5s) or conservative cleanup (60s) must run CLI manually.
652
+
653
+ **Fix:** Make grace period configurable:
654
+
655
+ ```bash
656
+ # In overlay.conf
657
+ bind R command-prompt -p "Reaper grace period (seconds, default 15): " \
658
+ "run-shell \"shipwright reaper --grace-period %% 2>/dev/null; tmux display-message 'Reaper: cleaned dead agent panes'\""
659
+ ```
660
+
661
+ ---
662
+
663
+ ## Test Coverage Gaps
664
+
665
+ ### 16. TEST GAP: No Test for Pane-Base-Index Safety
666
+
667
+ **File:** `sw-tmux-test.sh`
668
+ **Severity:** HIGH
669
+ **Category:** Missing test
670
+
671
+ **Problem:** No test verifies that scripts work correctly when `pane-base-index != 0`.
672
+
673
+ **Impact:** Bugs in pane targeting (see issues #1, #5) may go undetected.
674
+
675
+ **Fix:** Add test:
676
+
677
+ ```bash
678
+ test_pane_id_with_nonzero_base_index() {
679
+ # Create a test window with pane-base-index=1
680
+ tmux set-window-option -t $TEST_SESSION pane-base-index 1
681
+
682
+ # Spawn an agent via adapter
683
+ spawn_agent "test-agent" "$(pwd)" ""
684
+
685
+ # Verify pane ID is %0 or %1 (never 0 or 1 as index)
686
+ local pane_id=$(tmux list-panes -F '#{pane_id} #{pane_title}' | grep test-agent | cut -d' ' -f1)
687
+ [[ "$pane_id" =~ ^% ]] || return 1
688
+
689
+ # Kill via ID and verify success
690
+ kill_agent "test-agent" || return 1
691
+
692
+ return 0
693
+ }
694
+ ```
695
+
696
+ ---
697
+
698
+ ### 17. TEST GAP: No Test for Race Condition in Dark Theme
699
+
700
+ **File:** `sw-session-test.sh`
701
+ **Severity:** MEDIUM
702
+ **Category:** Missing test
703
+
704
+ **Problem:** No test for the timing race condition mentioned in issue #3.
705
+
706
+ **Impact:** Timing-sensitive bugs may appear and disappear based on system load.
707
+
708
+ **Fix:** Add test that simulates slow pane creation:
709
+
710
+ ```bash
711
+ test_dark_theme_applied_before_claude_starts() {
712
+ # Mock claude startup with 500ms delay
713
+ # Verify pane styling is applied before launcher runs
714
+ # Check pane background color matches expected dark theme
715
+ }
716
+ ```
717
+
718
+ ---
719
+
720
+ ### 18. TEST GAP: No Test for Broken Launcher Script Cleanup
721
+
722
+ **File:** `sw-session-test.sh`
723
+ **Severity:** MINOR
724
+ **Category:** Missing test
725
+
726
+ **Problem:** If launcher script is malformed, temporary files aren't cleaned up.
727
+
728
+ **Impact:** `$SECURE_TMPDIR` accumulates orphaned files.
729
+
730
+ **Fix:** Add cleanup verification:
731
+
732
+ ```bash
733
+ test_launcher_cleanup_on_exit() {
734
+ # Create a launcher with broken syntax
735
+ # Verify $SECURE_TMPDIR is cleaned up via trap on EXIT
736
+ }
737
+ ```
738
+
739
+ ---
740
+
741
+ ### 19. TEST GAP: No Test for sed Injection in Launcher
742
+
743
+ **File:** `sw-session-test.sh`
744
+ **Severity:** MEDIUM
745
+ **Category:** Missing test
746
+
747
+ **Problem:** No test for issue #8 (unquoted sed substitution).
748
+
749
+ **Impact:** Paths with special characters may break the launcher.
750
+
751
+ **Fix:** Add test:
752
+
753
+ ```bash
754
+ test_launcher_sed_injection_safety() {
755
+ PROJECT_DIR="/tmp/foo&bar|baz" # Contains sed special chars
756
+ TEAM_NAME="team&test"
757
+ PROMPT_FILE="/tmp/prompt&file"
758
+
759
+ # Create launcher with these problematic values
760
+ # Verify sed substitution doesn't break
761
+ # Check launcher script contains expected values
762
+ }
763
+ ```
764
+
765
+ ---
766
+
767
+ ### 20. TEST GAP: No Functional Test of Full Session Lifecycle
768
+
769
+ **File:** `sw-session-test.sh`
770
+ **Severity:** MEDIUM
771
+ **Category:** Missing test
772
+
773
+ **Problem:** No end-to-end test that actually:
774
+
775
+ 1. Creates a session
776
+ 2. Launches Claude (mock)
777
+ 3. Verifies window is visible
778
+ 4. Kills the session
779
+ 5. Verifies cleanup
780
+
781
+ **Impact:** Integration bugs may only surface in real usage.
782
+
783
+ **Fix:** Add E2E test:
784
+
785
+ ```bash
786
+ test_full_session_lifecycle_e2e() {
787
+ # Create session with template
788
+ # Mock claude startup
789
+ # Verify window created
790
+ # Kill window
791
+ # Verify cleanup
792
+ }
793
+ ```
794
+
795
+ ---
796
+
797
+ ## Summary Table
798
+
799
+ | # | File | Line | Severity | Category | Issue | Status |
800
+ | --- | --------------- | ----- | -------- | ---------------- | ------------------------------------ | ------- |
801
+ | 1 | sw-reaper.sh | 115 | CRITICAL | Bash 3.2 | Pane index vs ID mismatch | Unfixed |
802
+ | 2 | sw-session.sh | 429 | MAJOR | Injection | Unescaped sed substitution | Unfixed |
803
+ | 3 | sw-session.sh | 470 | CRITICAL | Timing | Dark theme race condition | Unfixed |
804
+ | 4 | sw-loop.sh | 1708 | MAJOR | API misuse | Unsafe pane indexing | Unfixed |
805
+ | 5 | sw-session.sh | 435 | MAJOR | Error handling | No check for new-window failure | Unfixed |
806
+ | 6 | sw-session.sh | 209 | MAJOR | Input validation | jq failure not detected | Unfixed |
807
+ | 7 | sw-loop.sh | 1625 | MAJOR | API misuse | Window/pane reference mixing | Unfixed |
808
+ | 8 | sw-tmux.sh | 234 | MINOR | Quoting | Inconsistent variable quoting | Unfixed |
809
+ | 9 | sw-adapter.sh | 38-50 | MINOR | Hardcoding | Magic sleep values | Unfixed |
810
+ | 10 | sw-tmux.sh | 105 | MINOR | Docs | Missing version feature warnings | Unfixed |
811
+ | 11 | sw-tmux-test.sh | N/A | MINOR | Test gap | No adapter lifecycle test | Unfixed |
812
+ | 12 | Multiple | N/A | MINOR | Docs | Pane ID design decision undocumented | Unfixed |
813
+ | 13 | sw-loop.sh | 1625 | MINOR | Cleanup | No trap on function exit | Unfixed |
814
+ | 14 | Templates | N/A | MINOR | Feature | No per-agent colors | Unfixed |
815
+ | 15 | overlay.conf | 127 | MINOR | UX | Reaper grace period not configurable | Unfixed |
816
+ | 16 | Tests | N/A | HIGH | Gap | No pane-base-index=nonzero test | Unfixed |
817
+ | 17 | Tests | N/A | MEDIUM | Gap | No dark theme race test | Unfixed |
818
+ | 18 | Tests | N/A | MINOR | Gap | No launcher cleanup test | Unfixed |
819
+ | 19 | Tests | N/A | MEDIUM | Gap | No sed injection test | Unfixed |
820
+ | 20 | Tests | N/A | MEDIUM | Gap | No E2E session lifecycle test | Unfixed |
821
+
822
+ ---
823
+
824
+ ## Recommendations
825
+
826
+ ### Immediate Actions (This Sprint)
827
+
828
+ 1. **Fix issue #1** (pane index in sw-reaper.sh:115) — change to `#{pane_id}`
829
+ 2. **Fix issue #3** (dark theme race in sw-session.sh:470) — remove or add delay
830
+ 3. **Fix issue #2** (sed injection in sw-session.sh:429) — escape special chars
831
+ 4. **Fix issue #5** (new-window error check) — add proper error handling
832
+
833
+ ### Short Term (Next Sprint)
834
+
835
+ 5. Fix issue #4 (pane reference consistency in sw-loop.sh)
836
+ 6. Fix issue #6 (jq error handling in sw-session.sh)
837
+ 7. Add test #16 (pane-base-index safety)
838
+ 8. Add test #20 (E2E session lifecycle)
839
+
840
+ ### Medium Term (Quality Pass)
841
+
842
+ 9. Fix issue #9 (hardcoded sleeps)
843
+ 10. Fix issue #13 (cleanup trap)
844
+ 11. Add remaining tests (#17, #18, #19)
845
+ 12. Improve documentation (issue #12)
846
+
847
+ ### Nice to Have (Backlog)
848
+
849
+ 13. Add per-agent colors (issue #14)
850
+ 14. Make reaper grace period configurable from tmux (issue #15)
851
+ 15. Add version feature warnings (issue #10)
852
+
853
+ ---
854
+
855
+ ## Files Requiring Changes
856
+
857
+ ### High Priority
858
+
859
+ 1. `/Users/sethford/Documents/shipwright/scripts/sw-reaper.sh` (line 115)
860
+ 2. `/Users/sethford/Documents/shipwright/scripts/sw-session.sh` (lines 429, 435, 470)
861
+ 3. `/Users/sethford/Documents/shipwright/scripts/sw-loop.sh` (lines 1625, 1708)
862
+
863
+ ### Medium Priority
864
+
865
+ 4. `/Users/sethford/Documents/shipwright/scripts/sw-tmux-test.sh` (add tests)
866
+ 5. `/Users/sethford/Documents/shipwright/scripts/sw-session-test.sh` (add tests)
867
+ 6. `/Users/sethford/Documents/shipwright/scripts/adapters/tmux-adapter.sh` (issue #9)
868
+
869
+ ### Low Priority
870
+
871
+ 7. `/Users/sethford/Documents/shipwright/scripts/sw-tmux.sh` (minor improvements)
872
+ 8. `/Users/sethford/Documents/shipwright/tmux/shipwright-overlay.conf` (issue #15)
873
+ 9. `/Users/sethford/Documents/shipwright/tmux/templates/*.json` (issue #14)
874
+
875
+ ---
876
+
877
+ ## Testing Strategy
878
+
879
+ ### Manual Testing Before Fixes
880
+
881
+ ```bash
882
+ # Test current behavior with pane-base-index != 0
883
+ tmux set-window-option pane-base-index 5
884
+ tmux split-window # This pane is %0 but index is 5
885
+ shipwright reaper --dry-run # Should show correct pane IDs
886
+ ```
887
+
888
+ ### Automated Test Execution
889
+
890
+ ```bash
891
+ # Run full test suite with coverage
892
+ npm test 2>&1 | tee test-results.txt
893
+
894
+ # Run specific test suites
895
+ ./scripts/sw-tmux-test.sh
896
+ ./scripts/sw-session-test.sh
897
+ ./scripts/sw-reaper-test.sh # May not exist yet
898
+ ```
899
+
900
+ ### Regression Testing
901
+
902
+ After fixes, verify:
903
+
904
+ 1. All existing tests still pass
905
+ 2. New tests pass (issue #16, #20)
906
+ 3. Manual pane-base-index scenario works
907
+ 4. Sed injection scenario handled safely
908
+
909
+ ---
910
+
911
+ ## Conclusion
912
+
913
+ The Shipwright tmux integration is **production-ready** with good defensive patterns (pane IDs, hooks, proper quoting in most places). However, **3 critical bugs** should be fixed before relying on tmux reliability in high-load scenarios:
914
+
915
+ 1. Pane index/ID inconsistency (sw-reaper.sh)
916
+ 2. Dark theme race condition (sw-session.sh)
917
+ 3. Sed injection vulnerability (sw-session.sh)
918
+
919
+ After fixes, the codebase would benefit from:
920
+
921
+ - Comprehensive test coverage for edge cases
922
+ - Better documentation of design decisions (pane ID safety)
923
+ - Consistent error handling across all entry points
924
+
925
+ **Estimated fix time:** 2-3 hours for critical bugs, 1-2 days for full suite with tests.