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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +926 -0
  3. package/claude-code/CLAUDE.md.shipwright +125 -0
  4. package/claude-code/hooks/notify-idle.sh +35 -0
  5. package/claude-code/hooks/pre-compact-save.sh +57 -0
  6. package/claude-code/hooks/task-completed.sh +170 -0
  7. package/claude-code/hooks/teammate-idle.sh +68 -0
  8. package/claude-code/settings.json.template +184 -0
  9. package/completions/_shipwright +140 -0
  10. package/completions/shipwright.bash +89 -0
  11. package/completions/shipwright.fish +107 -0
  12. package/docs/KNOWN-ISSUES.md +199 -0
  13. package/docs/TIPS.md +331 -0
  14. package/docs/definition-of-done.example.md +16 -0
  15. package/docs/patterns/README.md +139 -0
  16. package/docs/patterns/audit-loop.md +149 -0
  17. package/docs/patterns/bug-hunt.md +183 -0
  18. package/docs/patterns/feature-implementation.md +159 -0
  19. package/docs/patterns/refactoring.md +183 -0
  20. package/docs/patterns/research-exploration.md +144 -0
  21. package/docs/patterns/test-generation.md +173 -0
  22. package/package.json +49 -0
  23. package/scripts/adapters/docker-deploy.sh +50 -0
  24. package/scripts/adapters/fly-deploy.sh +41 -0
  25. package/scripts/adapters/iterm2-adapter.sh +122 -0
  26. package/scripts/adapters/railway-deploy.sh +34 -0
  27. package/scripts/adapters/tmux-adapter.sh +87 -0
  28. package/scripts/adapters/vercel-deploy.sh +35 -0
  29. package/scripts/adapters/wezterm-adapter.sh +103 -0
  30. package/scripts/cct +242 -0
  31. package/scripts/cct-cleanup.sh +172 -0
  32. package/scripts/cct-cost.sh +590 -0
  33. package/scripts/cct-daemon.sh +3189 -0
  34. package/scripts/cct-doctor.sh +328 -0
  35. package/scripts/cct-fix.sh +478 -0
  36. package/scripts/cct-fleet.sh +904 -0
  37. package/scripts/cct-init.sh +282 -0
  38. package/scripts/cct-logs.sh +273 -0
  39. package/scripts/cct-loop.sh +1332 -0
  40. package/scripts/cct-memory.sh +1148 -0
  41. package/scripts/cct-pipeline.sh +3844 -0
  42. package/scripts/cct-prep.sh +1352 -0
  43. package/scripts/cct-ps.sh +168 -0
  44. package/scripts/cct-reaper.sh +390 -0
  45. package/scripts/cct-session.sh +284 -0
  46. package/scripts/cct-status.sh +169 -0
  47. package/scripts/cct-templates.sh +242 -0
  48. package/scripts/cct-upgrade.sh +422 -0
  49. package/scripts/cct-worktree.sh +405 -0
  50. package/scripts/postinstall.mjs +96 -0
  51. package/templates/pipelines/autonomous.json +71 -0
  52. package/templates/pipelines/cost-aware.json +95 -0
  53. package/templates/pipelines/deployed.json +79 -0
  54. package/templates/pipelines/enterprise.json +114 -0
  55. package/templates/pipelines/fast.json +63 -0
  56. package/templates/pipelines/full.json +104 -0
  57. package/templates/pipelines/hotfix.json +63 -0
  58. package/templates/pipelines/standard.json +91 -0
  59. package/tmux/claude-teams-overlay.conf +109 -0
  60. package/tmux/templates/architecture.json +19 -0
  61. package/tmux/templates/bug-fix.json +24 -0
  62. package/tmux/templates/code-review.json +24 -0
  63. package/tmux/templates/devops.json +19 -0
  64. package/tmux/templates/documentation.json +19 -0
  65. package/tmux/templates/exploration.json +19 -0
  66. package/tmux/templates/feature-dev.json +24 -0
  67. package/tmux/templates/full-stack.json +24 -0
  68. package/tmux/templates/migration.json +24 -0
  69. package/tmux/templates/refactor.json +19 -0
  70. package/tmux/templates/security-audit.json +24 -0
  71. package/tmux/templates/testing.json +24 -0
  72. package/tmux/tmux.conf +167 -0
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright worktree — Git worktree management for multi-agent isolation ║
4
+ # ║ ║
5
+ # ║ Each agent gets its own worktree so parallel agents don't clobber ║
6
+ # ║ each other's files. Worktrees live in .worktrees/ relative to root. ║
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
+ # ─── Repo root ─────────────────────────────────────────────────────────────
26
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
27
+ error "Not inside a git repository."
28
+ exit 1
29
+ }
30
+
31
+ WORKTREE_DIR="$REPO_ROOT/.worktrees"
32
+
33
+ # ─── .gitignore helper ────────────────────────────────────────────────────
34
+ ensure_gitignore() {
35
+ local gitignore="$REPO_ROOT/.gitignore"
36
+ if ! grep -q '^\.worktrees/' "$gitignore" 2>/dev/null; then
37
+ echo ".worktrees/" >> "$gitignore"
38
+ info "Added .worktrees/ to .gitignore"
39
+ fi
40
+ }
41
+
42
+ # ─── Commands ──────────────────────────────────────────────────────────────
43
+
44
+ worktree_create() {
45
+ local name="$1"
46
+ local branch="${2:-loop/$name}"
47
+ local worktree_path="$WORKTREE_DIR/$name"
48
+
49
+ if [[ -d "$worktree_path" ]]; then
50
+ warn "Worktree '$name' already exists at $worktree_path"
51
+ return 0
52
+ fi
53
+
54
+ ensure_gitignore
55
+ mkdir -p "$WORKTREE_DIR"
56
+
57
+ # Create branch from current HEAD if it doesn't exist
58
+ git branch "$branch" HEAD 2>/dev/null || true
59
+
60
+ # Create worktree
61
+ git worktree add "$worktree_path" "$branch"
62
+
63
+ success "Created worktree: ${BOLD}$name${RESET} → $worktree_path ${DIM}(branch: $branch)${RESET}"
64
+ }
65
+
66
+ worktree_list() {
67
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
68
+ echo -e " ${DIM}No worktrees found.${RESET}"
69
+ return 0
70
+ fi
71
+
72
+ local found=0
73
+ echo ""
74
+ echo -e "${BOLD}AGENT WORKTREES${RESET}"
75
+ echo -e "${DIM}───────────────────────────────────────────────────────────────${RESET}"
76
+
77
+ for dir in "$WORKTREE_DIR"/*/; do
78
+ [[ -d "$dir" ]] || continue
79
+ local name
80
+ name="$(basename "$dir")"
81
+ local branch="loop/$name"
82
+ local main_branch
83
+ main_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")"
84
+
85
+ local ahead behind
86
+ ahead="$(git rev-list "$main_branch".."$branch" --count 2>/dev/null || echo "?")"
87
+ behind="$(git rev-list "$branch".."$main_branch" --count 2>/dev/null || echo "?")"
88
+
89
+ local status_str=""
90
+ if [[ "$ahead" != "?" && "$behind" != "?" ]]; then
91
+ if [[ "$ahead" -gt 0 && "$behind" -gt 0 ]]; then
92
+ status_str="${GREEN}${ahead} ahead${RESET}, ${YELLOW}${behind} behind${RESET}"
93
+ elif [[ "$ahead" -gt 0 ]]; then
94
+ status_str="${GREEN}${ahead} ahead${RESET}"
95
+ elif [[ "$behind" -gt 0 ]]; then
96
+ status_str="${YELLOW}${behind} behind${RESET}"
97
+ else
98
+ status_str="${DIM}up to date${RESET}"
99
+ fi
100
+ else
101
+ status_str="${DIM}?${RESET}"
102
+ fi
103
+
104
+ printf " ${CYAN}%-16s${RESET} ${PURPLE}%-22s${RESET} %b ${DIM}.worktrees/%s/${RESET}\n" \
105
+ "$name" "$branch" "$status_str" "$name"
106
+ ((found++))
107
+ done
108
+
109
+ if [[ $found -eq 0 ]]; then
110
+ echo -e " ${DIM}No worktrees found.${RESET}"
111
+ fi
112
+ echo ""
113
+ }
114
+
115
+ worktree_sync() {
116
+ local name="$1"
117
+ local worktree_path="$WORKTREE_DIR/$name"
118
+
119
+ if [[ ! -d "$worktree_path" ]]; then
120
+ error "Worktree '$name' does not exist."
121
+ return 1
122
+ fi
123
+
124
+ info "Syncing ${BOLD}$name${RESET} with main..."
125
+
126
+ (
127
+ cd "$worktree_path"
128
+ git fetch origin main 2>/dev/null || true
129
+ git merge origin/main --no-edit 2>/dev/null || {
130
+ warn "Merge conflict in $name — resolve manually in $worktree_path"
131
+ return 1
132
+ }
133
+ )
134
+
135
+ success "Synced $name with latest main"
136
+ }
137
+
138
+ worktree_sync_all() {
139
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
140
+ warn "No worktrees found."
141
+ return 0
142
+ fi
143
+
144
+ local count=0
145
+ local failed=0
146
+
147
+ for dir in "$WORKTREE_DIR"/*/; do
148
+ [[ -d "$dir" ]] || continue
149
+ local name
150
+ name="$(basename "$dir")"
151
+ worktree_sync "$name" || ((failed++))
152
+ ((count++))
153
+ done
154
+
155
+ echo ""
156
+ if [[ $failed -gt 0 ]]; then
157
+ warn "Synced $count worktrees, $failed had conflicts"
158
+ else
159
+ success "Synced $count worktrees"
160
+ fi
161
+ }
162
+
163
+ worktree_merge() {
164
+ local name="$1"
165
+ local branch="loop/$name"
166
+ local current_branch
167
+ current_branch="$(git branch --show-current)"
168
+
169
+ if ! git rev-parse --verify "$branch" &>/dev/null; then
170
+ error "Branch '$branch' does not exist."
171
+ return 1
172
+ fi
173
+
174
+ info "Merging ${BOLD}$branch${RESET} into ${BOLD}$current_branch${RESET}..."
175
+
176
+ git merge "$branch" --no-edit || {
177
+ error "Merge conflict merging $branch"
178
+ echo -e " ${DIM}Resolve conflicts, then run: git merge --continue${RESET}"
179
+ return 1
180
+ }
181
+
182
+ success "Merged $branch into $current_branch"
183
+ }
184
+
185
+ worktree_merge_all() {
186
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
187
+ warn "No worktrees found."
188
+ return 0
189
+ fi
190
+
191
+ local count=0
192
+
193
+ for dir in "$WORKTREE_DIR"/*/; do
194
+ [[ -d "$dir" ]] || continue
195
+ local name
196
+ name="$(basename "$dir")"
197
+
198
+ worktree_merge "$name" || {
199
+ error "Stopping merge-all due to conflict in $name"
200
+ echo -e " ${DIM}Resolve the conflict, then re-run: shipwright worktree merge-all${RESET}"
201
+ return 1
202
+ }
203
+ ((count++))
204
+ done
205
+
206
+ echo ""
207
+ success "Merged $count worktree branches"
208
+ }
209
+
210
+ worktree_remove() {
211
+ local name="$1"
212
+ local worktree_path="$WORKTREE_DIR/$name"
213
+ local branch="loop/$name"
214
+
215
+ if [[ -d "$worktree_path" ]]; then
216
+ git worktree remove "$worktree_path" --force 2>/dev/null || {
217
+ warn "Could not cleanly remove worktree $name, forcing..."
218
+ rm -rf "$worktree_path"
219
+ }
220
+ fi
221
+
222
+ git branch -D "$branch" 2>/dev/null || true
223
+
224
+ success "Removed worktree: ${BOLD}$name${RESET}"
225
+ }
226
+
227
+ worktree_cleanup() {
228
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
229
+ success "Nothing to clean up — no .worktrees/ directory."
230
+ return 0
231
+ fi
232
+
233
+ local count=0
234
+
235
+ for dir in "$WORKTREE_DIR"/*/; do
236
+ [[ -d "$dir" ]] || continue
237
+ local name
238
+ name="$(basename "$dir")"
239
+ worktree_remove "$name"
240
+ ((count++))
241
+ done
242
+
243
+ # Prune stale worktree references
244
+ git worktree prune
245
+
246
+ # Remove .worktrees directory if empty
247
+ rmdir "$WORKTREE_DIR" 2>/dev/null || true
248
+
249
+ echo ""
250
+ success "Cleaned up $count worktrees"
251
+ }
252
+
253
+ worktree_status() {
254
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
255
+ echo -e " ${DIM}No worktrees found.${RESET}"
256
+ return 0
257
+ fi
258
+
259
+ local main_branch
260
+ main_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")"
261
+ local found=0
262
+
263
+ echo ""
264
+ echo -e "${BOLD}WORKTREE STATUS${RESET} ${DIM}(relative to ${main_branch})${RESET}"
265
+ echo -e "${DIM}───────────────────────────────────────────────────────────────${RESET}"
266
+
267
+ for dir in "$WORKTREE_DIR"/*/; do
268
+ [[ -d "$dir" ]] || continue
269
+ local name
270
+ name="$(basename "$dir")"
271
+ local branch="loop/$name"
272
+
273
+ local ahead behind
274
+ ahead="$(git rev-list "$main_branch".."$branch" --count 2>/dev/null || echo "?")"
275
+ behind="$(git rev-list "$branch".."$main_branch" --count 2>/dev/null || echo "?")"
276
+
277
+ # Check for uncommitted changes in the worktree
278
+ local dirty=""
279
+ if (cd "$dir" && [[ -n "$(git status --porcelain 2>/dev/null)" ]]); then
280
+ dirty=" ${RED}(dirty)${RESET}"
281
+ fi
282
+
283
+ printf " ${CYAN}%-16s${RESET} ${PURPLE}%-22s${RESET} ${GREEN}%s ahead${RESET}, ${YELLOW}%s behind${RESET}%b\n" \
284
+ "$name" "$branch" "$ahead" "$behind" "$dirty"
285
+ ((found++))
286
+ done
287
+
288
+ if [[ $found -eq 0 ]]; then
289
+ echo -e " ${DIM}No worktrees found.${RESET}"
290
+ fi
291
+ echo ""
292
+ }
293
+
294
+ worktree_help() {
295
+ echo ""
296
+ echo -e "${CYAN}${BOLD}shipwright worktree${RESET} — Git worktree management for multi-agent isolation"
297
+ echo ""
298
+ echo -e "${BOLD}USAGE${RESET}"
299
+ echo -e " shipwright worktree <command> [options]"
300
+ echo ""
301
+ echo -e "${BOLD}COMMANDS${RESET}"
302
+ echo -e " ${GREEN}create${RESET} <name> [--branch <branch>] Create a worktree for an agent"
303
+ echo -e " ${GREEN}list${RESET} List active agent worktrees"
304
+ echo -e " ${GREEN}sync${RESET} <name> Pull latest main into a worktree"
305
+ echo -e " ${GREEN}sync-all${RESET} Sync all worktrees with main"
306
+ echo -e " ${GREEN}merge${RESET} <name> Merge worktree branch back to main"
307
+ echo -e " ${GREEN}merge-all${RESET} Merge all worktree branches sequentially"
308
+ echo -e " ${GREEN}remove${RESET} <name> Remove a single worktree"
309
+ echo -e " ${GREEN}cleanup${RESET} Remove ALL worktrees and branches"
310
+ echo -e " ${GREEN}status${RESET} Show status of all worktrees"
311
+ echo -e " ${GREEN}help${RESET} Show this help"
312
+ echo ""
313
+ echo -e "${BOLD}EXAMPLES${RESET}"
314
+ echo -e " ${DIM}shipwright worktree create agent-1${RESET} # Create worktree on branch loop/agent-1"
315
+ echo -e " ${DIM}shipwright worktree create agent-1 --branch feat${RESET} # Custom branch name"
316
+ echo -e " ${DIM}shipwright worktree merge-all${RESET} # Merge all agent work back to main"
317
+ echo -e " ${DIM}shipwright worktree cleanup${RESET} # Remove all worktrees when done"
318
+ echo ""
319
+ echo -e "${BOLD}DIRECTORY STRUCTURE${RESET}"
320
+ echo -e " ${DIM}project-root/${RESET}"
321
+ echo -e " ${DIM}├── .worktrees/ # All agent worktrees${RESET}"
322
+ echo -e " ${DIM}│ ├── agent-1/ # Full repo copy for agent 1${RESET}"
323
+ echo -e " ${DIM}│ ├── agent-2/ # Full repo copy for agent 2${RESET}"
324
+ echo -e " ${DIM}│ └── agent-3/ # Full repo copy for agent 3${RESET}"
325
+ echo -e " ${DIM}└── .gitignore # Includes .worktrees/${RESET}"
326
+ echo ""
327
+ }
328
+
329
+ # ─── Main dispatch ─────────────────────────────────────────────────────────
330
+
331
+ COMMAND="${1:-help}"
332
+ shift || true
333
+
334
+ case "$COMMAND" in
335
+ create)
336
+ if [[ $# -lt 1 ]]; then
337
+ error "Usage: shipwright worktree create <name> [--branch <branch>]"
338
+ exit 1
339
+ fi
340
+ NAME="$1"
341
+ shift
342
+ BRANCH=""
343
+ while [[ $# -gt 0 ]]; do
344
+ case "$1" in
345
+ --branch)
346
+ BRANCH="${2:-}"
347
+ shift 2 || { error "--branch requires a value"; exit 1; }
348
+ ;;
349
+ *)
350
+ error "Unknown option: $1"
351
+ exit 1
352
+ ;;
353
+ esac
354
+ done
355
+ if [[ -n "$BRANCH" ]]; then
356
+ worktree_create "$NAME" "$BRANCH"
357
+ else
358
+ worktree_create "$NAME"
359
+ fi
360
+ ;;
361
+ list)
362
+ worktree_list
363
+ ;;
364
+ sync)
365
+ if [[ $# -lt 1 ]]; then
366
+ error "Usage: shipwright worktree sync <name>"
367
+ exit 1
368
+ fi
369
+ worktree_sync "$1"
370
+ ;;
371
+ sync-all)
372
+ worktree_sync_all
373
+ ;;
374
+ merge)
375
+ if [[ $# -lt 1 ]]; then
376
+ error "Usage: shipwright worktree merge <name>"
377
+ exit 1
378
+ fi
379
+ worktree_merge "$1"
380
+ ;;
381
+ merge-all)
382
+ worktree_merge_all
383
+ ;;
384
+ remove)
385
+ if [[ $# -lt 1 ]]; then
386
+ error "Usage: shipwright worktree remove <name>"
387
+ exit 1
388
+ fi
389
+ worktree_remove "$1"
390
+ ;;
391
+ cleanup)
392
+ worktree_cleanup
393
+ ;;
394
+ status)
395
+ worktree_status
396
+ ;;
397
+ help|--help|-h)
398
+ worktree_help
399
+ ;;
400
+ *)
401
+ error "Unknown command: $COMMAND"
402
+ echo -e " ${DIM}Run 'shipwright worktree help' for usage.${RESET}"
403
+ exit 1
404
+ ;;
405
+ esac
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ // ║ Shipwright — npm postinstall ║
4
+ // ║ Copies templates and migrates legacy config directories ║
5
+ // ╚═══════════════════════════════════════════════════════════════════════════╝
6
+
7
+ import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, appendFileSync } from "fs";
8
+ import { join } from "path";
9
+
10
+ const HOME = process.env.HOME || process.env.USERPROFILE;
11
+ const PKG_DIR = join(import.meta.dirname, "..");
12
+ const SHIPWRIGHT_DIR = join(HOME, ".shipwright");
13
+ const LEGACY_DIR = join(HOME, ".claude-teams");
14
+ const CLAUDE_DIR = join(HOME, ".claude");
15
+
16
+ const CYAN = "\x1b[38;2;0;212;255m";
17
+ const GREEN = "\x1b[38;2;74;222;128m";
18
+ const YELLOW = "\x1b[38;2;250;204;21m";
19
+ const DIM = "\x1b[2m";
20
+ const BOLD = "\x1b[1m";
21
+ const RESET = "\x1b[0m";
22
+
23
+ function info(msg) { console.log(`${CYAN}${BOLD}▸${RESET} ${msg}`); }
24
+ function success(msg) { console.log(`${GREEN}${BOLD}✓${RESET} ${msg}`); }
25
+ function warn(msg) { console.log(`${YELLOW}${BOLD}⚠${RESET} ${msg}`); }
26
+
27
+ function ensureDir(dir) {
28
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
29
+ }
30
+
31
+ function copyDir(src, dest) {
32
+ if (!existsSync(src)) return;
33
+ ensureDir(dest);
34
+ cpSync(src, dest, { recursive: true, force: false });
35
+ }
36
+
37
+ try {
38
+ // Copy team templates → ~/.shipwright/templates/
39
+ copyDir(join(PKG_DIR, "tmux", "templates"), join(SHIPWRIGHT_DIR, "templates"));
40
+ success("Installed team templates");
41
+
42
+ // Copy pipeline templates → ~/.shipwright/pipelines/
43
+ copyDir(join(PKG_DIR, "templates", "pipelines"), join(SHIPWRIGHT_DIR, "pipelines"));
44
+ success("Installed pipeline templates");
45
+
46
+ // Copy settings template → ~/.claude/settings.json.template (if missing)
47
+ const settingsTemplate = join(PKG_DIR, "claude-code", "settings.json");
48
+ const settingsDest = join(CLAUDE_DIR, "settings.json.template");
49
+ if (existsSync(settingsTemplate) && !existsSync(settingsDest)) {
50
+ ensureDir(CLAUDE_DIR);
51
+ cpSync(settingsTemplate, settingsDest);
52
+ success("Installed settings template");
53
+ }
54
+
55
+ // Install CLAUDE.md agent instructions → ~/.claude/CLAUDE.md (idempotent)
56
+ const claudeMdSrc = join(PKG_DIR, "claude-code", "CLAUDE.md.shipwright");
57
+ const claudeMdDest = join(CLAUDE_DIR, "CLAUDE.md");
58
+ if (existsSync(claudeMdSrc)) {
59
+ ensureDir(CLAUDE_DIR);
60
+ if (existsSync(claudeMdDest)) {
61
+ const existing = readFileSync(claudeMdDest, "utf8");
62
+ if (!existing.includes("Shipwright")) {
63
+ appendFileSync(claudeMdDest, "\n---\n\n" + readFileSync(claudeMdSrc, "utf8"));
64
+ success("Appended Shipwright instructions to ~/.claude/CLAUDE.md");
65
+ } else {
66
+ success("~/.claude/CLAUDE.md already contains Shipwright instructions");
67
+ }
68
+ } else {
69
+ cpSync(claudeMdSrc, claudeMdDest);
70
+ success("Installed ~/.claude/CLAUDE.md");
71
+ }
72
+ }
73
+
74
+ // Migrate ~/.claude-teams/ → ~/.shipwright/ (non-destructive)
75
+ if (existsSync(LEGACY_DIR) && !existsSync(join(SHIPWRIGHT_DIR, ".migrated"))) {
76
+ info("Migrating legacy ~/.claude-teams/ config...");
77
+ copyDir(LEGACY_DIR, SHIPWRIGHT_DIR);
78
+ writeFileSync(join(SHIPWRIGHT_DIR, ".migrated"), new Date().toISOString());
79
+ success("Migrated legacy config (originals preserved)");
80
+ }
81
+
82
+ // Print success banner
83
+ const version = JSON.parse(readFileSync(join(PKG_DIR, "package.json"), "utf8")).version;
84
+ console.log();
85
+ console.log(`${CYAN}${BOLD} ⚓ Shipwright v${version} installed${RESET}`);
86
+ console.log();
87
+ console.log(` Next steps:`);
88
+ console.log(` ${DIM}$${RESET} shipwright doctor ${DIM}# Verify your setup${RESET}`);
89
+ console.log(` ${DIM}$${RESET} shipwright session ${DIM}# Launch an agent team${RESET}`);
90
+ console.log(` ${DIM}$${RESET} shipwright pipeline ${DIM}# Run a delivery pipeline${RESET}`);
91
+ console.log();
92
+ } catch (err) {
93
+ warn(`Postinstall encountered an issue: ${err.message}`);
94
+ warn("Shipwright is installed — some templates may need manual setup.");
95
+ warn(`Run: shipwright doctor`);
96
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "autonomous",
3
+ "description": "Fully autonomous pipeline with compound quality — zero human gates",
4
+ "defaults": {
5
+ "test_cmd": "npm test",
6
+ "model": "opus",
7
+ "agents": 1
8
+ },
9
+ "stages": [
10
+ { "id": "intake", "enabled": true, "gate": "auto", "config": {} },
11
+ { "id": "plan", "enabled": true, "gate": "auto", "config": { "model": "opus" } },
12
+ { "id": "design", "enabled": true, "gate": "auto", "config": { "model": "opus" } },
13
+ {
14
+ "id": "build",
15
+ "enabled": true,
16
+ "gate": "auto",
17
+ "config": { "max_iterations": 20, "audit": true, "quality_gates": true }
18
+ },
19
+ { "id": "test", "enabled": true, "gate": "auto", "config": { "coverage_min": 80 } },
20
+ { "id": "review", "enabled": true, "gate": "auto", "config": {} },
21
+ {
22
+ "id": "compound_quality",
23
+ "enabled": true,
24
+ "gate": "auto",
25
+ "config": {
26
+ "adversarial": true,
27
+ "negative": true,
28
+ "e2e": true,
29
+ "dod_audit": true,
30
+ "max_cycles": 3
31
+ }
32
+ },
33
+ { "id": "pr", "enabled": true, "gate": "auto", "config": { "wait_ci": false } },
34
+ {
35
+ "id": "merge",
36
+ "enabled": true,
37
+ "gate": "auto",
38
+ "config": {
39
+ "merge_method": "squash",
40
+ "wait_ci_timeout_s": 600,
41
+ "auto_delete_branch": true
42
+ }
43
+ },
44
+ {
45
+ "id": "deploy",
46
+ "enabled": false,
47
+ "gate": "approve",
48
+ "config": { "staging_cmd": "", "production_cmd": "", "rollback_cmd": "" }
49
+ },
50
+ {
51
+ "id": "validate",
52
+ "enabled": false,
53
+ "gate": "auto",
54
+ "config": { "smoke_cmd": "", "health_url": "", "close_issue": true }
55
+ },
56
+ {
57
+ "id": "monitor",
58
+ "enabled": true,
59
+ "gate": "auto",
60
+ "config": {
61
+ "duration_minutes": 5,
62
+ "health_url": "",
63
+ "error_threshold": 5,
64
+ "log_pattern": "ERROR|FATAL|PANIC",
65
+ "log_cmd": "",
66
+ "rollback_cmd": "",
67
+ "auto_rollback": false
68
+ }
69
+ }
70
+ ]
71
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "cost-aware",
3
+ "description": "Cost-optimized pipeline: budget limits, model routing (haiku→sonnet→opus), per-stage tracking",
4
+ "defaults": {
5
+ "test_cmd": "npm test",
6
+ "model": "sonnet",
7
+ "agents": 1,
8
+ "cost_tracking": true,
9
+ "budget_check": true
10
+ },
11
+ "stages": [
12
+ {
13
+ "id": "intake",
14
+ "enabled": true,
15
+ "gate": "auto",
16
+ "config": {
17
+ "model": "haiku",
18
+ "cost_tracking": true
19
+ }
20
+ },
21
+ {
22
+ "id": "plan",
23
+ "enabled": true,
24
+ "gate": "approve",
25
+ "config": {
26
+ "model": "sonnet",
27
+ "cost_tracking": true
28
+ }
29
+ },
30
+ {
31
+ "id": "build",
32
+ "enabled": true,
33
+ "gate": "auto",
34
+ "config": {
35
+ "model": "sonnet",
36
+ "max_iterations": 20,
37
+ "audit": true,
38
+ "quality_gates": true,
39
+ "cost_tracking": true,
40
+ "abort_on_budget": true
41
+ }
42
+ },
43
+ {
44
+ "id": "test",
45
+ "enabled": true,
46
+ "gate": "auto",
47
+ "config": {
48
+ "coverage_min": 80,
49
+ "model": "haiku",
50
+ "cost_tracking": true
51
+ }
52
+ },
53
+ {
54
+ "id": "review",
55
+ "enabled": true,
56
+ "gate": "approve",
57
+ "config": {
58
+ "model": "opus",
59
+ "cost_tracking": true
60
+ }
61
+ },
62
+ {
63
+ "id": "pr",
64
+ "enabled": true,
65
+ "gate": "approve",
66
+ "config": {
67
+ "wait_ci": false,
68
+ "model": "haiku",
69
+ "cost_tracking": true
70
+ }
71
+ },
72
+ {
73
+ "id": "deploy",
74
+ "enabled": false,
75
+ "gate": "approve",
76
+ "config": {
77
+ "staging_cmd": "",
78
+ "production_cmd": "",
79
+ "rollback_cmd": "",
80
+ "cost_tracking": true
81
+ }
82
+ },
83
+ {
84
+ "id": "validate",
85
+ "enabled": false,
86
+ "gate": "auto",
87
+ "config": {
88
+ "smoke_cmd": "",
89
+ "health_url": "",
90
+ "close_issue": true,
91
+ "cost_tracking": true
92
+ }
93
+ }
94
+ ]
95
+ }