shipwright-cli 1.7.0 → 1.9.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.
- package/.claude/agents/code-reviewer.md +90 -0
- package/.claude/agents/devops-engineer.md +142 -0
- package/.claude/agents/pipeline-agent.md +80 -0
- package/.claude/agents/shell-script-specialist.md +150 -0
- package/.claude/agents/test-specialist.md +196 -0
- package/.claude/hooks/post-tool-use.sh +38 -0
- package/.claude/hooks/pre-tool-use.sh +25 -0
- package/.claude/hooks/session-started.sh +37 -0
- package/README.md +212 -814
- package/claude-code/CLAUDE.md.shipwright +54 -0
- package/claude-code/hooks/notify-idle.sh +2 -2
- package/claude-code/hooks/session-start.sh +24 -0
- package/claude-code/hooks/task-completed.sh +6 -2
- package/claude-code/settings.json.template +12 -0
- package/dashboard/public/app.js +4422 -0
- package/dashboard/public/index.html +816 -0
- package/dashboard/public/styles.css +4755 -0
- package/dashboard/server.ts +4315 -0
- package/docs/KNOWN-ISSUES.md +18 -10
- package/docs/TIPS.md +38 -26
- package/docs/patterns/README.md +33 -23
- package/package.json +9 -5
- package/scripts/adapters/iterm2-adapter.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +52 -23
- package/scripts/adapters/wezterm-adapter.sh +26 -14
- package/scripts/lib/compat.sh +200 -0
- package/scripts/lib/helpers.sh +72 -0
- package/scripts/postinstall.mjs +72 -13
- package/scripts/{cct → sw} +109 -21
- package/scripts/sw-adversarial.sh +274 -0
- package/scripts/sw-architecture-enforcer.sh +330 -0
- package/scripts/sw-checkpoint.sh +390 -0
- package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
- package/scripts/sw-connect.sh +619 -0
- package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
- package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
- package/scripts/sw-dashboard.sh +477 -0
- package/scripts/sw-developer-simulation.sh +252 -0
- package/scripts/sw-docs.sh +635 -0
- package/scripts/sw-doctor.sh +907 -0
- package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
- package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
- package/scripts/sw-github-checks.sh +521 -0
- package/scripts/sw-github-deploy.sh +533 -0
- package/scripts/sw-github-graphql.sh +972 -0
- package/scripts/sw-heartbeat.sh +293 -0
- package/scripts/sw-init.sh +522 -0
- package/scripts/sw-intelligence.sh +1196 -0
- package/scripts/sw-jira.sh +643 -0
- package/scripts/sw-launchd.sh +364 -0
- package/scripts/sw-linear.sh +648 -0
- package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
- package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
- package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
- package/scripts/sw-patrol-meta.sh +417 -0
- package/scripts/sw-pipeline-composer.sh +455 -0
- package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
- package/scripts/sw-predictive.sh +820 -0
- package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
- package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
- package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
- package/scripts/sw-remote.sh +687 -0
- package/scripts/sw-self-optimize.sh +947 -0
- package/scripts/sw-session.sh +519 -0
- package/scripts/sw-setup.sh +234 -0
- package/scripts/sw-status.sh +605 -0
- package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
- package/scripts/sw-tmux.sh +591 -0
- package/scripts/sw-tracker-jira.sh +277 -0
- package/scripts/sw-tracker-linear.sh +292 -0
- package/scripts/sw-tracker.sh +409 -0
- package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
- package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
- package/templates/pipelines/autonomous.json +27 -5
- package/templates/pipelines/full.json +12 -0
- package/templates/pipelines/standard.json +12 -0
- package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
- package/tmux/templates/accessibility.json +34 -0
- package/tmux/templates/api-design.json +35 -0
- package/tmux/templates/architecture.json +1 -0
- package/tmux/templates/bug-fix.json +9 -0
- package/tmux/templates/code-review.json +1 -0
- package/tmux/templates/compliance.json +36 -0
- package/tmux/templates/data-pipeline.json +36 -0
- package/tmux/templates/debt-paydown.json +34 -0
- package/tmux/templates/devops.json +1 -0
- package/tmux/templates/documentation.json +1 -0
- package/tmux/templates/exploration.json +1 -0
- package/tmux/templates/feature-dev.json +1 -0
- package/tmux/templates/full-stack.json +8 -0
- package/tmux/templates/i18n.json +34 -0
- package/tmux/templates/incident-response.json +36 -0
- package/tmux/templates/migration.json +1 -0
- package/tmux/templates/observability.json +35 -0
- package/tmux/templates/onboarding.json +33 -0
- package/tmux/templates/performance.json +35 -0
- package/tmux/templates/refactor.json +1 -0
- package/tmux/templates/release.json +35 -0
- package/tmux/templates/security-audit.json +8 -0
- package/tmux/templates/spike.json +34 -0
- package/tmux/templates/testing.json +1 -0
- package/tmux/tmux.conf +98 -9
- package/scripts/cct-doctor.sh +0 -328
- package/scripts/cct-init.sh +0 -282
- package/scripts/cct-session.sh +0 -284
- package/scripts/cct-status.sh +0 -169
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ sw-tmux.sh — tmux Health & Plugin Management ║
|
|
4
|
+
# ║ ║
|
|
5
|
+
# ║ Diagnoses tmux + Claude Code compatibility, installs TPM plugins, ║
|
|
6
|
+
# ║ and auto-fixes common issues. Part of the Shipwright CLI. ║
|
|
7
|
+
# ║ ║
|
|
8
|
+
# ║ Usage: ║
|
|
9
|
+
# ║ shipwright tmux doctor — Check tmux features + Claude compat ║
|
|
10
|
+
# ║ shipwright tmux install — Install TPM + all plugins ║
|
|
11
|
+
# ║ shipwright tmux fix — Auto-fix common issues ║
|
|
12
|
+
# ║ shipwright tmux reload — Reload tmux config ║
|
|
13
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
14
|
+
VERSION="1.9.0"
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
20
|
+
|
|
21
|
+
# ─── Colors ──────────────────────────────────────────────────────────────────
|
|
22
|
+
CYAN='\033[38;2;0;212;255m'
|
|
23
|
+
PURPLE='\033[38;2;124;58;237m'
|
|
24
|
+
GREEN='\033[38;2;74;222;128m'
|
|
25
|
+
YELLOW='\033[38;2;250;204;21m'
|
|
26
|
+
RED='\033[38;2;248;113;113m'
|
|
27
|
+
DIM='\033[2m'
|
|
28
|
+
BOLD='\033[1m'
|
|
29
|
+
RESET='\033[0m'
|
|
30
|
+
|
|
31
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
32
|
+
# shellcheck source=lib/compat.sh
|
|
33
|
+
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
34
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
35
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
36
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
37
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
38
|
+
|
|
39
|
+
PASS=0
|
|
40
|
+
WARN=0
|
|
41
|
+
FAIL=0
|
|
42
|
+
|
|
43
|
+
check_pass() { success "$*"; PASS=$((PASS + 1)); }
|
|
44
|
+
check_warn() { warn "$*"; WARN=$((WARN + 1)); }
|
|
45
|
+
check_fail() { error "$*"; FAIL=$((FAIL + 1)); }
|
|
46
|
+
|
|
47
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
48
|
+
# tmux doctor — Comprehensive tmux + Claude Code compatibility check
|
|
49
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
50
|
+
|
|
51
|
+
tmux_doctor() {
|
|
52
|
+
echo ""
|
|
53
|
+
echo -e "${CYAN}${BOLD} Shipwright — tmux Doctor${RESET}"
|
|
54
|
+
echo -e "${DIM} $(date '+%Y-%m-%d %H:%M:%S')${RESET}"
|
|
55
|
+
echo -e "${DIM} ══════════════════════════════════════════${RESET}"
|
|
56
|
+
echo ""
|
|
57
|
+
|
|
58
|
+
# ─── 1. tmux installed + version ─────────────────────────────────────
|
|
59
|
+
echo -e "${BOLD}1. tmux Version${RESET}"
|
|
60
|
+
if ! command -v tmux &>/dev/null; then
|
|
61
|
+
check_fail "tmux not installed"
|
|
62
|
+
echo -e " ${DIM}brew install tmux (macOS)${RESET}"
|
|
63
|
+
echo -e " ${DIM}sudo apt install tmux (Ubuntu/Debian)${RESET}"
|
|
64
|
+
echo ""
|
|
65
|
+
echo -e "${RED}${BOLD}Cannot continue without tmux.${RESET}"
|
|
66
|
+
return 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
local tmux_version tmux_major tmux_minor
|
|
70
|
+
tmux_version="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"
|
|
71
|
+
tmux_major="$(echo "$tmux_version" | cut -d. -f1)"
|
|
72
|
+
tmux_minor="$(echo "$tmux_version" | cut -d. -f2 | tr -dc '0-9')"
|
|
73
|
+
|
|
74
|
+
if [[ "$tmux_major" -ge 3 && "$tmux_minor" -ge 3 ]] || [[ "$tmux_major" -ge 4 ]]; then
|
|
75
|
+
check_pass "tmux ${tmux_version} (all features supported)"
|
|
76
|
+
elif [[ "$tmux_major" -ge 3 && "$tmux_minor" -ge 2 ]]; then
|
|
77
|
+
check_warn "tmux ${tmux_version} — 3.3+ recommended for popup styling + allow-passthrough"
|
|
78
|
+
else
|
|
79
|
+
check_fail "tmux ${tmux_version} — 3.2+ required for popups, 3.3+ for passthrough"
|
|
80
|
+
echo -e " ${DIM}brew upgrade tmux${RESET}"
|
|
81
|
+
fi
|
|
82
|
+
echo ""
|
|
83
|
+
|
|
84
|
+
# ─── 2. Claude Code compatibility features ──────────────────────────
|
|
85
|
+
echo -e "${BOLD}2. Claude Code Compatibility${RESET}"
|
|
86
|
+
|
|
87
|
+
if [[ -n "${TMUX:-}" ]]; then
|
|
88
|
+
# Check allow-passthrough (DEC 2026 synchronized output)
|
|
89
|
+
local passthrough
|
|
90
|
+
passthrough="$(tmux show-option -gv allow-passthrough 2>/dev/null || echo "off")"
|
|
91
|
+
if [[ "$passthrough" == "on" ]]; then
|
|
92
|
+
check_pass "allow-passthrough: on (DEC 2026 synchronized output works)"
|
|
93
|
+
else
|
|
94
|
+
check_fail "allow-passthrough: ${passthrough} — Claude Code will flicker"
|
|
95
|
+
echo -e " ${DIM}Fix: set -g allow-passthrough on${RESET}"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Check extended-keys
|
|
99
|
+
local extkeys
|
|
100
|
+
extkeys="$(tmux show-option -gv extended-keys 2>/dev/null || echo "off")"
|
|
101
|
+
if [[ "$extkeys" == "on" ]]; then
|
|
102
|
+
check_pass "extended-keys: on (better key handling for TUI apps)"
|
|
103
|
+
else
|
|
104
|
+
check_warn "extended-keys: ${extkeys} — some key combos may not work"
|
|
105
|
+
echo -e " ${DIM}Fix: set -g extended-keys on${RESET}"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Check escape-time
|
|
109
|
+
local esc_time
|
|
110
|
+
esc_time="$(tmux show-option -gv escape-time 2>/dev/null || echo "500")"
|
|
111
|
+
if [[ "$esc_time" -le 10 ]]; then
|
|
112
|
+
check_pass "escape-time: ${esc_time}ms (no input delay)"
|
|
113
|
+
else
|
|
114
|
+
check_fail "escape-time: ${esc_time}ms — causes input lag in Claude Code"
|
|
115
|
+
echo -e " ${DIM}Fix: set -sg escape-time 0${RESET}"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Check set-clipboard
|
|
119
|
+
local clipboard
|
|
120
|
+
clipboard="$(tmux show-option -gv set-clipboard 2>/dev/null || echo "off")"
|
|
121
|
+
if [[ "$clipboard" == "on" || "$clipboard" == "external" ]]; then
|
|
122
|
+
check_pass "set-clipboard: ${clipboard} (OSC 52 clipboard works)"
|
|
123
|
+
else
|
|
124
|
+
check_warn "set-clipboard: ${clipboard} — clipboard may not work in SSH"
|
|
125
|
+
echo -e " ${DIM}Fix: set -g set-clipboard on${RESET}"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Check history-limit
|
|
129
|
+
local hist
|
|
130
|
+
hist="$(tmux show-option -gv history-limit 2>/dev/null || echo "2000")"
|
|
131
|
+
if [[ "$hist" -ge 100000 ]]; then
|
|
132
|
+
check_pass "history-limit: ${hist} (sufficient for Claude Code output)"
|
|
133
|
+
elif [[ "$hist" -ge 50000 ]]; then
|
|
134
|
+
check_warn "history-limit: ${hist} — 250000+ recommended for long agent runs"
|
|
135
|
+
else
|
|
136
|
+
check_fail "history-limit: ${hist} — will overflow during Claude Code streaming"
|
|
137
|
+
echo -e " ${DIM}Fix: set -g history-limit 250000${RESET}"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Check focus-events
|
|
141
|
+
local focus
|
|
142
|
+
focus="$(tmux show-option -gv focus-events 2>/dev/null || echo "off")"
|
|
143
|
+
if [[ "$focus" == "on" ]]; then
|
|
144
|
+
check_pass "focus-events: on"
|
|
145
|
+
else
|
|
146
|
+
check_warn "focus-events: ${focus} — some TUI features won't work"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Check default-terminal
|
|
150
|
+
local term
|
|
151
|
+
term="$(tmux show-option -gv default-terminal 2>/dev/null || echo "unknown")"
|
|
152
|
+
if [[ "$term" == *"256color"* ]]; then
|
|
153
|
+
check_pass "default-terminal: ${term}"
|
|
154
|
+
else
|
|
155
|
+
check_warn "default-terminal: ${term} — 256color variant recommended"
|
|
156
|
+
fi
|
|
157
|
+
else
|
|
158
|
+
info "Not in tmux session — checking config file instead"
|
|
159
|
+
if [[ -f "$HOME/.tmux.conf" ]]; then
|
|
160
|
+
if grep -q "allow-passthrough" "$HOME/.tmux.conf" 2>/dev/null; then
|
|
161
|
+
check_pass "allow-passthrough found in ~/.tmux.conf"
|
|
162
|
+
else
|
|
163
|
+
check_fail "allow-passthrough not in ~/.tmux.conf — Claude Code will flicker"
|
|
164
|
+
fi
|
|
165
|
+
if grep -q "extended-keys" "$HOME/.tmux.conf" 2>/dev/null; then
|
|
166
|
+
check_pass "extended-keys found in ~/.tmux.conf"
|
|
167
|
+
else
|
|
168
|
+
check_warn "extended-keys not in ~/.tmux.conf"
|
|
169
|
+
fi
|
|
170
|
+
else
|
|
171
|
+
check_fail "No ~/.tmux.conf found"
|
|
172
|
+
fi
|
|
173
|
+
fi
|
|
174
|
+
echo ""
|
|
175
|
+
|
|
176
|
+
# ─── 3. Plugin Manager ───────────────────────────────────────────────
|
|
177
|
+
echo -e "${BOLD}3. Plugin Manager (TPM)${RESET}"
|
|
178
|
+
|
|
179
|
+
if [[ -d "$HOME/.tmux/plugins/tpm" ]]; then
|
|
180
|
+
check_pass "TPM installed: ~/.tmux/plugins/tpm"
|
|
181
|
+
else
|
|
182
|
+
check_fail "TPM not installed"
|
|
183
|
+
echo -e " ${DIM}Run: shipwright tmux install${RESET}"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Check individual plugins
|
|
187
|
+
local plugins=("tmux-sensible" "tmux-resurrect" "tmux-continuum" "tmux-yank" "tmux-fzf")
|
|
188
|
+
for plugin in "${plugins[@]}"; do
|
|
189
|
+
local plugin_dir=""
|
|
190
|
+
# tmux-fzf is under sainnhe org
|
|
191
|
+
if [[ "$plugin" == "tmux-fzf" ]]; then
|
|
192
|
+
plugin_dir="$HOME/.tmux/plugins/tmux-fzf"
|
|
193
|
+
else
|
|
194
|
+
plugin_dir="$HOME/.tmux/plugins/${plugin}"
|
|
195
|
+
fi
|
|
196
|
+
if [[ -d "$plugin_dir" ]]; then
|
|
197
|
+
check_pass "Plugin: ${plugin}"
|
|
198
|
+
else
|
|
199
|
+
check_warn "Plugin not installed: ${plugin}"
|
|
200
|
+
echo -e " ${DIM}Press prefix + I inside tmux to install${RESET}"
|
|
201
|
+
fi
|
|
202
|
+
done
|
|
203
|
+
echo ""
|
|
204
|
+
|
|
205
|
+
# ─── 4. Shipwright overlay ───────────────────────────────────────────
|
|
206
|
+
echo -e "${BOLD}4. Shipwright Integration${RESET}"
|
|
207
|
+
|
|
208
|
+
if [[ -f "$HOME/.tmux/shipwright-overlay.conf" ]]; then
|
|
209
|
+
check_pass "Overlay: ~/.tmux/shipwright-overlay.conf"
|
|
210
|
+
else
|
|
211
|
+
check_fail "Overlay not found"
|
|
212
|
+
echo -e " ${DIM}Run: shipwright init${RESET}"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
if [[ -f "$HOME/.tmux.conf" ]] && grep -q "shipwright-overlay" "$HOME/.tmux.conf" 2>/dev/null; then
|
|
216
|
+
check_pass "Overlay sourced in ~/.tmux.conf"
|
|
217
|
+
else
|
|
218
|
+
check_warn "Overlay not sourced in ~/.tmux.conf"
|
|
219
|
+
echo -e " ${DIM}Add: source-file -q ~/.tmux/shipwright-overlay.conf${RESET}"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
if [[ -n "${TMUX:-}" ]]; then
|
|
223
|
+
# Check pane-border-status
|
|
224
|
+
local border_status
|
|
225
|
+
border_status="$(tmux show-option -gv pane-border-status 2>/dev/null || echo "off")"
|
|
226
|
+
if [[ "$border_status" == "top" ]]; then
|
|
227
|
+
check_pass "pane-border-status: top (agent names visible)"
|
|
228
|
+
else
|
|
229
|
+
check_warn "pane-border-status: ${border_status} — agent names won't show"
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Check dark theme hooks
|
|
233
|
+
if tmux show-hooks -g 2>/dev/null | grep -q "after-split-window"; then
|
|
234
|
+
check_pass "Dark theme hooks active"
|
|
235
|
+
else
|
|
236
|
+
check_warn "Dark theme hooks not active — new panes may flash white"
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# Check mouse
|
|
240
|
+
local mouse
|
|
241
|
+
mouse="$(tmux show-option -gv mouse 2>/dev/null || echo "off")"
|
|
242
|
+
if [[ "$mouse" == "on" ]]; then
|
|
243
|
+
check_pass "Mouse: on"
|
|
244
|
+
else
|
|
245
|
+
check_warn "Mouse: off — enable for better agent pane interaction"
|
|
246
|
+
fi
|
|
247
|
+
fi
|
|
248
|
+
echo ""
|
|
249
|
+
|
|
250
|
+
# ─── 5. Known Issues ─────────────────────────────────────────────────
|
|
251
|
+
echo -e "${BOLD}5. Known Issues Check${RESET}"
|
|
252
|
+
|
|
253
|
+
if [[ -n "${TMUX:-}" ]]; then
|
|
254
|
+
# Check pane-base-index compatibility
|
|
255
|
+
local pbi
|
|
256
|
+
pbi="$(tmux show-window-option -gv pane-base-index 2>/dev/null || echo "0")"
|
|
257
|
+
if [[ "$pbi" != "0" ]]; then
|
|
258
|
+
check_warn "pane-base-index: ${pbi} — Claude Code teammate mode may misaddress panes"
|
|
259
|
+
echo -e " ${DIM}Shipwright adapter uses pane IDs to work around this${RESET}"
|
|
260
|
+
else
|
|
261
|
+
check_pass "pane-base-index: 0 (Claude Code compatible)"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# Check for MouseDown1Status fix
|
|
265
|
+
local mouse_bind
|
|
266
|
+
mouse_bind="$(tmux list-keys 2>/dev/null | grep 'MouseDown1Status' | head -1 || true)"
|
|
267
|
+
if echo "$mouse_bind" | grep -q "select-window"; then
|
|
268
|
+
check_pass "MouseDown1Status: select-window (clicks work correctly)"
|
|
269
|
+
elif [[ -n "$mouse_bind" ]]; then
|
|
270
|
+
check_warn "MouseDown1Status: may switch sessions instead of windows"
|
|
271
|
+
echo -e " ${DIM}Fix: bind -T root MouseDown1Status select-window -t =${RESET}"
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Check terminal emulator
|
|
276
|
+
local parent_term="${LC_TERMINAL:-${TERM_PROGRAM:-unknown}}"
|
|
277
|
+
case "$parent_term" in
|
|
278
|
+
*Ghostty*|*ghostty*)
|
|
279
|
+
check_pass "Terminal: Ghostty (best Claude Code + tmux compat)"
|
|
280
|
+
;;
|
|
281
|
+
*iTerm*|*iterm*)
|
|
282
|
+
check_pass "Terminal: iTerm2 (good compat, true color)"
|
|
283
|
+
;;
|
|
284
|
+
*kitty*)
|
|
285
|
+
check_pass "Terminal: kitty (good compat)"
|
|
286
|
+
;;
|
|
287
|
+
*WezTerm*|*wezterm*)
|
|
288
|
+
check_pass "Terminal: WezTerm (good compat)"
|
|
289
|
+
;;
|
|
290
|
+
*Alacritty*|*alacritty*)
|
|
291
|
+
check_pass "Terminal: Alacritty (good compat)"
|
|
292
|
+
;;
|
|
293
|
+
*Apple_Terminal*|*Terminal*)
|
|
294
|
+
check_warn "Terminal: Apple Terminal — limited color support, no true color"
|
|
295
|
+
echo -e " ${DIM}Recommend: iTerm2, Ghostty, or WezTerm for full experience${RESET}"
|
|
296
|
+
;;
|
|
297
|
+
*)
|
|
298
|
+
info "Terminal: ${parent_term}"
|
|
299
|
+
;;
|
|
300
|
+
esac
|
|
301
|
+
echo ""
|
|
302
|
+
|
|
303
|
+
# ─── Summary ─────────────────────────────────────────────────────────
|
|
304
|
+
echo -e "${DIM} ──────────────────────────────────────────${RESET}"
|
|
305
|
+
echo -e " ${GREEN}${BOLD}${PASS} passed${RESET} ${YELLOW}${BOLD}${WARN} warnings${RESET} ${RED}${BOLD}${FAIL} failed${RESET}"
|
|
306
|
+
|
|
307
|
+
if [[ $FAIL -gt 0 ]]; then
|
|
308
|
+
echo ""
|
|
309
|
+
echo -e " Run ${CYAN}${BOLD}shipwright tmux fix${RESET} to auto-fix issues."
|
|
310
|
+
elif [[ $WARN -gt 0 ]]; then
|
|
311
|
+
echo ""
|
|
312
|
+
echo -e " ${GREEN}Good shape!${RESET} Warnings are informational."
|
|
313
|
+
else
|
|
314
|
+
echo ""
|
|
315
|
+
echo -e " ${GREEN}${BOLD}Perfect setup!${RESET} tmux + Claude Code fully optimized."
|
|
316
|
+
fi
|
|
317
|
+
echo ""
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
321
|
+
# tmux install — Install TPM and all plugins
|
|
322
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
323
|
+
|
|
324
|
+
tmux_install() {
|
|
325
|
+
echo ""
|
|
326
|
+
echo -e "${CYAN}${BOLD} Shipwright — tmux Plugin Installer${RESET}"
|
|
327
|
+
echo -e "${DIM} ══════════════════════════════════════════${RESET}"
|
|
328
|
+
echo ""
|
|
329
|
+
|
|
330
|
+
# Install TPM if not present
|
|
331
|
+
if [[ ! -d "$HOME/.tmux/plugins/tpm" ]]; then
|
|
332
|
+
info "Installing TPM (Tmux Plugin Manager)..."
|
|
333
|
+
if git clone https://github.com/tmux-plugins/tpm "$HOME/.tmux/plugins/tpm" 2>/dev/null; then
|
|
334
|
+
success "TPM installed"
|
|
335
|
+
else
|
|
336
|
+
error "Failed to clone TPM — check git + network"
|
|
337
|
+
return 1
|
|
338
|
+
fi
|
|
339
|
+
else
|
|
340
|
+
success "TPM already installed"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
# Install Shipwright tmux.conf if not present
|
|
344
|
+
if [[ -f "$REPO_DIR/tmux/tmux.conf" ]]; then
|
|
345
|
+
if [[ ! -f "$HOME/.tmux.conf" ]]; then
|
|
346
|
+
cp "$REPO_DIR/tmux/tmux.conf" "$HOME/.tmux.conf"
|
|
347
|
+
success "Installed ~/.tmux.conf"
|
|
348
|
+
else
|
|
349
|
+
info "~/.tmux.conf exists — run 'shipwright init' to update"
|
|
350
|
+
fi
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
# Install overlay
|
|
354
|
+
if [[ -f "$REPO_DIR/tmux/shipwright-overlay.conf" ]]; then
|
|
355
|
+
mkdir -p "$HOME/.tmux"
|
|
356
|
+
cp "$REPO_DIR/tmux/shipwright-overlay.conf" "$HOME/.tmux/shipwright-overlay.conf"
|
|
357
|
+
success "Installed ~/.tmux/shipwright-overlay.conf"
|
|
358
|
+
fi
|
|
359
|
+
|
|
360
|
+
# Trigger TPM plugin install
|
|
361
|
+
if [[ -x "$HOME/.tmux/plugins/tpm/bin/install_plugins" ]]; then
|
|
362
|
+
info "Installing tmux plugins via TPM..."
|
|
363
|
+
"$HOME/.tmux/plugins/tpm/bin/install_plugins" 2>/dev/null && \
|
|
364
|
+
success "All plugins installed" || \
|
|
365
|
+
warn "Some plugins may not have installed — press prefix + I inside tmux"
|
|
366
|
+
else
|
|
367
|
+
warn "TPM install script not found — press prefix + I inside tmux to install plugins"
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
# Reload config if inside tmux
|
|
371
|
+
if [[ -n "${TMUX:-}" ]]; then
|
|
372
|
+
tmux source-file "$HOME/.tmux.conf" 2>/dev/null && \
|
|
373
|
+
success "tmux config reloaded" || true
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
echo ""
|
|
377
|
+
success "tmux setup complete!"
|
|
378
|
+
echo ""
|
|
379
|
+
echo -e "${BOLD}Plugins installed:${RESET}"
|
|
380
|
+
echo -e " ${CYAN}tmux-sensible${RESET} — Sensible defaults everyone agrees on"
|
|
381
|
+
echo -e " ${CYAN}tmux-resurrect${RESET} — Persist sessions across restarts"
|
|
382
|
+
echo -e " ${CYAN}tmux-continuum${RESET} — Auto-save every 15 min, auto-restore"
|
|
383
|
+
echo -e " ${CYAN}tmux-yank${RESET} — System clipboard integration (OSC 52)"
|
|
384
|
+
echo -e " ${CYAN}tmux-fzf${RESET} — Fuzzy finder for sessions/windows/panes"
|
|
385
|
+
echo ""
|
|
386
|
+
echo -e "${BOLD}Key bindings added:${RESET}"
|
|
387
|
+
echo -e " ${CYAN}prefix + F${RESET} — Floating popup terminal"
|
|
388
|
+
echo -e " ${CYAN}prefix + C-f${RESET} — FZF session switcher"
|
|
389
|
+
echo -e " ${CYAN}prefix + T${RESET} — Launch Shipwright team session"
|
|
390
|
+
echo -e " ${CYAN}prefix + C-t${RESET} — Team dashboard popup"
|
|
391
|
+
echo -e " ${CYAN}prefix + M-d${RESET} — Full dashboard popup"
|
|
392
|
+
echo -e " ${CYAN}prefix + M-m${RESET} — Memory system popup"
|
|
393
|
+
echo -e " ${CYAN}prefix + R${RESET} — Reap dead agent panes"
|
|
394
|
+
echo ""
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
398
|
+
# tmux fix — Auto-fix common tmux + Claude Code issues
|
|
399
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
400
|
+
|
|
401
|
+
tmux_fix() {
|
|
402
|
+
echo ""
|
|
403
|
+
echo -e "${CYAN}${BOLD} Shipwright — tmux Auto-Fix${RESET}"
|
|
404
|
+
echo -e "${DIM} ══════════════════════════════════════════${RESET}"
|
|
405
|
+
echo ""
|
|
406
|
+
|
|
407
|
+
local fixed=0
|
|
408
|
+
|
|
409
|
+
if [[ -z "${TMUX:-}" ]]; then
|
|
410
|
+
error "Not inside a tmux session — start tmux first"
|
|
411
|
+
return 1
|
|
412
|
+
fi
|
|
413
|
+
|
|
414
|
+
# Fix 1: allow-passthrough
|
|
415
|
+
local passthrough
|
|
416
|
+
passthrough="$(tmux show-option -gv allow-passthrough 2>/dev/null || echo "off")"
|
|
417
|
+
if [[ "$passthrough" != "on" ]]; then
|
|
418
|
+
tmux set -g allow-passthrough on 2>/dev/null
|
|
419
|
+
success "Fixed: allow-passthrough → on (eliminates Claude Code flicker)"
|
|
420
|
+
fixed=$((fixed + 1))
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
# Fix 2: extended-keys
|
|
424
|
+
local extkeys
|
|
425
|
+
extkeys="$(tmux show-option -gv extended-keys 2>/dev/null || echo "off")"
|
|
426
|
+
if [[ "$extkeys" != "on" ]]; then
|
|
427
|
+
tmux set -g extended-keys on 2>/dev/null
|
|
428
|
+
success "Fixed: extended-keys → on"
|
|
429
|
+
fixed=$((fixed + 1))
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# Fix 3: escape-time
|
|
433
|
+
local esc_time
|
|
434
|
+
esc_time="$(tmux show-option -gv escape-time 2>/dev/null || echo "500")"
|
|
435
|
+
if [[ "$esc_time" -gt 10 ]]; then
|
|
436
|
+
tmux set -sg escape-time 0 2>/dev/null
|
|
437
|
+
success "Fixed: escape-time → 0 (was ${esc_time}ms)"
|
|
438
|
+
fixed=$((fixed + 1))
|
|
439
|
+
fi
|
|
440
|
+
|
|
441
|
+
# Fix 4: set-clipboard
|
|
442
|
+
local clipboard
|
|
443
|
+
clipboard="$(tmux show-option -gv set-clipboard 2>/dev/null || echo "off")"
|
|
444
|
+
if [[ "$clipboard" == "off" ]]; then
|
|
445
|
+
tmux set -g set-clipboard on 2>/dev/null
|
|
446
|
+
success "Fixed: set-clipboard → on"
|
|
447
|
+
fixed=$((fixed + 1))
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
# Fix 5: history-limit
|
|
451
|
+
local hist
|
|
452
|
+
hist="$(tmux show-option -gv history-limit 2>/dev/null || echo "2000")"
|
|
453
|
+
if [[ "$hist" -lt 100000 ]]; then
|
|
454
|
+
tmux set -g history-limit 250000 2>/dev/null
|
|
455
|
+
success "Fixed: history-limit → 250000 (was ${hist})"
|
|
456
|
+
fixed=$((fixed + 1))
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
# Fix 6: focus-events
|
|
460
|
+
local focus
|
|
461
|
+
focus="$(tmux show-option -gv focus-events 2>/dev/null || echo "off")"
|
|
462
|
+
if [[ "$focus" != "on" ]]; then
|
|
463
|
+
tmux set -g focus-events on 2>/dev/null
|
|
464
|
+
success "Fixed: focus-events → on"
|
|
465
|
+
fixed=$((fixed + 1))
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
# Fix 7: mouse
|
|
469
|
+
local mouse
|
|
470
|
+
mouse="$(tmux show-option -gv mouse 2>/dev/null || echo "off")"
|
|
471
|
+
if [[ "$mouse" != "on" ]]; then
|
|
472
|
+
tmux set -g mouse on 2>/dev/null
|
|
473
|
+
success "Fixed: mouse → on"
|
|
474
|
+
fixed=$((fixed + 1))
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
# Fix 8: MouseDown1Status
|
|
478
|
+
local mouse_bind
|
|
479
|
+
mouse_bind="$(tmux list-keys 2>/dev/null | grep 'MouseDown1Status' | head -1 || true)"
|
|
480
|
+
if ! echo "$mouse_bind" | grep -q "select-window"; then
|
|
481
|
+
tmux bind -T root MouseDown1Status select-window -t = 2>/dev/null
|
|
482
|
+
success "Fixed: MouseDown1Status → select-window"
|
|
483
|
+
fixed=$((fixed + 1))
|
|
484
|
+
fi
|
|
485
|
+
|
|
486
|
+
# Fix 9: dark theme hooks
|
|
487
|
+
if ! tmux show-hooks -g 2>/dev/null | grep -q "after-split-window"; then
|
|
488
|
+
tmux set-hook -g after-split-window "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'" 2>/dev/null
|
|
489
|
+
tmux set-hook -g after-new-window "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'" 2>/dev/null
|
|
490
|
+
tmux set-hook -g after-new-session "select-pane -P 'bg=#1a1a2e,fg=#e4e4e7'" 2>/dev/null
|
|
491
|
+
success "Fixed: dark theme hooks installed"
|
|
492
|
+
fixed=$((fixed + 1))
|
|
493
|
+
fi
|
|
494
|
+
|
|
495
|
+
# Fix 10: pane-border-status
|
|
496
|
+
local border_status
|
|
497
|
+
border_status="$(tmux show-option -gv pane-border-status 2>/dev/null || echo "off")"
|
|
498
|
+
if [[ "$border_status" != "top" ]]; then
|
|
499
|
+
tmux set -g pane-border-status top 2>/dev/null
|
|
500
|
+
success "Fixed: pane-border-status → top (agent names visible)"
|
|
501
|
+
fixed=$((fixed + 1))
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
echo ""
|
|
505
|
+
if [[ $fixed -eq 0 ]]; then
|
|
506
|
+
success "No fixes needed — tmux is already optimized!"
|
|
507
|
+
else
|
|
508
|
+
success "Applied ${fixed} fixes"
|
|
509
|
+
echo ""
|
|
510
|
+
echo -e "${DIM}These fixes are applied to the running session only.${RESET}"
|
|
511
|
+
echo -e "${DIM}For persistence, update your tmux config:${RESET}"
|
|
512
|
+
echo -e "${DIM} shipwright init${RESET}"
|
|
513
|
+
fi
|
|
514
|
+
echo ""
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
518
|
+
# tmux reload — Reload tmux configuration
|
|
519
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
520
|
+
|
|
521
|
+
tmux_reload() {
|
|
522
|
+
if [[ -z "${TMUX:-}" ]]; then
|
|
523
|
+
error "Not inside a tmux session"
|
|
524
|
+
return 1
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
if [[ -f "$HOME/.tmux.conf" ]]; then
|
|
528
|
+
tmux source-file "$HOME/.tmux.conf" 2>/dev/null && \
|
|
529
|
+
success "tmux config reloaded" || \
|
|
530
|
+
error "Failed to reload — check config syntax"
|
|
531
|
+
else
|
|
532
|
+
error "No ~/.tmux.conf found"
|
|
533
|
+
fi
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
537
|
+
# Main dispatcher
|
|
538
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
539
|
+
|
|
540
|
+
show_help() {
|
|
541
|
+
echo -e "${CYAN}${BOLD}shipwright tmux${RESET} — tmux Health & Plugin Management"
|
|
542
|
+
echo ""
|
|
543
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
544
|
+
echo -e " shipwright tmux <command>"
|
|
545
|
+
echo ""
|
|
546
|
+
echo -e "${BOLD}COMMANDS${RESET}"
|
|
547
|
+
echo -e " ${CYAN}doctor${RESET} Check tmux features + Claude Code compatibility"
|
|
548
|
+
echo -e " ${CYAN}install${RESET} Install TPM + all plugins"
|
|
549
|
+
echo -e " ${CYAN}fix${RESET} Auto-fix common issues in running session"
|
|
550
|
+
echo -e " ${CYAN}reload${RESET} Reload tmux configuration"
|
|
551
|
+
echo ""
|
|
552
|
+
echo -e "${BOLD}Claude Code Compatibility${RESET}"
|
|
553
|
+
echo -e " Shipwright configures tmux for optimal Claude Code TUI compatibility:"
|
|
554
|
+
echo -e " ${DIM}• allow-passthrough: DEC 2026 synchronized output (no flicker)${RESET}"
|
|
555
|
+
echo -e " ${DIM}• extended-keys: better key handling for TUI apps${RESET}"
|
|
556
|
+
echo -e " ${DIM}• escape-time 0: no input delay${RESET}"
|
|
557
|
+
echo -e " ${DIM}• history-limit 250000: handles Claude Code's high output volume${RESET}"
|
|
558
|
+
echo -e " ${DIM}• set-clipboard on: native OSC 52 clipboard${RESET}"
|
|
559
|
+
echo -e " ${DIM}• pane IDs (not indices): fixes teammate pane-base-index bug${RESET}"
|
|
560
|
+
echo ""
|
|
561
|
+
echo -e "${BOLD}Plugins${RESET}"
|
|
562
|
+
echo -e " ${DIM}tmux-sensible — Sensible defaults${RESET}"
|
|
563
|
+
echo -e " ${DIM}tmux-resurrect — Persist sessions across restarts${RESET}"
|
|
564
|
+
echo -e " ${DIM}tmux-continuum — Auto-save + auto-restore${RESET}"
|
|
565
|
+
echo -e " ${DIM}tmux-yank — System clipboard (OSC 52)${RESET}"
|
|
566
|
+
echo -e " ${DIM}tmux-fzf — Fuzzy finder for sessions/windows/panes${RESET}"
|
|
567
|
+
echo ""
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
case "${1:-}" in
|
|
571
|
+
doctor|check|status)
|
|
572
|
+
tmux_doctor
|
|
573
|
+
;;
|
|
574
|
+
install|setup)
|
|
575
|
+
tmux_install
|
|
576
|
+
;;
|
|
577
|
+
fix|repair)
|
|
578
|
+
tmux_fix
|
|
579
|
+
;;
|
|
580
|
+
reload)
|
|
581
|
+
tmux_reload
|
|
582
|
+
;;
|
|
583
|
+
--help|-h|help|"")
|
|
584
|
+
show_help
|
|
585
|
+
;;
|
|
586
|
+
*)
|
|
587
|
+
error "Unknown command: $1"
|
|
588
|
+
echo -e " Run ${CYAN}shipwright tmux --help${RESET} for usage."
|
|
589
|
+
exit 1
|
|
590
|
+
;;
|
|
591
|
+
esac
|