shipwright-cli 1.10.0 → 2.0.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 (108) hide show
  1. package/README.md +114 -36
  2. package/completions/_shipwright +212 -32
  3. package/completions/shipwright.bash +97 -25
  4. package/docs/strategy/01-market-research.md +619 -0
  5. package/docs/strategy/02-mission-and-brand.md +587 -0
  6. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  7. package/docs/strategy/QUICK-START.txt +289 -0
  8. package/docs/strategy/README.md +172 -0
  9. package/package.json +4 -2
  10. package/scripts/sw +208 -1
  11. package/scripts/sw-activity.sh +500 -0
  12. package/scripts/sw-adaptive.sh +925 -0
  13. package/scripts/sw-adversarial.sh +1 -1
  14. package/scripts/sw-architecture-enforcer.sh +1 -1
  15. package/scripts/sw-auth.sh +613 -0
  16. package/scripts/sw-autonomous.sh +664 -0
  17. package/scripts/sw-changelog.sh +704 -0
  18. package/scripts/sw-checkpoint.sh +1 -1
  19. package/scripts/sw-ci.sh +602 -0
  20. package/scripts/sw-cleanup.sh +1 -1
  21. package/scripts/sw-code-review.sh +637 -0
  22. package/scripts/sw-connect.sh +1 -1
  23. package/scripts/sw-context.sh +605 -0
  24. package/scripts/sw-cost.sh +1 -1
  25. package/scripts/sw-daemon.sh +432 -130
  26. package/scripts/sw-dashboard.sh +1 -1
  27. package/scripts/sw-db.sh +540 -0
  28. package/scripts/sw-decompose.sh +539 -0
  29. package/scripts/sw-deps.sh +551 -0
  30. package/scripts/sw-developer-simulation.sh +1 -1
  31. package/scripts/sw-discovery.sh +412 -0
  32. package/scripts/sw-docs-agent.sh +539 -0
  33. package/scripts/sw-docs.sh +1 -1
  34. package/scripts/sw-doctor.sh +59 -1
  35. package/scripts/sw-dora.sh +615 -0
  36. package/scripts/sw-durable.sh +710 -0
  37. package/scripts/sw-e2e-orchestrator.sh +535 -0
  38. package/scripts/sw-eventbus.sh +393 -0
  39. package/scripts/sw-feedback.sh +471 -0
  40. package/scripts/sw-fix.sh +1 -1
  41. package/scripts/sw-fleet-discover.sh +567 -0
  42. package/scripts/sw-fleet-viz.sh +404 -0
  43. package/scripts/sw-fleet.sh +8 -1
  44. package/scripts/sw-github-app.sh +596 -0
  45. package/scripts/sw-github-checks.sh +1 -1
  46. package/scripts/sw-github-deploy.sh +1 -1
  47. package/scripts/sw-github-graphql.sh +1 -1
  48. package/scripts/sw-guild.sh +569 -0
  49. package/scripts/sw-heartbeat.sh +1 -1
  50. package/scripts/sw-hygiene.sh +559 -0
  51. package/scripts/sw-incident.sh +617 -0
  52. package/scripts/sw-init.sh +88 -1
  53. package/scripts/sw-instrument.sh +699 -0
  54. package/scripts/sw-intelligence.sh +1 -1
  55. package/scripts/sw-jira.sh +1 -1
  56. package/scripts/sw-launchd.sh +363 -28
  57. package/scripts/sw-linear.sh +1 -1
  58. package/scripts/sw-logs.sh +1 -1
  59. package/scripts/sw-loop.sh +64 -3
  60. package/scripts/sw-memory.sh +1 -1
  61. package/scripts/sw-mission-control.sh +487 -0
  62. package/scripts/sw-model-router.sh +545 -0
  63. package/scripts/sw-otel.sh +596 -0
  64. package/scripts/sw-oversight.sh +689 -0
  65. package/scripts/sw-pipeline-composer.sh +1 -1
  66. package/scripts/sw-pipeline-vitals.sh +1 -1
  67. package/scripts/sw-pipeline.sh +687 -24
  68. package/scripts/sw-pm.sh +693 -0
  69. package/scripts/sw-pr-lifecycle.sh +522 -0
  70. package/scripts/sw-predictive.sh +1 -1
  71. package/scripts/sw-prep.sh +1 -1
  72. package/scripts/sw-ps.sh +1 -1
  73. package/scripts/sw-public-dashboard.sh +798 -0
  74. package/scripts/sw-quality.sh +595 -0
  75. package/scripts/sw-reaper.sh +1 -1
  76. package/scripts/sw-recruit.sh +573 -0
  77. package/scripts/sw-regression.sh +642 -0
  78. package/scripts/sw-release-manager.sh +736 -0
  79. package/scripts/sw-release.sh +706 -0
  80. package/scripts/sw-remote.sh +1 -1
  81. package/scripts/sw-replay.sh +520 -0
  82. package/scripts/sw-retro.sh +691 -0
  83. package/scripts/sw-scale.sh +444 -0
  84. package/scripts/sw-security-audit.sh +505 -0
  85. package/scripts/sw-self-optimize.sh +1 -1
  86. package/scripts/sw-session.sh +1 -1
  87. package/scripts/sw-setup.sh +1 -1
  88. package/scripts/sw-standup.sh +712 -0
  89. package/scripts/sw-status.sh +1 -1
  90. package/scripts/sw-strategic.sh +658 -0
  91. package/scripts/sw-stream.sh +450 -0
  92. package/scripts/sw-swarm.sh +583 -0
  93. package/scripts/sw-team-stages.sh +511 -0
  94. package/scripts/sw-templates.sh +1 -1
  95. package/scripts/sw-testgen.sh +515 -0
  96. package/scripts/sw-tmux-pipeline.sh +554 -0
  97. package/scripts/sw-tmux.sh +1 -1
  98. package/scripts/sw-trace.sh +485 -0
  99. package/scripts/sw-tracker-github.sh +188 -0
  100. package/scripts/sw-tracker-jira.sh +172 -0
  101. package/scripts/sw-tracker-linear.sh +251 -0
  102. package/scripts/sw-tracker.sh +117 -2
  103. package/scripts/sw-triage.sh +603 -0
  104. package/scripts/sw-upgrade.sh +1 -1
  105. package/scripts/sw-ux.sh +677 -0
  106. package/scripts/sw-webhook.sh +627 -0
  107. package/scripts/sw-widgets.sh +530 -0
  108. package/scripts/sw-worktree.sh +1 -1
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright tmux-pipeline — Spawn and manage pipelines in tmux windows ║
4
+ # ║ Native tmux integration for pipeline visibility and control ║
5
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
6
+ set -euo pipefail
7
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
+
9
+ VERSION="2.0.0"
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
+
13
+ # ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
14
+ CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
15
+ PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
16
+ BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
17
+ GREEN='\033[38;2;74;222;128m' # success
18
+ YELLOW='\033[38;2;250;204;21m' # warning
19
+ RED='\033[38;2;248;113;113m' # error
20
+ DIM='\033[2m'
21
+ BOLD='\033[1m'
22
+ RESET='\033[0m'
23
+
24
+ # ─── Cross-platform compatibility ──────────────────────────────────────────
25
+ # shellcheck source=lib/compat.sh
26
+ [[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
27
+
28
+ # ─── Helpers ─────────────────────────────────────────────────────────────────
29
+
30
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
31
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
32
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
33
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
34
+
35
+ emit_event() {
36
+ local event_type="$1"; shift
37
+ local events_file="${HOME}/.shipwright/events.jsonl"
38
+ mkdir -p "$(dirname "$events_file")"
39
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
40
+ while [[ $# -gt 0 ]]; do
41
+ local key="${1%%=*}" val="${1#*=}"
42
+ payload="${payload},\"${key}\":\"${val}\""
43
+ shift
44
+ done
45
+ payload="${payload}}"
46
+ echo "$payload" >> "$events_file"
47
+ }
48
+
49
+ # Get daemon session name
50
+ get_daemon_session() {
51
+ echo "sw-daemon"
52
+ }
53
+
54
+ # Get pipeline window name from issue number
55
+ get_window_name() {
56
+ local issue_num="$1"
57
+ echo "pipeline-${issue_num}"
58
+ }
59
+
60
+ # ─── Spawn subcommand ──────────────────────────────────────────────────────
61
+
62
+ cmd_spawn() {
63
+ local issue_num=""
64
+ local daemon_session
65
+ daemon_session="$(get_daemon_session)"
66
+
67
+ # Parse arguments
68
+ while [[ $# -gt 0 ]]; do
69
+ case "$1" in
70
+ --issue)
71
+ issue_num="$2"
72
+ shift 2
73
+ ;;
74
+ *)
75
+ error "Unknown option: $1"
76
+ return 1
77
+ ;;
78
+ esac
79
+ done
80
+
81
+ if [[ -z "$issue_num" ]]; then
82
+ error "Issue number required: --issue <number>"
83
+ return 1
84
+ fi
85
+
86
+ # Check if daemon session exists
87
+ if ! tmux has-session -t "$daemon_session" 2>/dev/null; then
88
+ error "Daemon session not running: $daemon_session"
89
+ echo " Start with: ${DIM}shipwright daemon start --detach${RESET}"
90
+ return 1
91
+ fi
92
+
93
+ local window_name
94
+ window_name="$(get_window_name "$issue_num")"
95
+
96
+ # Check if window already exists
97
+ if tmux list-windows -t "$daemon_session" 2>/dev/null | grep -q "^[0-9]*: $window_name"; then
98
+ warn "Pipeline window already exists: $window_name"
99
+ return 1
100
+ fi
101
+
102
+ # Create new window in daemon session
103
+ info "Creating pipeline window: ${CYAN}${window_name}${RESET}"
104
+ local pane_id
105
+ pane_id=$(tmux new-window -t "$daemon_session" -n "$window_name" -P -F "#{pane_id}")
106
+
107
+ if [[ -z "$pane_id" ]]; then
108
+ error "Failed to create tmux window"
109
+ return 1
110
+ fi
111
+
112
+ # Send pipeline command to pane
113
+ local pipeline_cmd="cd '$REPO_DIR' && env -u CLAUDECODE '$SCRIPT_DIR/sw-pipeline.sh' start --issue $issue_num"
114
+ tmux send-keys -t "$pane_id" "$pipeline_cmd" Enter
115
+
116
+ # Store pane ID in heartbeat
117
+ local heartbeat_file
118
+ heartbeat_file="${HOME}/.shipwright/heartbeats/pipeline-${issue_num}.json"
119
+ mkdir -p "$(dirname "$heartbeat_file")"
120
+
121
+ local tmp_file
122
+ tmp_file=$(mktemp)
123
+ cat > "$tmp_file" << EOF
124
+ {
125
+ "job_id": "pipeline-${issue_num}",
126
+ "type": "pipeline",
127
+ "issue": "$issue_num",
128
+ "pane_id": "$pane_id",
129
+ "window": "$window_name",
130
+ "started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
131
+ "status": "running"
132
+ }
133
+ EOF
134
+ mv "$tmp_file" "$heartbeat_file"
135
+
136
+ success "Pipeline spawned in window: ${CYAN}${window_name}${RESET}"
137
+ echo -e " Pane ID: ${DIM}${pane_id}${RESET}"
138
+ echo -e " Attach: ${DIM}tmux attach-session -t $daemon_session -c $window_name${RESET}"
139
+
140
+ emit_event "pipeline_spawn" "issue=$issue_num" "pane_id=$pane_id" "window=$window_name"
141
+ }
142
+
143
+ # ─── Attach subcommand ────────────────────────────────────────────────────
144
+
145
+ cmd_attach() {
146
+ local issue_num=""
147
+ local daemon_session
148
+ daemon_session="$(get_daemon_session)"
149
+
150
+ # Parse arguments
151
+ while [[ $# -gt 0 ]]; do
152
+ case "$1" in
153
+ --issue)
154
+ issue_num="$2"
155
+ shift 2
156
+ ;;
157
+ *)
158
+ issue_num="$1"
159
+ shift
160
+ ;;
161
+ esac
162
+ done
163
+
164
+ if [[ -z "$issue_num" ]]; then
165
+ error "Issue number required"
166
+ return 1
167
+ fi
168
+
169
+ # Check if daemon session exists
170
+ if ! tmux has-session -t "$daemon_session" 2>/dev/null; then
171
+ error "Daemon session not running: $daemon_session"
172
+ return 1
173
+ fi
174
+
175
+ local window_name
176
+ window_name="$(get_window_name "$issue_num")"
177
+
178
+ # Check if window exists
179
+ if ! tmux list-windows -t "$daemon_session" 2>/dev/null | grep -q "^[0-9]*: $window_name"; then
180
+ error "Pipeline window not found: $window_name"
181
+ return 1
182
+ fi
183
+
184
+ info "Attaching to: ${CYAN}${window_name}${RESET}"
185
+ tmux select-window -t "$daemon_session:$window_name"
186
+ tmux attach-session -t "$daemon_session"
187
+ }
188
+
189
+ # ─── Capture subcommand ───────────────────────────────────────────────────
190
+
191
+ cmd_capture() {
192
+ local issue_num=""
193
+ local daemon_session
194
+ daemon_session="$(get_daemon_session)"
195
+
196
+ # Parse arguments
197
+ while [[ $# -gt 0 ]]; do
198
+ case "$1" in
199
+ --issue)
200
+ issue_num="$2"
201
+ shift 2
202
+ ;;
203
+ *)
204
+ issue_num="$1"
205
+ shift
206
+ ;;
207
+ esac
208
+ done
209
+
210
+ if [[ -z "$issue_num" ]]; then
211
+ error "Issue number required"
212
+ return 1
213
+ fi
214
+
215
+ local window_name
216
+ window_name="$(get_window_name "$issue_num")"
217
+
218
+ # Check if window exists
219
+ if ! tmux list-windows -t "$daemon_session" 2>/dev/null | grep -q "^[0-9]*: $window_name"; then
220
+ error "Pipeline window not found: $window_name"
221
+ return 1
222
+ fi
223
+
224
+ # Get pane ID from window
225
+ local pane_id
226
+ pane_id=$(tmux list-panes -t "$daemon_session:$window_name" -F "#{pane_id}" | head -1)
227
+
228
+ if [[ -z "$pane_id" ]]; then
229
+ error "Failed to get pane ID for window: $window_name"
230
+ return 1
231
+ fi
232
+
233
+ # Capture pane output
234
+ info "Capturing output from: ${CYAN}${window_name}${RESET}"
235
+ tmux capture-pane -t "$pane_id" -p
236
+ }
237
+
238
+ # ─── Stream subcommand ────────────────────────────────────────────────────
239
+
240
+ cmd_stream() {
241
+ local issue_num=""
242
+ local daemon_session
243
+ daemon_session="$(get_daemon_session)"
244
+
245
+ # Parse arguments
246
+ while [[ $# -gt 0 ]]; do
247
+ case "$1" in
248
+ --issue)
249
+ issue_num="$2"
250
+ shift 2
251
+ ;;
252
+ *)
253
+ issue_num="$1"
254
+ shift
255
+ ;;
256
+ esac
257
+ done
258
+
259
+ if [[ -z "$issue_num" ]]; then
260
+ error "Issue number required"
261
+ return 1
262
+ fi
263
+
264
+ local window_name
265
+ window_name="$(get_window_name "$issue_num")"
266
+
267
+ # Check if window exists
268
+ if ! tmux list-windows -t "$daemon_session" 2>/dev/null | grep -q "^[0-9]*: $window_name"; then
269
+ error "Pipeline window not found: $window_name"
270
+ return 1
271
+ fi
272
+
273
+ # Get pane ID from window
274
+ local pane_id
275
+ pane_id=$(tmux list-panes -t "$daemon_session:$window_name" -F "#{pane_id}" | head -1)
276
+
277
+ if [[ -z "$pane_id" ]]; then
278
+ error "Failed to get pane ID for window: $window_name"
279
+ return 1
280
+ fi
281
+
282
+ info "Streaming output from: ${CYAN}${window_name}${RESET}"
283
+ info "Press Ctrl-C to stop streaming"
284
+ echo ""
285
+
286
+ # Stream with continuous capture
287
+ while true; do
288
+ tmux capture-pane -t "$pane_id" -p -S -100
289
+ sleep 1
290
+ # Clear previous output
291
+ printf '\033[2J\033[H'
292
+ done
293
+ }
294
+
295
+ # ─── List subcommand ──────────────────────────────────────────────────────
296
+
297
+ cmd_list() {
298
+ local daemon_session
299
+ daemon_session="$(get_daemon_session)"
300
+
301
+ # Check if daemon session exists
302
+ if ! tmux has-session -t "$daemon_session" 2>/dev/null; then
303
+ warn "Daemon session not running: $daemon_session"
304
+ return
305
+ fi
306
+
307
+ info "Pipeline windows in: ${CYAN}${daemon_session}${RESET}"
308
+ echo ""
309
+
310
+ local has_pipelines=false
311
+ while IFS= read -r line; do
312
+ [[ -z "$line" ]] && continue
313
+
314
+ # Parse window line: "0: pipeline-42 (1 panes)"
315
+ local window_num window_name status
316
+ window_num=$(echo "$line" | cut -d: -f1)
317
+ window_name=$(echo "$line" | cut -d' ' -f2)
318
+
319
+ # Check if it's a pipeline window
320
+ if [[ "$window_name" =~ ^pipeline- ]]; then
321
+ has_pipelines=true
322
+
323
+ # Extract issue number
324
+ local issue_num="${window_name#pipeline-}"
325
+
326
+ # Get pane info
327
+ local pane_id
328
+ pane_id=$(tmux list-panes -t "$daemon_session:$window_num" -F "#{pane_id}" | head -1)
329
+
330
+ # Get pane status
331
+ local pane_status
332
+ pane_status=$(tmux list-panes -t "$daemon_session:$window_num" -F "#{pane_title}" | head -1)
333
+ [[ -z "$pane_status" ]] && pane_status="running"
334
+
335
+ echo -e " ${CYAN}#${issue_num}${RESET} ${DIM}(window ${window_num})${RESET} — ${pane_status}"
336
+ echo -e " ${DIM}pane: ${pane_id}${RESET}"
337
+ fi
338
+ done < <(tmux list-windows -t "$daemon_session" 2>/dev/null)
339
+
340
+ if ! $has_pipelines; then
341
+ warn "No pipeline windows found"
342
+ fi
343
+
344
+ echo ""
345
+ }
346
+
347
+ # ─── Kill subcommand ──────────────────────────────────────────────────────
348
+
349
+ cmd_kill() {
350
+ local issue_num=""
351
+ local daemon_session
352
+ daemon_session="$(get_daemon_session)"
353
+
354
+ # Parse arguments
355
+ while [[ $# -gt 0 ]]; do
356
+ case "$1" in
357
+ --issue)
358
+ issue_num="$2"
359
+ shift 2
360
+ ;;
361
+ *)
362
+ issue_num="$1"
363
+ shift
364
+ ;;
365
+ esac
366
+ done
367
+
368
+ if [[ -z "$issue_num" ]]; then
369
+ error "Issue number required"
370
+ return 1
371
+ fi
372
+
373
+ local window_name
374
+ window_name="$(get_window_name "$issue_num")"
375
+
376
+ # Check if window exists
377
+ if ! tmux list-windows -t "$daemon_session" 2>/dev/null | grep -q "^[0-9]*: $window_name"; then
378
+ warn "Pipeline window not found: $window_name"
379
+ return
380
+ fi
381
+
382
+ # Get window number
383
+ local window_num
384
+ window_num=$(tmux list-windows -t "$daemon_session" 2>/dev/null | grep "^[0-9]*: $window_name" | cut -d: -f1)
385
+
386
+ info "Killing pipeline window: ${CYAN}${window_name}${RESET}"
387
+ tmux kill-window -t "$daemon_session:$window_num"
388
+
389
+ # Clean up heartbeat
390
+ local heartbeat_file
391
+ heartbeat_file="${HOME}/.shipwright/heartbeats/pipeline-${issue_num}.json"
392
+ rm -f "$heartbeat_file"
393
+
394
+ success "Pipeline killed"
395
+ emit_event "pipeline_kill" "issue=$issue_num" "window=$window_name"
396
+ }
397
+
398
+ # ─── Layout subcommand ────────────────────────────────────────────────────
399
+
400
+ cmd_layout() {
401
+ local layout="${1:-tiled}"
402
+ local daemon_session
403
+ daemon_session="$(get_daemon_session)"
404
+
405
+ # Check if daemon session exists
406
+ if ! tmux has-session -t "$daemon_session" 2>/dev/null; then
407
+ error "Daemon session not running: $daemon_session"
408
+ return 1
409
+ fi
410
+
411
+ info "Applying layout: ${CYAN}${layout}${RESET}"
412
+
413
+ case "$layout" in
414
+ tiled|tile)
415
+ tmux select-layout -t "$daemon_session" tiled
416
+ success "Layout applied: tiled"
417
+ ;;
418
+ even-horizontal|horizontal|h)
419
+ tmux select-layout -t "$daemon_session" even-horizontal
420
+ success "Layout applied: even-horizontal"
421
+ ;;
422
+ even-vertical|vertical|v)
423
+ tmux select-layout -t "$daemon_session" even-vertical
424
+ success "Layout applied: even-vertical"
425
+ ;;
426
+ *)
427
+ error "Unknown layout: $layout"
428
+ echo " Available: tiled, horizontal, vertical"
429
+ return 1
430
+ ;;
431
+ esac
432
+ }
433
+
434
+ # ─── Help subcommand ──────────────────────────────────────────────────────
435
+
436
+ cmd_help() {
437
+ cat << 'EOF'
438
+ shipwright tmux-pipeline — Spawn and manage pipelines in tmux windows
439
+
440
+ USAGE
441
+ shipwright tmux-pipeline <command> [options]
442
+
443
+ COMMANDS
444
+ spawn --issue <N>
445
+ Create a new tmux window for a pipeline and run it
446
+ Window name: pipeline-<N>
447
+ Stores pane ID in ~/.shipwright/heartbeats/pipeline-<N>.json
448
+
449
+ attach [--issue] <N>
450
+ Attach to a running pipeline's tmux window
451
+ Example: shipwright tmux-pipeline attach 42
452
+
453
+ capture [--issue] <N>
454
+ Capture and print current output of a pipeline pane
455
+ Example: shipwright tmux-pipeline capture 42
456
+
457
+ stream [--issue] <N>
458
+ Continuously stream a pipeline's output to stdout
459
+ Like "tail -f" for tmux panes
460
+ Press Ctrl-C to stop
461
+
462
+ list
463
+ Show all pipeline windows with status
464
+ Displays issue numbers, pane IDs, and status
465
+
466
+ kill [--issue] <N>
467
+ Terminate a pipeline window gracefully
468
+ Cleans up associated heartbeat file
469
+ Example: shipwright tmux-pipeline kill 42
470
+
471
+ layout <type>
472
+ Arrange pipeline windows in a grid layout
473
+ Types: tiled, horizontal, vertical
474
+
475
+ help
476
+ Show this help message
477
+
478
+ OPTIONS
479
+ --issue <N> Specify issue number (can also be positional arg)
480
+
481
+ EXAMPLES
482
+ # Create and spawn a pipeline for issue #42
483
+ shipwright tmux-pipeline spawn --issue 42
484
+
485
+ # Attach to the pipeline window
486
+ shipwright tmux-pipeline attach 42
487
+
488
+ # Capture current output
489
+ shipwright tmux-pipeline capture 42
490
+
491
+ # Stream output continuously
492
+ shipwright tmux-pipeline stream 42
493
+
494
+ # List all running pipelines
495
+ shipwright tmux-pipeline list
496
+
497
+ # Kill the pipeline
498
+ shipwright tmux-pipeline kill 42
499
+
500
+ # Arrange windows in a tiled grid
501
+ shipwright tmux-pipeline layout tiled
502
+
503
+ INTEGRATION
504
+ Works with: shipwright daemon start --detach
505
+ Requires: tmux session "sw-daemon" running
506
+ State: ~/.shipwright/heartbeats/pipeline-<N>.json
507
+
508
+ EOF
509
+ }
510
+
511
+ # ─── Main router ──────────────────────────────────────────────────────────
512
+
513
+ main() {
514
+ local cmd="${1:-help}"
515
+ shift 2>/dev/null || true
516
+
517
+ case "$cmd" in
518
+ spawn)
519
+ cmd_spawn "$@"
520
+ ;;
521
+ attach)
522
+ cmd_attach "$@"
523
+ ;;
524
+ capture)
525
+ cmd_capture "$@"
526
+ ;;
527
+ stream)
528
+ cmd_stream "$@"
529
+ ;;
530
+ list)
531
+ cmd_list "$@"
532
+ ;;
533
+ kill)
534
+ cmd_kill "$@"
535
+ ;;
536
+ layout)
537
+ cmd_layout "$@"
538
+ ;;
539
+ help|--help|-h)
540
+ cmd_help
541
+ ;;
542
+ *)
543
+ error "Unknown command: $cmd"
544
+ echo ""
545
+ cmd_help
546
+ exit 1
547
+ ;;
548
+ esac
549
+ }
550
+
551
+ # Only run main if this script is executed directly (not sourced)
552
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
553
+ main "$@"
554
+ fi
@@ -11,7 +11,7 @@
11
11
  # ║ shipwright tmux fix — Auto-fix common issues ║
12
12
  # ║ shipwright tmux reload — Reload tmux config ║
13
13
  # ╚═══════════════════════════════════════════════════════════════════════════╝
14
- VERSION="1.10.0"
14
+ VERSION="2.0.0"
15
15
  set -euo pipefail
16
16
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
17
17