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
package/scripts/cct-init.sh
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
-
# ║ shipwright init — One-command tmux setup + optional deploy configuration ║
|
|
4
|
-
# ║ ║
|
|
5
|
-
# ║ Installs tmux config, overlay, and templates. No interactive prompts, ║
|
|
6
|
-
# ║ no hooks, no Claude Code settings — just tmux config. ║
|
|
7
|
-
# ║ ║
|
|
8
|
-
# ║ --deploy Detect platform and generate deployed.json template ║
|
|
9
|
-
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
-
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
14
|
-
ADAPTERS_DIR="$SCRIPT_DIR/adapters"
|
|
15
|
-
|
|
16
|
-
# ─── Colors ──────────────────────────────────────────────────────────────────
|
|
17
|
-
CYAN='\033[38;2;0;212;255m'
|
|
18
|
-
GREEN='\033[38;2;74;222;128m'
|
|
19
|
-
YELLOW='\033[38;2;250;204;21m'
|
|
20
|
-
RED='\033[38;2;248;113;113m'
|
|
21
|
-
DIM='\033[2m'
|
|
22
|
-
BOLD='\033[1m'
|
|
23
|
-
RESET='\033[0m'
|
|
24
|
-
|
|
25
|
-
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
26
|
-
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
27
|
-
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
28
|
-
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
29
|
-
|
|
30
|
-
# ─── Flag parsing ───────────────────────────────────────────────────────────
|
|
31
|
-
DEPLOY_SETUP=false
|
|
32
|
-
DEPLOY_PLATFORM=""
|
|
33
|
-
SKIP_CLAUDE_MD=false
|
|
34
|
-
|
|
35
|
-
while [[ $# -gt 0 ]]; do
|
|
36
|
-
case "$1" in
|
|
37
|
-
--deploy)
|
|
38
|
-
DEPLOY_SETUP=true
|
|
39
|
-
shift
|
|
40
|
-
;;
|
|
41
|
-
--platform)
|
|
42
|
-
DEPLOY_PLATFORM="${2:-}"
|
|
43
|
-
[[ -z "$DEPLOY_PLATFORM" ]] && { error "Missing value for --platform"; exit 1; }
|
|
44
|
-
shift 2
|
|
45
|
-
;;
|
|
46
|
-
--no-claude-md)
|
|
47
|
-
SKIP_CLAUDE_MD=true
|
|
48
|
-
shift
|
|
49
|
-
;;
|
|
50
|
-
--help|-h)
|
|
51
|
-
echo "Usage: shipwright init [--deploy] [--platform vercel|fly|railway|docker] [--no-claude-md]"
|
|
52
|
-
echo ""
|
|
53
|
-
echo "Options:"
|
|
54
|
-
echo " --deploy Detect deploy platform and generate deployed.json"
|
|
55
|
-
echo " --platform PLATFORM Skip detection, use specified platform"
|
|
56
|
-
echo " --no-claude-md Skip creating .claude/CLAUDE.md"
|
|
57
|
-
echo " --help, -h Show this help"
|
|
58
|
-
exit 0
|
|
59
|
-
;;
|
|
60
|
-
*)
|
|
61
|
-
warn "Unknown option: $1"
|
|
62
|
-
shift
|
|
63
|
-
;;
|
|
64
|
-
esac
|
|
65
|
-
done
|
|
66
|
-
|
|
67
|
-
echo ""
|
|
68
|
-
echo -e "${CYAN}${BOLD}shipwright init${RESET} — Quick tmux setup"
|
|
69
|
-
echo -e "${DIM}══════════════════════════════════════════${RESET}"
|
|
70
|
-
echo ""
|
|
71
|
-
|
|
72
|
-
# ─── tmux.conf ────────────────────────────────────────────────────────────────
|
|
73
|
-
if [[ -f "$HOME/.tmux.conf" ]]; then
|
|
74
|
-
cp "$HOME/.tmux.conf" "$HOME/.tmux.conf.bak"
|
|
75
|
-
warn "Backed up existing ~/.tmux.conf → ~/.tmux.conf.bak"
|
|
76
|
-
fi
|
|
77
|
-
cp "$REPO_DIR/tmux/tmux.conf" "$HOME/.tmux.conf"
|
|
78
|
-
success "Installed ~/.tmux.conf"
|
|
79
|
-
|
|
80
|
-
# ─── Overlay ──────────────────────────────────────────────────────────────────
|
|
81
|
-
mkdir -p "$HOME/.tmux"
|
|
82
|
-
cp "$REPO_DIR/tmux/claude-teams-overlay.conf" "$HOME/.tmux/claude-teams-overlay.conf"
|
|
83
|
-
success "Installed ~/.tmux/claude-teams-overlay.conf"
|
|
84
|
-
|
|
85
|
-
# ─── Templates ────────────────────────────────────────────────────────────────
|
|
86
|
-
mkdir -p "$HOME/.claude-teams/templates"
|
|
87
|
-
for tpl in "$REPO_DIR"/tmux/templates/*.json; do
|
|
88
|
-
[[ -f "$tpl" ]] || continue
|
|
89
|
-
cp "$tpl" "$HOME/.claude-teams/templates/$(basename "$tpl")"
|
|
90
|
-
done
|
|
91
|
-
success "Installed templates → ~/.claude-teams/templates/"
|
|
92
|
-
|
|
93
|
-
# ─── CLAUDE.md — Agent instructions ──────────────────────────────────────────
|
|
94
|
-
CLAUDE_MD_SRC="$REPO_DIR/claude-code/CLAUDE.md.shipwright"
|
|
95
|
-
CLAUDE_MD_DST=".claude/CLAUDE.md"
|
|
96
|
-
|
|
97
|
-
if [[ "$SKIP_CLAUDE_MD" == "false" && -f "$CLAUDE_MD_SRC" ]]; then
|
|
98
|
-
if [[ -f "$CLAUDE_MD_DST" ]]; then
|
|
99
|
-
# Check if it already contains Shipwright instructions
|
|
100
|
-
if grep -q "Shipwright" "$CLAUDE_MD_DST" 2>/dev/null; then
|
|
101
|
-
info "CLAUDE.md already contains Shipwright instructions — skipping"
|
|
102
|
-
else
|
|
103
|
-
# Append Shipwright section to existing CLAUDE.md
|
|
104
|
-
{
|
|
105
|
-
echo ""
|
|
106
|
-
echo "---"
|
|
107
|
-
echo ""
|
|
108
|
-
cat "$CLAUDE_MD_SRC"
|
|
109
|
-
} >> "$CLAUDE_MD_DST"
|
|
110
|
-
success "Appended Shipwright instructions to ${CLAUDE_MD_DST}"
|
|
111
|
-
fi
|
|
112
|
-
else
|
|
113
|
-
mkdir -p ".claude"
|
|
114
|
-
cp "$CLAUDE_MD_SRC" "$CLAUDE_MD_DST"
|
|
115
|
-
success "Created ${CLAUDE_MD_DST} with Shipwright agent instructions"
|
|
116
|
-
fi
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
# ─── Reload tmux if inside a session ──────────────────────────────────────────
|
|
120
|
-
if [[ -n "${TMUX:-}" ]]; then
|
|
121
|
-
tmux source-file "$HOME/.tmux.conf" 2>/dev/null && \
|
|
122
|
-
success "Reloaded tmux config" || \
|
|
123
|
-
warn "Could not reload tmux config (reload manually with prefix + r)"
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
# ─── Quick-start instructions ─────────────────────────────────────────────────
|
|
127
|
-
echo ""
|
|
128
|
-
echo -e "${BOLD}Done!${RESET} tmux is configured for Claude Code Teams."
|
|
129
|
-
echo ""
|
|
130
|
-
echo -e "${BOLD}Quick start:${RESET}"
|
|
131
|
-
if [[ -z "${TMUX:-}" ]]; then
|
|
132
|
-
echo -e " ${DIM}1.${RESET} tmux new -s dev"
|
|
133
|
-
echo -e " ${DIM}2.${RESET} shipwright session my-feature --template feature-dev"
|
|
134
|
-
else
|
|
135
|
-
echo -e " ${DIM}1.${RESET} shipwright session my-feature --template feature-dev"
|
|
136
|
-
fi
|
|
137
|
-
echo ""
|
|
138
|
-
echo -e "${BOLD}Layout keybindings:${RESET}"
|
|
139
|
-
echo -e " ${CYAN}prefix + M-1${RESET} main-horizontal (leader 65% left)"
|
|
140
|
-
echo -e " ${CYAN}prefix + M-2${RESET} main-vertical (leader 60% top)"
|
|
141
|
-
echo -e " ${CYAN}prefix + M-3${RESET} tiled (equal sizes)"
|
|
142
|
-
echo ""
|
|
143
|
-
|
|
144
|
-
# ─── Deploy setup (--deploy) ─────────────────────────────────────────────────
|
|
145
|
-
[[ "$DEPLOY_SETUP" == "false" ]] && exit 0
|
|
146
|
-
|
|
147
|
-
echo -e "${CYAN}${BOLD}Deploy Setup${RESET}"
|
|
148
|
-
echo -e "${DIM}══════════════════════════════════════════${RESET}"
|
|
149
|
-
echo ""
|
|
150
|
-
|
|
151
|
-
# Platform detection
|
|
152
|
-
detect_deploy_platform() {
|
|
153
|
-
local detected=""
|
|
154
|
-
|
|
155
|
-
for adapter_file in "$ADAPTERS_DIR"/*-deploy.sh; do
|
|
156
|
-
[[ -f "$adapter_file" ]] || continue
|
|
157
|
-
# Source the adapter in a subshell to get detection
|
|
158
|
-
if ( source "$adapter_file" && detect_platform ); then
|
|
159
|
-
local name
|
|
160
|
-
name=$(basename "$adapter_file" | sed 's/-deploy\.sh$//')
|
|
161
|
-
if [[ -n "$detected" ]]; then
|
|
162
|
-
detected="$detected $name"
|
|
163
|
-
else
|
|
164
|
-
detected="$name"
|
|
165
|
-
fi
|
|
166
|
-
fi
|
|
167
|
-
done
|
|
168
|
-
|
|
169
|
-
echo "$detected"
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if [[ -n "$DEPLOY_PLATFORM" ]]; then
|
|
173
|
-
# User specified --platform, validate it
|
|
174
|
-
if [[ ! -f "$ADAPTERS_DIR/${DEPLOY_PLATFORM}-deploy.sh" ]]; then
|
|
175
|
-
error "Unknown platform: $DEPLOY_PLATFORM"
|
|
176
|
-
echo -e " Available: vercel, fly, railway, docker"
|
|
177
|
-
exit 1
|
|
178
|
-
fi
|
|
179
|
-
info "Using specified platform: ${BOLD}${DEPLOY_PLATFORM}${RESET}"
|
|
180
|
-
else
|
|
181
|
-
info "Detecting deploy platform..."
|
|
182
|
-
detected=$(detect_deploy_platform)
|
|
183
|
-
|
|
184
|
-
if [[ -z "$detected" ]]; then
|
|
185
|
-
warn "No platform detected in current directory"
|
|
186
|
-
echo ""
|
|
187
|
-
echo -e " Supported platforms:"
|
|
188
|
-
echo -e " ${CYAN}vercel${RESET} — vercel.json or .vercel/"
|
|
189
|
-
echo -e " ${CYAN}fly${RESET} — fly.toml"
|
|
190
|
-
echo -e " ${CYAN}railway${RESET} — railway.toml or .railway/"
|
|
191
|
-
echo -e " ${CYAN}docker${RESET} — Dockerfile or docker-compose.yml"
|
|
192
|
-
echo ""
|
|
193
|
-
echo -e " Specify manually: ${DIM}shipwright init --deploy --platform vercel${RESET}"
|
|
194
|
-
exit 1
|
|
195
|
-
fi
|
|
196
|
-
|
|
197
|
-
# If multiple platforms detected, use the first and warn
|
|
198
|
-
platform_count=$(echo "$detected" | wc -w | tr -d ' ')
|
|
199
|
-
DEPLOY_PLATFORM=$(echo "$detected" | awk '{print $1}')
|
|
200
|
-
|
|
201
|
-
if [[ "$platform_count" -gt 1 ]]; then
|
|
202
|
-
warn "Multiple platforms detected: ${BOLD}${detected}${RESET}"
|
|
203
|
-
info "Using: ${BOLD}${DEPLOY_PLATFORM}${RESET}"
|
|
204
|
-
echo -e " ${DIM}Override with: shipwright init --deploy --platform <name>${RESET}"
|
|
205
|
-
echo ""
|
|
206
|
-
else
|
|
207
|
-
success "Detected platform: ${BOLD}${DEPLOY_PLATFORM}${RESET}"
|
|
208
|
-
fi
|
|
209
|
-
|
|
210
|
-
# Confirm with user
|
|
211
|
-
read -rp "$(echo -e "${CYAN}${BOLD}▸${RESET} Configure deploy for ${BOLD}${DEPLOY_PLATFORM}${RESET}? [Y/n] ")" confirm
|
|
212
|
-
if [[ "${confirm,,}" == "n" ]]; then
|
|
213
|
-
info "Aborted. Use --platform to specify manually."
|
|
214
|
-
exit 0
|
|
215
|
-
fi
|
|
216
|
-
fi
|
|
217
|
-
|
|
218
|
-
# Source the adapter to get command values
|
|
219
|
-
ADAPTER_FILE="$ADAPTERS_DIR/${DEPLOY_PLATFORM}-deploy.sh"
|
|
220
|
-
source "$ADAPTER_FILE"
|
|
221
|
-
|
|
222
|
-
staging_cmd=$(get_staging_cmd)
|
|
223
|
-
production_cmd=$(get_production_cmd)
|
|
224
|
-
rollback_cmd=$(get_rollback_cmd)
|
|
225
|
-
health_url=$(get_health_url)
|
|
226
|
-
smoke_cmd=$(get_smoke_cmd)
|
|
227
|
-
|
|
228
|
-
# Generate deployed.json from template
|
|
229
|
-
TEMPLATE_SRC="$REPO_DIR/templates/pipelines/deployed.json"
|
|
230
|
-
TEMPLATE_DST=".claude/pipeline-templates/deployed.json"
|
|
231
|
-
|
|
232
|
-
if [[ ! -f "$TEMPLATE_SRC" ]]; then
|
|
233
|
-
error "Template not found: $TEMPLATE_SRC"
|
|
234
|
-
exit 1
|
|
235
|
-
fi
|
|
236
|
-
|
|
237
|
-
mkdir -p ".claude/pipeline-templates"
|
|
238
|
-
|
|
239
|
-
# Use jq to properly fill in the template values
|
|
240
|
-
jq --arg staging "$staging_cmd" \
|
|
241
|
-
--arg production "$production_cmd" \
|
|
242
|
-
--arg rollback "$rollback_cmd" \
|
|
243
|
-
--arg health "$health_url" \
|
|
244
|
-
--arg smoke "$smoke_cmd" \
|
|
245
|
-
--arg platform "$DEPLOY_PLATFORM" \
|
|
246
|
-
'
|
|
247
|
-
.name = "deployed-" + $platform |
|
|
248
|
-
.description = "Autonomous pipeline with " + $platform + " deploy — generated by shipwright init --deploy" |
|
|
249
|
-
(.stages[] | select(.id == "deploy") | .config) |= {
|
|
250
|
-
staging_cmd: $staging,
|
|
251
|
-
production_cmd: $production,
|
|
252
|
-
rollback_cmd: $rollback
|
|
253
|
-
} |
|
|
254
|
-
(.stages[] | select(.id == "validate") | .config) |= {
|
|
255
|
-
smoke_cmd: $smoke,
|
|
256
|
-
health_url: $health,
|
|
257
|
-
close_issue: true
|
|
258
|
-
} |
|
|
259
|
-
(.stages[] | select(.id == "monitor") | .config) |= (
|
|
260
|
-
.health_url = $health |
|
|
261
|
-
.rollback_cmd = $rollback
|
|
262
|
-
)
|
|
263
|
-
' "$TEMPLATE_SRC" > "$TEMPLATE_DST"
|
|
264
|
-
|
|
265
|
-
success "Generated ${BOLD}${TEMPLATE_DST}${RESET}"
|
|
266
|
-
|
|
267
|
-
echo ""
|
|
268
|
-
echo -e "${BOLD}Deploy configured for ${DEPLOY_PLATFORM}!${RESET}"
|
|
269
|
-
echo ""
|
|
270
|
-
echo -e "${BOLD}Commands configured:${RESET}"
|
|
271
|
-
echo -e " ${DIM}staging:${RESET} $staging_cmd"
|
|
272
|
-
echo -e " ${DIM}production:${RESET} $production_cmd"
|
|
273
|
-
echo -e " ${DIM}rollback:${RESET} $rollback_cmd"
|
|
274
|
-
if [[ -n "$health_url" ]]; then
|
|
275
|
-
echo -e " ${DIM}health:${RESET} $health_url"
|
|
276
|
-
fi
|
|
277
|
-
echo ""
|
|
278
|
-
echo -e "${BOLD}Usage:${RESET}"
|
|
279
|
-
echo -e " ${DIM}shipwright pipeline start --issue 42 --template .claude/pipeline-templates/deployed.json${RESET}"
|
|
280
|
-
echo ""
|
|
281
|
-
echo -e "${DIM}Edit ${TEMPLATE_DST} to customize deploy commands, gates, or thresholds.${RESET}"
|
|
282
|
-
echo ""
|
package/scripts/cct-session.sh
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
-
# ║ cct-session.sh — Launch a Claude Code team session in a new tmux window║
|
|
4
|
-
# ║ ║
|
|
5
|
-
# ║ Uses new-window (NOT split-window) to avoid the tmux send-keys race ║
|
|
6
|
-
# ║ condition that affects 4+ agents. See KNOWN-ISSUES.md for details. ║
|
|
7
|
-
# ║ ║
|
|
8
|
-
# ║ Supports --template to scaffold from a team template and --terminal ║
|
|
9
|
-
# ║ to select a terminal adapter (tmux, iterm2, wezterm). ║
|
|
10
|
-
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
11
|
-
set -euo pipefail
|
|
12
|
-
|
|
13
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
-
|
|
15
|
-
# ─── Colors ──────────────────────────────────────────────────────────────────
|
|
16
|
-
CYAN='\033[38;2;0;212;255m'
|
|
17
|
-
PURPLE='\033[38;2;124;58;237m'
|
|
18
|
-
GREEN='\033[38;2;74;222;128m'
|
|
19
|
-
YELLOW='\033[38;2;250;204;21m'
|
|
20
|
-
RED='\033[38;2;248;113;113m'
|
|
21
|
-
DIM='\033[2m'
|
|
22
|
-
BOLD='\033[1m'
|
|
23
|
-
RESET='\033[0m'
|
|
24
|
-
|
|
25
|
-
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
26
|
-
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
27
|
-
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
28
|
-
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
29
|
-
|
|
30
|
-
# ─── Parse Arguments ────────────────────────────────────────────────────────
|
|
31
|
-
|
|
32
|
-
TEAM_NAME=""
|
|
33
|
-
TEMPLATE_NAME=""
|
|
34
|
-
TERMINAL_ADAPTER=""
|
|
35
|
-
|
|
36
|
-
while [[ $# -gt 0 ]]; do
|
|
37
|
-
case "$1" in
|
|
38
|
-
--template|-t)
|
|
39
|
-
TEMPLATE_NAME="${2:-}"
|
|
40
|
-
[[ -z "$TEMPLATE_NAME" ]] && { error "Missing template name after --template"; exit 1; }
|
|
41
|
-
shift 2
|
|
42
|
-
;;
|
|
43
|
-
--terminal)
|
|
44
|
-
TERMINAL_ADAPTER="${2:-}"
|
|
45
|
-
[[ -z "$TERMINAL_ADAPTER" ]] && { error "Missing adapter name after --terminal"; exit 1; }
|
|
46
|
-
shift 2
|
|
47
|
-
;;
|
|
48
|
-
--help|-h)
|
|
49
|
-
echo -e "${CYAN}${BOLD}shipwright session${RESET} — Create a new team session"
|
|
50
|
-
echo ""
|
|
51
|
-
echo -e "${BOLD}USAGE${RESET}"
|
|
52
|
-
echo -e " shipwright session [name] [--template <name>] [--terminal <adapter>]"
|
|
53
|
-
echo ""
|
|
54
|
-
echo -e "${BOLD}OPTIONS${RESET}"
|
|
55
|
-
echo -e " ${CYAN}--template, -t${RESET} <name> Use a team template (see: shipwright templates list)"
|
|
56
|
-
echo -e " ${CYAN}--terminal${RESET} <adapter> Terminal adapter: tmux (default), iterm2, wezterm"
|
|
57
|
-
echo ""
|
|
58
|
-
echo -e "${BOLD}EXAMPLES${RESET}"
|
|
59
|
-
echo -e " ${DIM}shipwright session refactor${RESET}"
|
|
60
|
-
echo -e " ${DIM}shipwright session my-feature --template feature-dev${RESET}"
|
|
61
|
-
echo -e " ${DIM}shipwright session my-feature --terminal iterm2${RESET}"
|
|
62
|
-
exit 0
|
|
63
|
-
;;
|
|
64
|
-
-*)
|
|
65
|
-
error "Unknown option: $1"
|
|
66
|
-
exit 1
|
|
67
|
-
;;
|
|
68
|
-
*)
|
|
69
|
-
# Positional: team name
|
|
70
|
-
[[ -z "$TEAM_NAME" ]] && TEAM_NAME="$1" || { error "Unexpected argument: $1"; exit 1; }
|
|
71
|
-
shift
|
|
72
|
-
;;
|
|
73
|
-
esac
|
|
74
|
-
done
|
|
75
|
-
|
|
76
|
-
TEAM_NAME="${TEAM_NAME:-team-$(date +%s)}"
|
|
77
|
-
WINDOW_NAME="claude-${TEAM_NAME}"
|
|
78
|
-
|
|
79
|
-
# ─── Template Loading ───────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
TEMPLATE_FILE=""
|
|
82
|
-
TEMPLATE_LAYOUT=""
|
|
83
|
-
TEMPLATE_LAYOUT_STYLE=""
|
|
84
|
-
TEMPLATE_MAIN_PANE_PERCENT=""
|
|
85
|
-
TEMPLATE_DESC=""
|
|
86
|
-
TEMPLATE_AGENTS=() # Populated as "name|role|focus" entries
|
|
87
|
-
|
|
88
|
-
if [[ -n "$TEMPLATE_NAME" ]]; then
|
|
89
|
-
# Search for template: user dir first, then repo dir
|
|
90
|
-
USER_TEMPLATES_DIR="${HOME}/.claude-teams/templates"
|
|
91
|
-
REPO_TEMPLATES_DIR="$(cd "$SCRIPT_DIR/../tmux/templates" 2>/dev/null && pwd)" || REPO_TEMPLATES_DIR=""
|
|
92
|
-
|
|
93
|
-
TEMPLATE_NAME="${TEMPLATE_NAME%.json}"
|
|
94
|
-
|
|
95
|
-
if [[ -f "$USER_TEMPLATES_DIR/${TEMPLATE_NAME}.json" ]]; then
|
|
96
|
-
TEMPLATE_FILE="$USER_TEMPLATES_DIR/${TEMPLATE_NAME}.json"
|
|
97
|
-
elif [[ -n "$REPO_TEMPLATES_DIR" && -f "$REPO_TEMPLATES_DIR/${TEMPLATE_NAME}.json" ]]; then
|
|
98
|
-
TEMPLATE_FILE="$REPO_TEMPLATES_DIR/${TEMPLATE_NAME}.json"
|
|
99
|
-
else
|
|
100
|
-
error "Template '${TEMPLATE_NAME}' not found."
|
|
101
|
-
echo -e " Run ${DIM}shipwright templates list${RESET} to see available templates."
|
|
102
|
-
exit 1
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
info "Loading template: ${PURPLE}${BOLD}${TEMPLATE_NAME}${RESET}"
|
|
106
|
-
|
|
107
|
-
# Parse template — single jq call extracts all fields + agents in one pass
|
|
108
|
-
if command -v jq &>/dev/null; then
|
|
109
|
-
# Single jq call: outputs metadata lines then agent lines
|
|
110
|
-
# Format: META<tab>field<tab>value for metadata, AGENT<tab>name|role|focus for agents
|
|
111
|
-
while IFS=$'\t' read -r tag key value; do
|
|
112
|
-
case "$tag" in
|
|
113
|
-
META)
|
|
114
|
-
case "$key" in
|
|
115
|
-
description) TEMPLATE_DESC="$value" ;;
|
|
116
|
-
layout) TEMPLATE_LAYOUT="$value" ;;
|
|
117
|
-
layout_style) TEMPLATE_LAYOUT_STYLE="$value" ;;
|
|
118
|
-
main_pane_percent) TEMPLATE_MAIN_PANE_PERCENT="$value" ;;
|
|
119
|
-
esac
|
|
120
|
-
;;
|
|
121
|
-
AGENT) [[ -n "$key" ]] && TEMPLATE_AGENTS+=("$key") ;;
|
|
122
|
-
esac
|
|
123
|
-
done < <(jq -r '
|
|
124
|
-
"META\tdescription\t\(.description // "")",
|
|
125
|
-
"META\tlayout\t\(.layout // "tiled")",
|
|
126
|
-
"META\tlayout_style\t\(.layout_style // "")",
|
|
127
|
-
"META\tmain_pane_percent\t\(.main_pane_percent // "")",
|
|
128
|
-
(.agents // [] | .[] | "AGENT\t\(.name)|\(.role // "")|\(.focus // "")\t")
|
|
129
|
-
' "$TEMPLATE_FILE")
|
|
130
|
-
else
|
|
131
|
-
error "jq is required for template parsing."
|
|
132
|
-
echo -e " ${DIM}brew install jq${RESET}"
|
|
133
|
-
exit 1
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
echo -e " ${DIM}${TEMPLATE_DESC}${RESET}"
|
|
137
|
-
echo -e " ${DIM}Agents: ${#TEMPLATE_AGENTS[@]} Layout: ${TEMPLATE_LAYOUT}${RESET}"
|
|
138
|
-
fi
|
|
139
|
-
|
|
140
|
-
# ─── Resolve Terminal Adapter ───────────────────────────────────────────────
|
|
141
|
-
|
|
142
|
-
# Auto-detect if not specified
|
|
143
|
-
if [[ -z "$TERMINAL_ADAPTER" ]]; then
|
|
144
|
-
TERMINAL_ADAPTER="tmux"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
ADAPTER_FILE="$SCRIPT_DIR/adapters/${TERMINAL_ADAPTER}-adapter.sh"
|
|
148
|
-
if [[ -f "$ADAPTER_FILE" ]]; then
|
|
149
|
-
# shellcheck source=/dev/null
|
|
150
|
-
source "$ADAPTER_FILE"
|
|
151
|
-
else
|
|
152
|
-
# Default to inline tmux behavior (backwards compatible)
|
|
153
|
-
if [[ "$TERMINAL_ADAPTER" != "tmux" ]]; then
|
|
154
|
-
error "Terminal adapter '${TERMINAL_ADAPTER}' not found."
|
|
155
|
-
echo -e " Available: tmux (default), iterm2, wezterm"
|
|
156
|
-
echo -e " Adapter dir: ${DIM}${SCRIPT_DIR}/adapters/${RESET}"
|
|
157
|
-
exit 1
|
|
158
|
-
fi
|
|
159
|
-
fi
|
|
160
|
-
|
|
161
|
-
# ─── Create Session (tmux default path) ─────────────────────────────────────
|
|
162
|
-
|
|
163
|
-
if [[ "$TERMINAL_ADAPTER" == "tmux" && ! -f "$ADAPTER_FILE" ]]; then
|
|
164
|
-
# Inline tmux path — original behavior (adapter not required for tmux)
|
|
165
|
-
|
|
166
|
-
# Check if a window with this name already exists
|
|
167
|
-
if tmux list-windows -F '#W' 2>/dev/null | grep -qx "$WINDOW_NAME"; then
|
|
168
|
-
warn "Window '${WINDOW_NAME}' already exists. Switching to it."
|
|
169
|
-
tmux select-window -t "$WINDOW_NAME"
|
|
170
|
-
exit 0
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
info "Creating team session: ${CYAN}${BOLD}${TEAM_NAME}${RESET}"
|
|
174
|
-
|
|
175
|
-
# Create a new window (not split-window — avoids race condition #23615)
|
|
176
|
-
tmux new-window -n "$WINDOW_NAME" -c "#{pane_current_path}"
|
|
177
|
-
|
|
178
|
-
# Force dark theme on the new pane (belt-and-suspenders with overlay hooks)
|
|
179
|
-
tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
|
|
180
|
-
|
|
181
|
-
# Set the pane title so the overlay shows the team name
|
|
182
|
-
tmux send-keys -t "$WINDOW_NAME" "printf '\\033]2;${TEAM_NAME}-lead\\033\\\\'" Enter
|
|
183
|
-
|
|
184
|
-
sleep 0.2
|
|
185
|
-
tmux send-keys -t "$WINDOW_NAME" "clear" Enter
|
|
186
|
-
|
|
187
|
-
# ─── Template: Create Agent Panes ────────────────────────────────────────
|
|
188
|
-
if [[ ${#TEMPLATE_AGENTS[@]} -gt 0 ]]; then
|
|
189
|
-
info "Scaffolding ${#TEMPLATE_AGENTS[@]} agent panes..."
|
|
190
|
-
|
|
191
|
-
for agent_entry in "${TEMPLATE_AGENTS[@]}"; do
|
|
192
|
-
IFS='|' read -r aname arole afocus <<< "$agent_entry"
|
|
193
|
-
|
|
194
|
-
# Split the window to create a new pane
|
|
195
|
-
tmux split-window -t "$WINDOW_NAME" -c "#{pane_current_path}"
|
|
196
|
-
sleep 0.1
|
|
197
|
-
|
|
198
|
-
# Force dark theme on agent pane
|
|
199
|
-
tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7'
|
|
200
|
-
|
|
201
|
-
# Set the pane title to the agent name
|
|
202
|
-
tmux send-keys -t "$WINDOW_NAME" "printf '\\033]2;${TEAM_NAME}-${aname}\\033\\\\'" Enter
|
|
203
|
-
sleep 0.1
|
|
204
|
-
tmux send-keys -t "$WINDOW_NAME" "clear" Enter
|
|
205
|
-
done
|
|
206
|
-
|
|
207
|
-
# Apply the layout from the template (layout_style takes precedence over layout)
|
|
208
|
-
if [[ -n "$TEMPLATE_LAYOUT_STYLE" ]]; then
|
|
209
|
-
tmux select-layout -t "$WINDOW_NAME" "$TEMPLATE_LAYOUT_STYLE" 2>/dev/null || \
|
|
210
|
-
tmux select-layout -t "$WINDOW_NAME" "${TEMPLATE_LAYOUT:-tiled}" 2>/dev/null || true
|
|
211
|
-
else
|
|
212
|
-
tmux select-layout -t "$WINDOW_NAME" "${TEMPLATE_LAYOUT:-tiled}" 2>/dev/null || true
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
# Resize leader pane to desired percentage
|
|
216
|
-
if [[ -n "$TEMPLATE_MAIN_PANE_PERCENT" && -n "$TEMPLATE_LAYOUT_STYLE" ]]; then
|
|
217
|
-
case "$TEMPLATE_LAYOUT_STYLE" in
|
|
218
|
-
main-horizontal) tmux resize-pane -t "$WINDOW_NAME.0" -x "${TEMPLATE_MAIN_PANE_PERCENT}%" 2>/dev/null ;;
|
|
219
|
-
main-vertical) tmux resize-pane -t "$WINDOW_NAME.0" -y "${TEMPLATE_MAIN_PANE_PERCENT}%" 2>/dev/null ;;
|
|
220
|
-
esac
|
|
221
|
-
fi
|
|
222
|
-
|
|
223
|
-
# Select the first pane (leader)
|
|
224
|
-
tmux select-pane -t "$WINDOW_NAME.0"
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
else
|
|
228
|
-
# ─── Adapter-based session creation ──────────────────────────────────────
|
|
229
|
-
|
|
230
|
-
if type -t spawn_agent &>/dev/null; then
|
|
231
|
-
info "Creating team session: ${CYAN}${BOLD}${TEAM_NAME}${RESET} ${DIM}(${TERMINAL_ADAPTER})${RESET}"
|
|
232
|
-
|
|
233
|
-
# Spawn leader
|
|
234
|
-
spawn_agent "${TEAM_NAME}-lead" "#{pane_current_path}" ""
|
|
235
|
-
|
|
236
|
-
# Spawn template agents if provided
|
|
237
|
-
if [[ ${#TEMPLATE_AGENTS[@]} -gt 0 ]]; then
|
|
238
|
-
info "Scaffolding ${#TEMPLATE_AGENTS[@]} agents..."
|
|
239
|
-
for agent_entry in "${TEMPLATE_AGENTS[@]}"; do
|
|
240
|
-
IFS='|' read -r aname arole afocus <<< "$agent_entry"
|
|
241
|
-
spawn_agent "${TEAM_NAME}-${aname}" "#{pane_current_path}" ""
|
|
242
|
-
done
|
|
243
|
-
fi
|
|
244
|
-
else
|
|
245
|
-
error "Adapter '${TERMINAL_ADAPTER}' loaded but spawn_agent() not found."
|
|
246
|
-
exit 1
|
|
247
|
-
fi
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
# ─── Summary ────────────────────────────────────────────────────────────────
|
|
251
|
-
|
|
252
|
-
echo ""
|
|
253
|
-
success "Team session ${CYAN}${BOLD}${TEAM_NAME}${RESET} ready!"
|
|
254
|
-
|
|
255
|
-
if [[ ${#TEMPLATE_AGENTS[@]} -gt 0 ]]; then
|
|
256
|
-
echo ""
|
|
257
|
-
echo -e "${BOLD}Team from template ${PURPLE}${TEMPLATE_NAME}${RESET}${BOLD}:${RESET}"
|
|
258
|
-
echo -e " ${CYAN}${BOLD}lead${RESET} ${DIM}— Team coordinator${RESET}"
|
|
259
|
-
for agent_entry in "${TEMPLATE_AGENTS[@]}"; do
|
|
260
|
-
IFS='|' read -r aname arole afocus <<< "$agent_entry"
|
|
261
|
-
echo -e " ${PURPLE}${BOLD}${aname}${RESET} ${DIM}— ${arole}${RESET}"
|
|
262
|
-
done
|
|
263
|
-
echo ""
|
|
264
|
-
echo -e "${BOLD}Next steps:${RESET}"
|
|
265
|
-
echo -e " ${CYAN}1.${RESET} Switch to window ${DIM}${WINDOW_NAME}${RESET}"
|
|
266
|
-
echo -e " ${CYAN}2.${RESET} Start ${DIM}claude${RESET} in the lead pane (top-left)"
|
|
267
|
-
echo -e " ${CYAN}3.${RESET} Ask Claude to use the team — agents are ready in their panes"
|
|
268
|
-
else
|
|
269
|
-
echo ""
|
|
270
|
-
echo -e "${BOLD}Next steps:${RESET}"
|
|
271
|
-
echo -e " ${CYAN}1.${RESET} Switch to window ${DIM}${WINDOW_NAME}${RESET} ${DIM}(prefix + $(tmux list-windows -F '#I #W' | grep "$WINDOW_NAME" | cut -d' ' -f1))${RESET}"
|
|
272
|
-
echo -e " ${CYAN}2.${RESET} Start Claude Code:"
|
|
273
|
-
echo -e " ${DIM}claude${RESET}"
|
|
274
|
-
echo -e " ${CYAN}3.${RESET} Ask Claude to create a team:"
|
|
275
|
-
echo -e " ${DIM}\"Create a team with 2 agents to refactor the auth module\"${RESET}"
|
|
276
|
-
fi
|
|
277
|
-
|
|
278
|
-
echo ""
|
|
279
|
-
echo -e "${PURPLE}${BOLD}Tip:${RESET} For file isolation between agents, use git worktrees:"
|
|
280
|
-
echo -e " ${DIM}git worktree add ../project-${TEAM_NAME} -b ${TEAM_NAME}${RESET}"
|
|
281
|
-
echo -e " Then launch Claude inside the worktree directory."
|
|
282
|
-
echo ""
|
|
283
|
-
echo -e "${DIM}Settings: ~/.claude/settings.json (see settings.json.template)${RESET}"
|
|
284
|
-
echo -e "${DIM}Keybinding: prefix + T re-runs this command${RESET}"
|