shipwright-cli 1.7.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/LICENSE +21 -0
- package/README.md +926 -0
- package/claude-code/CLAUDE.md.shipwright +125 -0
- package/claude-code/hooks/notify-idle.sh +35 -0
- package/claude-code/hooks/pre-compact-save.sh +57 -0
- package/claude-code/hooks/task-completed.sh +170 -0
- package/claude-code/hooks/teammate-idle.sh +68 -0
- package/claude-code/settings.json.template +184 -0
- package/completions/_shipwright +140 -0
- package/completions/shipwright.bash +89 -0
- package/completions/shipwright.fish +107 -0
- package/docs/KNOWN-ISSUES.md +199 -0
- package/docs/TIPS.md +331 -0
- package/docs/definition-of-done.example.md +16 -0
- package/docs/patterns/README.md +139 -0
- package/docs/patterns/audit-loop.md +149 -0
- package/docs/patterns/bug-hunt.md +183 -0
- package/docs/patterns/feature-implementation.md +159 -0
- package/docs/patterns/refactoring.md +183 -0
- package/docs/patterns/research-exploration.md +144 -0
- package/docs/patterns/test-generation.md +173 -0
- package/package.json +49 -0
- package/scripts/adapters/docker-deploy.sh +50 -0
- package/scripts/adapters/fly-deploy.sh +41 -0
- package/scripts/adapters/iterm2-adapter.sh +122 -0
- package/scripts/adapters/railway-deploy.sh +34 -0
- package/scripts/adapters/tmux-adapter.sh +87 -0
- package/scripts/adapters/vercel-deploy.sh +35 -0
- package/scripts/adapters/wezterm-adapter.sh +103 -0
- package/scripts/cct +242 -0
- package/scripts/cct-cleanup.sh +172 -0
- package/scripts/cct-cost.sh +590 -0
- package/scripts/cct-daemon.sh +3189 -0
- package/scripts/cct-doctor.sh +328 -0
- package/scripts/cct-fix.sh +478 -0
- package/scripts/cct-fleet.sh +904 -0
- package/scripts/cct-init.sh +282 -0
- package/scripts/cct-logs.sh +273 -0
- package/scripts/cct-loop.sh +1332 -0
- package/scripts/cct-memory.sh +1148 -0
- package/scripts/cct-pipeline.sh +3844 -0
- package/scripts/cct-prep.sh +1352 -0
- package/scripts/cct-ps.sh +168 -0
- package/scripts/cct-reaper.sh +390 -0
- package/scripts/cct-session.sh +284 -0
- package/scripts/cct-status.sh +169 -0
- package/scripts/cct-templates.sh +242 -0
- package/scripts/cct-upgrade.sh +422 -0
- package/scripts/cct-worktree.sh +405 -0
- package/scripts/postinstall.mjs +96 -0
- package/templates/pipelines/autonomous.json +71 -0
- package/templates/pipelines/cost-aware.json +95 -0
- package/templates/pipelines/deployed.json +79 -0
- package/templates/pipelines/enterprise.json +114 -0
- package/templates/pipelines/fast.json +63 -0
- package/templates/pipelines/full.json +104 -0
- package/templates/pipelines/hotfix.json +63 -0
- package/templates/pipelines/standard.json +91 -0
- package/tmux/claude-teams-overlay.conf +109 -0
- package/tmux/templates/architecture.json +19 -0
- package/tmux/templates/bug-fix.json +24 -0
- package/tmux/templates/code-review.json +24 -0
- package/tmux/templates/devops.json +19 -0
- package/tmux/templates/documentation.json +19 -0
- package/tmux/templates/exploration.json +19 -0
- package/tmux/templates/feature-dev.json +24 -0
- package/tmux/templates/full-stack.json +24 -0
- package/tmux/templates/migration.json +24 -0
- package/tmux/templates/refactor.json +19 -0
- package/tmux/templates/security-audit.json +24 -0
- package/tmux/templates/testing.json +24 -0
- package/tmux/tmux.conf +167 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ wezterm-adapter.sh — Terminal adapter for WezTerm pane management ║
|
|
4
|
+
# ║ ║
|
|
5
|
+
# ║ Uses `wezterm cli` to spawn panes/tabs with named titles and working ║
|
|
6
|
+
# ║ directories. Cross-platform. ║
|
|
7
|
+
# ║ Sourced by cct-session.sh — exports: spawn_agent, list_agents, ║
|
|
8
|
+
# ║ kill_agent, focus_agent. ║
|
|
9
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
10
|
+
|
|
11
|
+
# Verify wezterm CLI is available
|
|
12
|
+
if ! command -v wezterm &>/dev/null; then
|
|
13
|
+
echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m wezterm CLI not found. Install WezTerm first." >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Track spawned pane IDs for agent management
|
|
18
|
+
declare -A _WEZTERM_AGENT_PANES
|
|
19
|
+
|
|
20
|
+
spawn_agent() {
|
|
21
|
+
local name="$1"
|
|
22
|
+
local working_dir="${2:-$PWD}"
|
|
23
|
+
local command="${3:-}"
|
|
24
|
+
|
|
25
|
+
# Resolve working_dir — tmux format won't work here
|
|
26
|
+
if [[ "$working_dir" == *"pane_current_path"* || "$working_dir" == "." ]]; then
|
|
27
|
+
working_dir="$PWD"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
local pane_id
|
|
31
|
+
|
|
32
|
+
# Spawn a new pane in the current tab (split right by default)
|
|
33
|
+
if [[ ${#_WEZTERM_AGENT_PANES[@]} -eq 0 ]]; then
|
|
34
|
+
# First agent: create a new tab
|
|
35
|
+
pane_id=$(wezterm cli spawn --cwd "$working_dir" 2>/dev/null)
|
|
36
|
+
else
|
|
37
|
+
# Subsequent agents: split from the first pane
|
|
38
|
+
local first_pane="${_WEZTERM_AGENT_PANES[${!_WEZTERM_AGENT_PANES[*]}]}"
|
|
39
|
+
pane_id=$(wezterm cli split-pane --cwd "$working_dir" --right --pane-id "${first_pane:-0}" 2>/dev/null) || \
|
|
40
|
+
pane_id=$(wezterm cli split-pane --cwd "$working_dir" --bottom 2>/dev/null)
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [[ -z "$pane_id" ]]; then
|
|
44
|
+
echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m Failed to spawn WezTerm pane for '${name}'." >&2
|
|
45
|
+
return 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Store mapping
|
|
49
|
+
_WEZTERM_AGENT_PANES["$name"]="$pane_id"
|
|
50
|
+
|
|
51
|
+
# Set the pane title
|
|
52
|
+
wezterm cli set-tab-title --pane-id "$pane_id" "$name" 2>/dev/null || true
|
|
53
|
+
|
|
54
|
+
# Clear the pane
|
|
55
|
+
wezterm cli send-text --pane-id "$pane_id" -- "clear" 2>/dev/null
|
|
56
|
+
wezterm cli send-text --pane-id "$pane_id" --no-paste $'\n' 2>/dev/null || true
|
|
57
|
+
|
|
58
|
+
# Run the command if provided
|
|
59
|
+
if [[ -n "$command" ]]; then
|
|
60
|
+
sleep 0.2
|
|
61
|
+
wezterm cli send-text --pane-id "$pane_id" -- "$command" 2>/dev/null
|
|
62
|
+
wezterm cli send-text --pane-id "$pane_id" --no-paste $'\n' 2>/dev/null || true
|
|
63
|
+
fi
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
list_agents() {
|
|
67
|
+
# List panes via wezterm CLI
|
|
68
|
+
wezterm cli list 2>/dev/null | while IFS=$'\t' read -r pane_id title workspace rest; do
|
|
69
|
+
echo "${pane_id}: ${title}"
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
# Also show our tracked agents
|
|
73
|
+
if [[ ${#_WEZTERM_AGENT_PANES[@]} -gt 0 ]]; then
|
|
74
|
+
echo ""
|
|
75
|
+
echo "Tracked agents:"
|
|
76
|
+
for name in "${!_WEZTERM_AGENT_PANES[@]}"; do
|
|
77
|
+
echo " ${name} → pane ${_WEZTERM_AGENT_PANES[$name]}"
|
|
78
|
+
done
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
kill_agent() {
|
|
83
|
+
local name="$1"
|
|
84
|
+
local pane_id="${_WEZTERM_AGENT_PANES[$name]:-}"
|
|
85
|
+
|
|
86
|
+
if [[ -z "$pane_id" ]]; then
|
|
87
|
+
return 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
wezterm cli kill-pane --pane-id "$pane_id" 2>/dev/null
|
|
91
|
+
unset '_WEZTERM_AGENT_PANES[$name]'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
focus_agent() {
|
|
95
|
+
local name="$1"
|
|
96
|
+
local pane_id="${_WEZTERM_AGENT_PANES[$name]:-}"
|
|
97
|
+
|
|
98
|
+
if [[ -z "$pane_id" ]]; then
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
wezterm cli activate-pane --pane-id "$pane_id" 2>/dev/null
|
|
103
|
+
}
|
package/scripts/cct
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ Shipwright CLI (cct) ║
|
|
4
|
+
# ║ Orchestrate autonomous Claude Code agent teams in tmux ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
VERSION="1.7.0"
|
|
9
|
+
|
|
10
|
+
# Resolve symlinks (required for npm global install where bin/ symlinks to node_modules/)
|
|
11
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
12
|
+
while [[ -L "$SOURCE" ]]; do
|
|
13
|
+
DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
|
|
14
|
+
SOURCE="$(readlink "$SOURCE")"
|
|
15
|
+
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
16
|
+
done
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
|
|
18
|
+
|
|
19
|
+
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
|
20
|
+
CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
|
|
21
|
+
PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
|
|
22
|
+
BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
|
|
23
|
+
GREEN='\033[38;2;74;222;128m' # success
|
|
24
|
+
YELLOW='\033[38;2;250;204;21m' # warning
|
|
25
|
+
RED='\033[38;2;248;113;113m' # error
|
|
26
|
+
DIM='\033[2m'
|
|
27
|
+
BOLD='\033[1m'
|
|
28
|
+
RESET='\033[0m'
|
|
29
|
+
|
|
30
|
+
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
33
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
34
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
35
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
36
|
+
|
|
37
|
+
check_tmux() {
|
|
38
|
+
if ! command -v tmux &>/dev/null; then
|
|
39
|
+
error "tmux is not installed. Install it first:"
|
|
40
|
+
echo -e " ${DIM}brew install tmux${RESET} (macOS)"
|
|
41
|
+
echo -e " ${DIM}sudo apt install tmux${RESET} (Ubuntu/Debian)"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
check_in_tmux() {
|
|
47
|
+
if [[ -z "${TMUX:-}" ]]; then
|
|
48
|
+
warn "Not inside a tmux session."
|
|
49
|
+
echo -e " Start one with: ${DIM}tmux new -s work${RESET}"
|
|
50
|
+
echo -e " Or attach: ${DIM}tmux attach${RESET}"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
show_version() {
|
|
56
|
+
echo -e "${CYAN}${BOLD}shipwright${RESET} ${DIM}v${VERSION}${RESET} — Orchestrate AI Coding Teams ${DIM}(aliases: sw, cct)${RESET}"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
show_help() {
|
|
60
|
+
show_version
|
|
61
|
+
echo ""
|
|
62
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
63
|
+
echo -e " ${CYAN}shipwright${RESET} <command> [options] ${DIM}(also: sw, cct)${RESET}"
|
|
64
|
+
echo ""
|
|
65
|
+
echo -e "${BOLD}COMMANDS${RESET}"
|
|
66
|
+
echo -e " ${CYAN}session${RESET} [name] Create a new tmux window for a Claude team"
|
|
67
|
+
echo -e " ${CYAN}status${RESET} Show dashboard of running teams and agents"
|
|
68
|
+
echo -e " ${CYAN}ps${RESET} Show running agent processes and status"
|
|
69
|
+
echo -e " ${CYAN}logs${RESET} [team] [opts] View and search agent pane logs"
|
|
70
|
+
echo -e " ${CYAN}templates${RESET} [list|show] Manage team composition templates"
|
|
71
|
+
echo -e " ${CYAN}doctor${RESET} Validate your setup and check for issues"
|
|
72
|
+
echo -e " ${CYAN}cleanup${RESET} [--force] Clean up orphaned team sessions"
|
|
73
|
+
echo -e " ${CYAN}reaper${RESET} [--watch] Automatic pane cleanup when agents exit"
|
|
74
|
+
echo -e " ${CYAN}upgrade${RESET} [--apply] Check for updates from the repo (dry-run by default)"
|
|
75
|
+
echo -e " ${CYAN}loop${RESET} \"goal\" [opts] ${BOLD}Continuous agent loop${RESET} — run until goal is achieved"
|
|
76
|
+
echo -e " ${CYAN}pipeline${RESET} <cmd> ${BOLD}Full delivery pipeline${RESET} — idea to production"
|
|
77
|
+
echo -e " ${CYAN}worktree${RESET} <cmd> Manage git worktrees for agent isolation"
|
|
78
|
+
echo -e " ${CYAN}prep${RESET} [options] ${BOLD}Repo preparation${RESET} — generate .claude/ configs for agents"
|
|
79
|
+
echo -e " ${CYAN}daemon${RESET} <cmd> ${BOLD}Issue watcher${RESET} — auto-process GitHub issues"
|
|
80
|
+
echo -e " ${CYAN}memory${RESET} <cmd> ${BOLD}Persistent memory${RESET} — learn from every pipeline run"
|
|
81
|
+
echo -e " ${CYAN}cost${RESET} <cmd> ${BOLD}Cost intelligence${RESET} — track tokens, budgets, model routing"
|
|
82
|
+
echo -e " ${CYAN}fleet${RESET} <cmd> ${BOLD}Multi-repo fleet${RESET} — orchestrate daemons across repos"
|
|
83
|
+
echo -e " ${CYAN}fix${RESET} \"goal\" [opts] ${BOLD}Bulk fix${RESET} — apply a fix across multiple repos"
|
|
84
|
+
echo -e " ${CYAN}init${RESET} ${BOLD}Quick tmux setup${RESET} — one command, no prompts"
|
|
85
|
+
echo -e " ${CYAN}help${RESET} Show this help message"
|
|
86
|
+
echo -e " ${CYAN}version${RESET} Show version"
|
|
87
|
+
echo ""
|
|
88
|
+
echo -e "${BOLD}CONTINUOUS LOOP${RESET} ${DIM}(autonomous agent operation)${RESET}"
|
|
89
|
+
echo -e " ${DIM}shipwright loop \"Build auth\" --test-cmd \"npm test\"${RESET}"
|
|
90
|
+
echo -e " ${DIM}shipwright loop \"Fix bugs\" --max-iterations 10 --model sonnet${RESET}"
|
|
91
|
+
echo -e " ${DIM}shipwright loop \"Build API\" --agents 3 --skip-permissions${RESET}"
|
|
92
|
+
echo -e " ${DIM}shipwright loop --resume${RESET} # Resume interrupted loop"
|
|
93
|
+
echo ""
|
|
94
|
+
echo -e "${BOLD}DELIVERY PIPELINE${RESET} ${DIM}(idea to production)${RESET}"
|
|
95
|
+
echo -e " ${DIM}shipwright pipeline start --goal \"Add auth\" --pipeline standard${RESET}"
|
|
96
|
+
echo -e " ${DIM}shipwright pipeline start --issue 123 --skip-gates${RESET}"
|
|
97
|
+
echo -e " ${DIM}shipwright pipeline resume${RESET} # Resume from last stage"
|
|
98
|
+
echo -e " ${DIM}shipwright pipeline status${RESET} # Show progress dashboard"
|
|
99
|
+
echo ""
|
|
100
|
+
echo -e "${BOLD}AUTONOMOUS DAEMON${RESET} ${DIM}(watch GitHub, auto-deliver)${RESET}"
|
|
101
|
+
echo -e " ${DIM}shipwright daemon start${RESET} # Start issue watcher (foreground)"
|
|
102
|
+
echo -e " ${DIM}shipwright daemon start --detach${RESET} # Start in background tmux session"
|
|
103
|
+
echo -e " ${DIM}shipwright daemon status${RESET} # Show active pipelines and queue"
|
|
104
|
+
echo -e " ${DIM}shipwright daemon metrics${RESET} # DORA/DX metrics dashboard"
|
|
105
|
+
echo -e " ${DIM}shipwright daemon stop${RESET} # Graceful shutdown"
|
|
106
|
+
echo ""
|
|
107
|
+
echo -e "${BOLD}MEMORY & COST${RESET} ${DIM}(intelligence layer)${RESET}"
|
|
108
|
+
echo -e " ${DIM}shipwright memory show${RESET} # Show learned patterns for this repo"
|
|
109
|
+
echo -e " ${DIM}shipwright memory search \"auth\"${RESET} # Search across all memories"
|
|
110
|
+
echo -e " ${DIM}shipwright memory stats${RESET} # Memory usage and coverage"
|
|
111
|
+
echo -e " ${DIM}shipwright cost show${RESET} # Token usage and spend overview"
|
|
112
|
+
echo -e " ${DIM}shipwright cost budget set 10.00${RESET} # Set daily budget"
|
|
113
|
+
echo -e " ${DIM}shipwright cost show --period 30 --by-stage${RESET} # Detailed cost report"
|
|
114
|
+
echo ""
|
|
115
|
+
echo -e "${BOLD}FLEET OPERATIONS${RESET} ${DIM}(multi-repo management)${RESET}"
|
|
116
|
+
echo -e " ${DIM}shipwright fleet start${RESET} # Start daemons for all configured repos"
|
|
117
|
+
echo -e " ${DIM}shipwright fleet status${RESET} # Show fleet-wide status"
|
|
118
|
+
echo -e " ${DIM}shipwright fleet metrics${RESET} # Cross-repo DORA metrics"
|
|
119
|
+
echo -e " ${DIM}shipwright fix \"Bump deps\" --repos ~/api,~/web${RESET} # Bulk fix"
|
|
120
|
+
echo ""
|
|
121
|
+
echo -e "${BOLD}REPO PREPARATION${RESET} ${DIM}(prep repo for agents)${RESET}"
|
|
122
|
+
echo -e " ${DIM}shipwright prep${RESET} # Analyze repo, generate .claude/ configs"
|
|
123
|
+
echo -e " ${DIM}shipwright prep --check${RESET} # Audit existing prep quality"
|
|
124
|
+
echo -e " ${DIM}shipwright prep --with-claude${RESET} # Deep analysis using Claude Code"
|
|
125
|
+
echo ""
|
|
126
|
+
echo -e "${BOLD}TEST SUITES${RESET} ${DIM}(validate shipwright components)${RESET}"
|
|
127
|
+
echo -e " ${DIM}shipwright daemon test${RESET} # Run daemon test suite"
|
|
128
|
+
echo -e " ${DIM}shipwright prep test${RESET} # Run prep test suite"
|
|
129
|
+
echo -e " ${DIM}shipwright pipeline test${RESET} # Run pipeline test suite"
|
|
130
|
+
echo ""
|
|
131
|
+
echo -e "${BOLD}AUDIT & QUALITY${RESET} ${DIM}(loop guardrails)${RESET}"
|
|
132
|
+
echo -e " ${DIM}shipwright loop \"goal\" --audit${RESET} # Self-reflection each iteration"
|
|
133
|
+
echo -e " ${DIM}shipwright loop \"goal\" --audit-agent${RESET} # Separate auditor reviews work"
|
|
134
|
+
echo -e " ${DIM}shipwright loop \"goal\" --quality-gates${RESET} # Automated quality checks"
|
|
135
|
+
echo -e " ${DIM}shipwright loop \"goal\" --definition-of-done dod.md${RESET} # Custom completion checklist"
|
|
136
|
+
echo ""
|
|
137
|
+
echo -e "${BOLD}TMUX KEYBINDINGS${RESET} ${DIM}(with the bundled tmux.conf)${RESET}"
|
|
138
|
+
echo -e " ${PURPLE}prefix + T${RESET} Launch a team session (runs ${DIM}shipwright session${RESET})"
|
|
139
|
+
echo -e " ${PURPLE}prefix + Ctrl-t${RESET} Show team dashboard (runs ${DIM}shipwright status${RESET})"
|
|
140
|
+
echo ""
|
|
141
|
+
echo -e "${BOLD}EXAMPLES${RESET}"
|
|
142
|
+
echo -e " ${DIM}shipwright session refactor${RESET} # New window named 'claude-refactor'"
|
|
143
|
+
echo -e " ${DIM}shipwright session${RESET} # New window with timestamp name"
|
|
144
|
+
echo -e " ${DIM}shipwright status${RESET} # See all active teams"
|
|
145
|
+
echo -e " ${DIM}shipwright ps${RESET} # Show agent processes"
|
|
146
|
+
echo -e " ${DIM}shipwright logs myteam${RESET} # View logs for a team"
|
|
147
|
+
echo -e " ${DIM}shipwright logs myteam --follow${RESET} # Tail agent logs"
|
|
148
|
+
echo -e " ${DIM}shipwright doctor${RESET} # Check setup health"
|
|
149
|
+
echo -e " ${DIM}shipwright cleanup${RESET} # Dry-run: show what would be cleaned"
|
|
150
|
+
echo -e " ${DIM}shipwright cleanup --force${RESET} # Actually kill orphaned sessions"
|
|
151
|
+
echo -e " ${DIM}shipwright upgrade${RESET} # Check what's changed since install"
|
|
152
|
+
echo -e " ${DIM}shipwright upgrade --apply${RESET} # Apply available updates"
|
|
153
|
+
echo ""
|
|
154
|
+
echo -e "${DIM}Docs: https://sethdford.github.io/shipwright | GitHub: https://github.com/sethdford/shipwright${RESET}"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# ─── Command Router ──────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
main() {
|
|
160
|
+
local cmd="${1:-help}"
|
|
161
|
+
shift 2>/dev/null || true
|
|
162
|
+
|
|
163
|
+
case "$cmd" in
|
|
164
|
+
session)
|
|
165
|
+
check_tmux
|
|
166
|
+
check_in_tmux
|
|
167
|
+
exec "$SCRIPT_DIR/cct-session.sh" "$@"
|
|
168
|
+
;;
|
|
169
|
+
status)
|
|
170
|
+
check_tmux
|
|
171
|
+
exec "$SCRIPT_DIR/cct-status.sh" "$@"
|
|
172
|
+
;;
|
|
173
|
+
ps)
|
|
174
|
+
check_tmux
|
|
175
|
+
exec "$SCRIPT_DIR/cct-ps.sh" "$@"
|
|
176
|
+
;;
|
|
177
|
+
logs)
|
|
178
|
+
exec "$SCRIPT_DIR/cct-logs.sh" "$@"
|
|
179
|
+
;;
|
|
180
|
+
templates)
|
|
181
|
+
exec "$SCRIPT_DIR/cct-templates.sh" "$@"
|
|
182
|
+
;;
|
|
183
|
+
doctor)
|
|
184
|
+
exec "$SCRIPT_DIR/cct-doctor.sh" "$@"
|
|
185
|
+
;;
|
|
186
|
+
cleanup)
|
|
187
|
+
check_tmux
|
|
188
|
+
exec "$SCRIPT_DIR/cct-cleanup.sh" "$@"
|
|
189
|
+
;;
|
|
190
|
+
reaper)
|
|
191
|
+
check_tmux
|
|
192
|
+
exec "$SCRIPT_DIR/cct-reaper.sh" "$@"
|
|
193
|
+
;;
|
|
194
|
+
upgrade)
|
|
195
|
+
exec "$SCRIPT_DIR/cct-upgrade.sh" "$@"
|
|
196
|
+
;;
|
|
197
|
+
loop)
|
|
198
|
+
exec "$SCRIPT_DIR/cct-loop.sh" "$@"
|
|
199
|
+
;;
|
|
200
|
+
pipeline)
|
|
201
|
+
exec "$SCRIPT_DIR/cct-pipeline.sh" "$@"
|
|
202
|
+
;;
|
|
203
|
+
worktree)
|
|
204
|
+
exec "$SCRIPT_DIR/cct-worktree.sh" "$@"
|
|
205
|
+
;;
|
|
206
|
+
prep)
|
|
207
|
+
exec "$SCRIPT_DIR/cct-prep.sh" "$@"
|
|
208
|
+
;;
|
|
209
|
+
daemon)
|
|
210
|
+
exec "$SCRIPT_DIR/cct-daemon.sh" "$@"
|
|
211
|
+
;;
|
|
212
|
+
memory)
|
|
213
|
+
exec "$SCRIPT_DIR/cct-memory.sh" "$@"
|
|
214
|
+
;;
|
|
215
|
+
cost)
|
|
216
|
+
exec "$SCRIPT_DIR/cct-cost.sh" "$@"
|
|
217
|
+
;;
|
|
218
|
+
fleet)
|
|
219
|
+
exec "$SCRIPT_DIR/cct-fleet.sh" "$@"
|
|
220
|
+
;;
|
|
221
|
+
fix)
|
|
222
|
+
exec "$SCRIPT_DIR/cct-fix.sh" "$@"
|
|
223
|
+
;;
|
|
224
|
+
init)
|
|
225
|
+
exec "$SCRIPT_DIR/cct-init.sh" "$@"
|
|
226
|
+
;;
|
|
227
|
+
help|--help|-h)
|
|
228
|
+
show_help
|
|
229
|
+
;;
|
|
230
|
+
version|--version|-v)
|
|
231
|
+
show_version
|
|
232
|
+
;;
|
|
233
|
+
*)
|
|
234
|
+
error "Unknown command: ${cmd}"
|
|
235
|
+
echo ""
|
|
236
|
+
show_help
|
|
237
|
+
exit 1
|
|
238
|
+
;;
|
|
239
|
+
esac
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main "$@"
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ cct-cleanup.sh — Clean up orphaned Claude team sessions ║
|
|
4
|
+
# ║ ║
|
|
5
|
+
# ║ Default: dry-run (shows what would be cleaned). ║
|
|
6
|
+
# ║ Use --force to actually kill sessions and remove files. ║
|
|
7
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# ─── Colors ──────────────────────────────────────────────────────────────────
|
|
11
|
+
CYAN='\033[38;2;0;212;255m'
|
|
12
|
+
PURPLE='\033[38;2;124;58;237m'
|
|
13
|
+
GREEN='\033[38;2;74;222;128m'
|
|
14
|
+
YELLOW='\033[38;2;250;204;21m'
|
|
15
|
+
RED='\033[38;2;248;113;113m'
|
|
16
|
+
DIM='\033[2m'
|
|
17
|
+
BOLD='\033[1m'
|
|
18
|
+
RESET='\033[0m'
|
|
19
|
+
|
|
20
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
21
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
22
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
23
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
24
|
+
|
|
25
|
+
# ─── Parse Args ──────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
FORCE=false
|
|
28
|
+
for arg in "$@"; do
|
|
29
|
+
case "$arg" in
|
|
30
|
+
--force|-f) FORCE=true ;;
|
|
31
|
+
--help|-h)
|
|
32
|
+
echo -e "${CYAN}${BOLD}shipwright cleanup${RESET} — Clean up orphaned Claude team sessions"
|
|
33
|
+
echo ""
|
|
34
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
35
|
+
echo -e " shipwright cleanup ${DIM}# Dry-run: show what would be cleaned${RESET}"
|
|
36
|
+
echo -e " shipwright cleanup --force ${DIM}# Actually kill sessions and remove files${RESET}"
|
|
37
|
+
exit 0
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
error "Unknown option: ${arg}"
|
|
41
|
+
exit 1
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
# ─── Track cleanup stats ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
WINDOWS_FOUND=0
|
|
49
|
+
WINDOWS_KILLED=0
|
|
50
|
+
TEAM_DIRS_FOUND=0
|
|
51
|
+
TEAM_DIRS_REMOVED=0
|
|
52
|
+
TASK_DIRS_FOUND=0
|
|
53
|
+
TASK_DIRS_REMOVED=0
|
|
54
|
+
|
|
55
|
+
# ─── 1. Find orphaned tmux windows ──────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
echo ""
|
|
58
|
+
if $FORCE; then
|
|
59
|
+
info "Cleaning up Claude team sessions ${RED}${BOLD}(FORCE MODE)${RESET}"
|
|
60
|
+
else
|
|
61
|
+
info "Scanning for orphaned Claude team sessions ${DIM}(dry-run)${RESET}"
|
|
62
|
+
fi
|
|
63
|
+
echo ""
|
|
64
|
+
|
|
65
|
+
echo -e "${BOLD}Tmux Windows${RESET}"
|
|
66
|
+
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
67
|
+
|
|
68
|
+
# Look for windows with "claude" in the name across all sessions
|
|
69
|
+
CLAUDE_WINDOWS=()
|
|
70
|
+
while IFS= read -r line; do
|
|
71
|
+
[[ -n "$line" ]] && CLAUDE_WINDOWS+=("$line")
|
|
72
|
+
done < <(tmux list-windows -a -F '#{session_name}:#{window_index} #{window_name}' 2>/dev/null | grep -i "claude" || true)
|
|
73
|
+
|
|
74
|
+
if [[ ${#CLAUDE_WINDOWS[@]} -eq 0 ]]; then
|
|
75
|
+
echo -e " ${DIM}No Claude team windows found.${RESET}"
|
|
76
|
+
else
|
|
77
|
+
for win in "${CLAUDE_WINDOWS[@]}"; do
|
|
78
|
+
WINDOWS_FOUND=$((WINDOWS_FOUND + 1))
|
|
79
|
+
local_target="$(echo "$win" | cut -d' ' -f1)"
|
|
80
|
+
local_name="$(echo "$win" | cut -d' ' -f2-)"
|
|
81
|
+
|
|
82
|
+
if $FORCE; then
|
|
83
|
+
tmux kill-window -t "$local_target" 2>/dev/null && {
|
|
84
|
+
echo -e " ${RED}✗${RESET} Killed: ${local_name} ${DIM}(${local_target})${RESET}"
|
|
85
|
+
WINDOWS_KILLED=$((WINDOWS_KILLED + 1))
|
|
86
|
+
} || {
|
|
87
|
+
warn " Could not kill: ${local_name} (${local_target})"
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
echo -e " ${YELLOW}○${RESET} Would kill: ${local_name} ${DIM}(${local_target})${RESET}"
|
|
91
|
+
fi
|
|
92
|
+
done
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# ─── 2. Clean up ~/.claude/teams/ ───────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
echo ""
|
|
98
|
+
echo -e "${BOLD}Team Configs${RESET} ${DIM}~/.claude/teams/${RESET}"
|
|
99
|
+
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
100
|
+
|
|
101
|
+
TEAMS_DIR="${HOME}/.claude/teams"
|
|
102
|
+
if [[ -d "$TEAMS_DIR" ]]; then
|
|
103
|
+
while IFS= read -r team_dir; do
|
|
104
|
+
[[ -z "$team_dir" ]] && continue
|
|
105
|
+
TEAM_DIRS_FOUND=$((TEAM_DIRS_FOUND + 1))
|
|
106
|
+
team_name="$(basename "$team_dir")"
|
|
107
|
+
|
|
108
|
+
if $FORCE; then
|
|
109
|
+
rm -rf "$team_dir" && {
|
|
110
|
+
echo -e " ${RED}✗${RESET} Removed: ${team_name}/"
|
|
111
|
+
TEAM_DIRS_REMOVED=$((TEAM_DIRS_REMOVED + 1))
|
|
112
|
+
}
|
|
113
|
+
else
|
|
114
|
+
# Count files inside
|
|
115
|
+
file_count=$(find "$team_dir" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
116
|
+
echo -e " ${YELLOW}○${RESET} Would remove: ${team_name}/ ${DIM}(${file_count} files)${RESET}"
|
|
117
|
+
fi
|
|
118
|
+
done < <(find "$TEAMS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
119
|
+
else
|
|
120
|
+
echo -e " ${DIM}No team configs found.${RESET}"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# ─── 3. Clean up ~/.claude/tasks/ ───────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
echo ""
|
|
126
|
+
echo -e "${BOLD}Task Lists${RESET} ${DIM}~/.claude/tasks/${RESET}"
|
|
127
|
+
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
128
|
+
|
|
129
|
+
TASKS_DIR="${HOME}/.claude/tasks"
|
|
130
|
+
if [[ -d "$TASKS_DIR" ]]; then
|
|
131
|
+
while IFS= read -r task_dir; do
|
|
132
|
+
[[ -z "$task_dir" ]] && continue
|
|
133
|
+
TASK_DIRS_FOUND=$((TASK_DIRS_FOUND + 1))
|
|
134
|
+
task_name="$(basename "$task_dir")"
|
|
135
|
+
|
|
136
|
+
if $FORCE; then
|
|
137
|
+
rm -rf "$task_dir" && {
|
|
138
|
+
echo -e " ${RED}✗${RESET} Removed: ${task_name}/"
|
|
139
|
+
TASK_DIRS_REMOVED=$((TASK_DIRS_REMOVED + 1))
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
task_count=$(find "$task_dir" -type f -name '*.json' 2>/dev/null | wc -l | tr -d ' ')
|
|
143
|
+
echo -e " ${YELLOW}○${RESET} Would remove: ${task_name}/ ${DIM}(${task_count} tasks)${RESET}"
|
|
144
|
+
fi
|
|
145
|
+
done < <(find "$TASKS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
146
|
+
else
|
|
147
|
+
echo -e " ${DIM}No task directories found.${RESET}"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# ─── Summary ─────────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
echo ""
|
|
153
|
+
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
154
|
+
|
|
155
|
+
TOTAL_FOUND=$((WINDOWS_FOUND + TEAM_DIRS_FOUND + TASK_DIRS_FOUND))
|
|
156
|
+
|
|
157
|
+
if $FORCE; then
|
|
158
|
+
TOTAL_CLEANED=$((WINDOWS_KILLED + TEAM_DIRS_REMOVED + TASK_DIRS_REMOVED))
|
|
159
|
+
if [[ $TOTAL_CLEANED -gt 0 ]]; then
|
|
160
|
+
success "Cleaned ${TOTAL_CLEANED} items (${WINDOWS_KILLED} windows, ${TEAM_DIRS_REMOVED} team dirs, ${TASK_DIRS_REMOVED} task dirs)"
|
|
161
|
+
else
|
|
162
|
+
success "Nothing to clean up."
|
|
163
|
+
fi
|
|
164
|
+
else
|
|
165
|
+
if [[ $TOTAL_FOUND -gt 0 ]]; then
|
|
166
|
+
warn "Found ${TOTAL_FOUND} items to clean. Run with ${BOLD}--force${RESET} to remove them:"
|
|
167
|
+
echo -e " ${DIM}shipwright cleanup --force${RESET}"
|
|
168
|
+
else
|
|
169
|
+
success "Everything is clean. No orphaned sessions found."
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
echo ""
|