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,393 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright eventbus — Durable event bus for real-time inter-component ║
4
+ # ║ communication with publishing, subscribing, process monitoring, file ║
5
+ # ║ watching, event replay, and lifecycle management. ║
6
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
7
+ set -euo pipefail
8
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
9
+
10
+ VERSION="2.0.0"
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && 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
+ info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
30
+ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
+ warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
+ error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
+
34
+ # ─── Configuration ──────────────────────────────────────────────────────────
35
+ EVENTBUS_FILE="${HOME}/.shipwright/eventbus.jsonl"
36
+ EVENT_TTL_DAYS=7 # Default TTL for events (seconds = 7 * 86400)
37
+
38
+ # ─── Initialize eventbus directory ──────────────────────────────────────────
39
+ ensure_eventbus_dir() {
40
+ local dir
41
+ dir="$(dirname "$EVENTBUS_FILE")"
42
+ [[ -d "$dir" ]] || mkdir -p "$dir"
43
+ }
44
+
45
+ # ─── Generate UUID (simple) ────────────────────────────────────────────────
46
+ generate_uuid() {
47
+ local uuid
48
+ uuid=$(od -x /dev/urandom | head -1 | awk '{OFS="-"; print $2$3, $4, $5, $6, $7$8$9}' | sed 's/-/-/g' | cut -c1-36)
49
+ echo "$uuid"
50
+ }
51
+
52
+ # ─── Publish command ────────────────────────────────────────────────────────
53
+ cmd_publish() {
54
+ local event_type="$1"
55
+ local source="${2:-unknown}"
56
+ local correlation_id="$3"
57
+ local payload_json="${4:-{}}"
58
+
59
+ if [[ -z "$event_type" ]]; then
60
+ error "publish requires event_type, source, [correlation_id], [payload_json]"
61
+ return 1
62
+ fi
63
+
64
+ ensure_eventbus_dir
65
+
66
+ if [[ -z "$correlation_id" ]]; then
67
+ correlation_id="$(generate_uuid)"
68
+ fi
69
+
70
+ local timestamp
71
+ timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
72
+
73
+ # Build event JSON on single line
74
+ local event_json
75
+ event_json="{\"type\": \"$event_type\", \"source\": \"$source\", \"correlation_id\": \"$correlation_id\", \"timestamp\": \"$timestamp\", \"payload\": $payload_json}"
76
+
77
+
78
+ # Atomic write (append to JSONL file)
79
+ local tmp_file
80
+ tmp_file="$(mktemp)"
81
+ if [[ -f "$EVENTBUS_FILE" ]]; then
82
+ cat "$EVENTBUS_FILE" > "$tmp_file"
83
+ fi
84
+ echo "$event_json" >> "$tmp_file"
85
+ mv "$tmp_file" "$EVENTBUS_FILE"
86
+
87
+ success "Published event: $event_type (correlation_id: $correlation_id)"
88
+ }
89
+
90
+ # ─── Subscribe command ──────────────────────────────────────────────────────
91
+ cmd_subscribe() {
92
+ local event_type_filter="${1:-}"
93
+ local max_lines="${2:-}"
94
+
95
+ ensure_eventbus_dir
96
+
97
+ [[ ! -f "$EVENTBUS_FILE" ]] && {
98
+ warn "Event bus is empty or does not exist yet"
99
+ return 0
100
+ }
101
+
102
+ info "Subscribing to event bus (${event_type_filter:-(all types)})..."
103
+ echo ""
104
+
105
+ # Tail the file with optional grep filter
106
+ if [[ -n "$event_type_filter" ]]; then
107
+ tail -f "$EVENTBUS_FILE" | grep "\"type\": \"$event_type_filter\""
108
+ else
109
+ tail -f "$EVENTBUS_FILE"
110
+ fi
111
+ }
112
+
113
+ # ─── Process reaper (SIGCHLD monitor) ──────────────────────────────────────
114
+ cmd_reaper() {
115
+ local pid_list=()
116
+
117
+ info "Starting process reaper. Press Ctrl+C to exit."
118
+ echo -e "${DIM}Monitoring child processes and emitting process.exited events...${RESET}"
119
+ echo ""
120
+
121
+ ensure_eventbus_dir
122
+
123
+ # Monitor child processes
124
+ while true; do
125
+ # Get list of all child processes
126
+ local pids
127
+ pids=$(jobs -p 2>/dev/null || echo "")
128
+
129
+ if [[ -n "$pids" ]]; then
130
+ while IFS= read -r pid; do
131
+ [[ -z "$pid" ]] && continue
132
+
133
+ # Check if process is still alive
134
+ if ! kill -0 "$pid" 2>/dev/null; then
135
+ # Process died — emit event
136
+ local payload="{\"pid\": $pid, \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
137
+ cmd_publish "process.exited" "reaper" "$(generate_uuid)" "$payload"
138
+ fi
139
+ done <<< "$pids"
140
+ fi
141
+
142
+ sleep 2
143
+ done
144
+ }
145
+
146
+ # ─── File watcher (fswatch or inotifywait) ────────────────────────────────
147
+ cmd_watch() {
148
+ local watch_dir="$1"
149
+ [[ -z "$watch_dir" ]] && {
150
+ error "watch requires a directory path"
151
+ return 1
152
+ }
153
+
154
+ [[ ! -d "$watch_dir" ]] && {
155
+ error "Directory not found: $watch_dir"
156
+ return 1
157
+ }
158
+
159
+ ensure_eventbus_dir
160
+
161
+ info "Watching directory: $watch_dir"
162
+ echo -e "${DIM}Press Ctrl+C to stop...${RESET}"
163
+ echo ""
164
+
165
+ # Determine platform and use appropriate watcher
166
+ if command -v fswatch &>/dev/null; then
167
+ # macOS with fswatch
168
+ fswatch -r "$watch_dir" | while read -r file; do
169
+ local payload
170
+ payload="{\"file\": \"$file\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
171
+ cmd_publish "file.changed" "watcher" "$(generate_uuid)" "$payload"
172
+ done
173
+ elif command -v inotifywait &>/dev/null; then
174
+ # Linux with inotify-tools
175
+ inotifywait -m -r "$watch_dir" | while read -r dir action file; do
176
+ local filepath="${dir}${file}"
177
+ local payload
178
+ payload="{\"file\": \"$filepath\", \"action\": \"$action\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
179
+ cmd_publish "file.changed" "watcher" "$(generate_uuid)" "$payload"
180
+ done
181
+ else
182
+ error "Neither fswatch (macOS) nor inotifywait (Linux) is installed"
183
+ return 1
184
+ fi
185
+ }
186
+
187
+ # ─── Event replay command ──────────────────────────────────────────────────
188
+ cmd_replay() {
189
+ local minutes="${1:-}"
190
+ [[ -z "$minutes" ]] && minutes=60
191
+
192
+ ensure_eventbus_dir
193
+
194
+ [[ ! -f "$EVENTBUS_FILE" ]] && {
195
+ warn "Event bus is empty or does not exist yet"
196
+ return 0
197
+ }
198
+
199
+ info "Replaying events from the last ${minutes} minutes..."
200
+ echo ""
201
+
202
+ # Calculate cutoff timestamp
203
+ local cutoff_epoch
204
+ cutoff_epoch=$(($(date +%s) - (minutes * 60)))
205
+ local cutoff_iso
206
+ cutoff_iso="$(date -u -j -f %s "$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d @"$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
207
+
208
+ # Grep and display events after cutoff
209
+ grep "timestamp" "$EVENTBUS_FILE" | while read -r line; do
210
+ local ts
211
+ ts=$(echo "$line" | grep -o '"timestamp": "[^"]*"' | cut -d'"' -f4)
212
+ if [[ "$ts" > "$cutoff_iso" ]]; then
213
+ echo "$line"
214
+ fi
215
+ done
216
+ }
217
+
218
+ # ─── Status command ────────────────────────────────────────────────────────
219
+ cmd_status() {
220
+ ensure_eventbus_dir
221
+
222
+ echo ""
223
+ echo -e "${CYAN}${BOLD}Event Bus Status${RESET}"
224
+ echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S')${RESET}"
225
+ echo ""
226
+
227
+ if [[ ! -f "$EVENTBUS_FILE" ]]; then
228
+ echo -e " ${YELLOW}Event bus not yet initialized${RESET}"
229
+ echo ""
230
+ return 0
231
+ fi
232
+
233
+ local total_events
234
+ total_events=$(wc -l < "$EVENTBUS_FILE" || echo 0)
235
+
236
+ local last_event_ts
237
+ last_event_ts=$(tail -1 "$EVENTBUS_FILE" | grep -o '"timestamp": "[^"]*"' | cut -d'"' -f4 || echo "never")
238
+
239
+ echo -e " ${CYAN}Event Bus:${RESET} $EVENTBUS_FILE"
240
+ echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
241
+ echo -e " ${CYAN}Last Event:${RESET} $last_event_ts"
242
+ echo ""
243
+
244
+ # Count events by type
245
+ if [[ $total_events -gt 0 ]]; then
246
+ echo -e " ${PURPLE}${BOLD}Events by Type${RESET}"
247
+ grep '"type"' "$EVENTBUS_FILE" | cut -d'"' -f4 | sort | uniq -c | sort -rn | while read -r count type; do
248
+ printf " ${DIM}%-40s${RESET} %3d events\n" "$type" "$count"
249
+ done
250
+ fi
251
+
252
+ echo ""
253
+ }
254
+
255
+ # ─── Clean command (remove old events) ─────────────────────────────────────
256
+ cmd_clean() {
257
+ local ttl_days="${1:-$EVENT_TTL_DAYS}"
258
+
259
+ ensure_eventbus_dir
260
+
261
+ [[ ! -f "$EVENTBUS_FILE" ]] && {
262
+ success "Event bus is empty"
263
+ return 0
264
+ }
265
+
266
+ info "Cleaning events older than ${ttl_days} days..."
267
+
268
+ # Calculate cutoff timestamp
269
+ local cutoff_epoch
270
+ cutoff_epoch=$(($(date +%s) - (ttl_days * 86400)))
271
+ local cutoff_iso
272
+ cutoff_iso="$(date -u -j -f %s "$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d @"$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
273
+
274
+ local old_count
275
+ old_count=$(grep -c "timestamp" "$EVENTBUS_FILE" 2>/dev/null || echo 0)
276
+
277
+ # Keep only recent events
278
+ local tmp_file
279
+ tmp_file="$(mktemp)"
280
+ grep "timestamp" "$EVENTBUS_FILE" | while read -r line; do
281
+ local ts
282
+ ts=$(echo "$line" | grep -o '"timestamp": "[^"]*"' | cut -d'"' -f4)
283
+ if [[ "$ts" > "$cutoff_iso" ]]; then
284
+ echo "$line" >> "$tmp_file"
285
+ fi
286
+ done
287
+
288
+ mv "$tmp_file" "$EVENTBUS_FILE"
289
+
290
+ local new_count
291
+ new_count=$(wc -l < "$EVENTBUS_FILE" || echo 0)
292
+ local removed=$((old_count - new_count))
293
+
294
+ success "Removed $removed old events. Remaining: $new_count"
295
+ }
296
+
297
+ # ─── Help command ──────────────────────────────────────────────────────────
298
+ cmd_help() {
299
+ echo ""
300
+ echo -e "${CYAN}${BOLD}shipwright eventbus${RESET} — Durable event bus for real-time inter-component communication"
301
+ echo ""
302
+ echo -e "${BOLD}USAGE${RESET}"
303
+ echo -e " ${CYAN}shipwright eventbus${RESET} <subcommand> [options]"
304
+ echo ""
305
+ echo -e "${BOLD}SUBCOMMANDS${RESET}"
306
+ echo ""
307
+ echo -e " ${CYAN}publish${RESET} <type> <source> [correlation_id] [payload_json]"
308
+ echo -e " Publish a structured event to the event bus"
309
+ echo -e " ${DIM}shipwright eventbus publish stage.complete pipeline 123 '{\"stage\": \"build\"}'${RESET}"
310
+ echo ""
311
+ echo -e " ${CYAN}subscribe${RESET} [event_type]"
312
+ echo -e " Subscribe to events (tail with optional type filter)"
313
+ echo -e " ${DIM}shipwright eventbus subscribe${RESET} # All events"
314
+ echo -e " ${DIM}shipwright eventbus subscribe stage.complete${RESET} # Only stage events"
315
+ echo ""
316
+ echo -e " ${CYAN}reaper${RESET}"
317
+ echo -e " Monitor child processes and emit process.exited events"
318
+ echo -e " ${DIM}shipwright eventbus reaper${RESET}"
319
+ echo ""
320
+ echo -e " ${CYAN}watch${RESET} <directory>"
321
+ echo -e " Watch directory for file changes and emit file.changed events"
322
+ echo -e " ${DIM}shipwright eventbus watch /tmp/project${RESET}"
323
+ echo ""
324
+ echo -e " ${CYAN}replay${RESET} [minutes]"
325
+ echo -e " Replay events from the last N minutes (default: 60)"
326
+ echo -e " ${DIM}shipwright eventbus replay 30${RESET}"
327
+ echo ""
328
+ echo -e " ${CYAN}status${RESET}"
329
+ echo -e " Show event bus statistics and event counts by type"
330
+ echo -e " ${DIM}shipwright eventbus status${RESET}"
331
+ echo ""
332
+ echo -e " ${CYAN}clean${RESET} [ttl_days]"
333
+ echo -e " Remove events older than TTL (default: 7 days)"
334
+ echo -e " ${DIM}shipwright eventbus clean 3${RESET}"
335
+ echo ""
336
+ echo -e " ${CYAN}help${RESET}"
337
+ echo -e " Show this help message"
338
+ echo ""
339
+ echo -e "${BOLD}EVENT FORMAT${RESET}"
340
+ echo -e " Events are stored as JSONL with fields: type, source, correlation_id, timestamp, payload"
341
+ echo -e " ${DIM}Example:${RESET}"
342
+ echo -e " ${DIM}{\"type\": \"stage.complete\", \"source\": \"pipeline\", \"correlation_id\": \"123\", \"timestamp\": \"2025-02-14T12:34:56Z\", \"payload\": {...}}${RESET}"
343
+ echo ""
344
+ }
345
+
346
+ # ─── Main command router ───────────────────────────────────────────────────
347
+ main() {
348
+ local cmd="${1:-help}"
349
+
350
+ case "$cmd" in
351
+ publish)
352
+ shift
353
+ cmd_publish "$@"
354
+ ;;
355
+ subscribe)
356
+ shift
357
+ cmd_subscribe "$@"
358
+ ;;
359
+ reaper)
360
+ shift
361
+ cmd_reaper "$@"
362
+ ;;
363
+ watch)
364
+ shift
365
+ cmd_watch "$@"
366
+ ;;
367
+ replay)
368
+ shift
369
+ cmd_replay "$@"
370
+ ;;
371
+ status)
372
+ shift
373
+ cmd_status "$@"
374
+ ;;
375
+ clean)
376
+ shift
377
+ cmd_clean "$@"
378
+ ;;
379
+ help|--help|-h)
380
+ cmd_help
381
+ ;;
382
+ *)
383
+ error "Unknown subcommand: $cmd"
384
+ cmd_help
385
+ exit 1
386
+ ;;
387
+ esac
388
+ }
389
+
390
+ # ─── Source guard ──────────────────────────────────────────────────────────
391
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
392
+ main "$@"
393
+ fi