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,404 @@
1
+ #!/usr/bin/env bash
2
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
3
+ # ║ shipwright fleet-viz — Multi-Repo Fleet Visualization ║
4
+ # ║ Cross-repo insights, queue management, worker allocation, cost tracking ║
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
+ 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
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
35
+ now_epoch() { date +%s; }
36
+
37
+ format_duration() {
38
+ local secs="$1"
39
+ if [[ "$secs" -ge 3600 ]]; then
40
+ printf "%dh %dm %ds" $((secs/3600)) $((secs%3600/60)) $((secs%60))
41
+ elif [[ "$secs" -ge 60 ]]; then
42
+ printf "%dm %ds" $((secs/60)) $((secs%60))
43
+ else
44
+ printf "%ds" "$secs"
45
+ fi
46
+ }
47
+
48
+ # ─── Data Paths ────────────────────────────────────────────────────────────
49
+ FLEET_DIR="${HOME}/.shipwright"
50
+ FLEET_STATE="${FLEET_DIR}/fleet-state.json"
51
+ EVENTS_FILE="${FLEET_DIR}/events.jsonl"
52
+ COSTS_FILE="${FLEET_DIR}/costs.json"
53
+ MACHINES_FILE="${FLEET_DIR}/machines.json"
54
+
55
+ # ─── Health Status Helpers ─────────────────────────────────────────────────
56
+ get_health_status() {
57
+ local repo="$1"
58
+ # Calculate health from recent events/pipeline state
59
+ # healthy (green) = no recent failures
60
+ # degraded (yellow) = some failures but recovering
61
+ # failing (red) = persistent failures
62
+
63
+ if ! command -v jq &>/dev/null; then
64
+ echo "unknown"
65
+ return
66
+ fi
67
+
68
+ # Check if any active jobs are stuck (queued >30min)
69
+ local stuck_count
70
+ stuck_count=$(jq -r "[.active_jobs[] | select(.repo==\"$repo\" and (.queued_at|todateiso8601|length) and (now - (.queued_at | fromdateiso8601) > 1800))] | length" "$FLEET_STATE" 2>/dev/null || echo "0")
71
+
72
+ if [[ "$stuck_count" -gt 0 ]]; then
73
+ echo "failing"
74
+ else
75
+ echo "healthy"
76
+ fi
77
+ }
78
+
79
+ color_health() {
80
+ local status="$1"
81
+ case "$status" in
82
+ healthy) echo "${GREEN}${status}${RESET}" ;;
83
+ degraded) echo "${YELLOW}${status}${RESET}" ;;
84
+ failing) echo "${RED}${status}${RESET}" ;;
85
+ *) echo "${DIM}${status}${RESET}" ;;
86
+ esac
87
+ }
88
+
89
+ # ─── Overview Subcommand ───────────────────────────────────────────────────
90
+ show_overview() {
91
+ if ! command -v jq &>/dev/null; then
92
+ error "jq is required for fleet visualization"
93
+ exit 1
94
+ fi
95
+
96
+ [[ ! -f "$FLEET_STATE" ]] && {
97
+ warn "No fleet state found at $FLEET_STATE"
98
+ echo "Run 'shipwright fleet start' first"
99
+ return
100
+ }
101
+
102
+ echo ""
103
+ echo -e "${PURPLE}${BOLD}━━━ Fleet Overview ━━━${RESET}"
104
+ echo ""
105
+
106
+ # Extract metrics
107
+ local active_pipelines queued_agents repos
108
+ active_pipelines=$(jq '[.active_jobs[]? | select(.status=="running")] | length' "$FLEET_STATE" 2>/dev/null || echo "0")
109
+ queued_agents=$(jq '[.active_jobs[]? | select(.status=="queued")] | length' "$FLEET_STATE" 2>/dev/null || echo "0")
110
+ repos=$(jq '[.active_jobs[]? | .repo] | unique | length' "$FLEET_STATE" 2>/dev/null || echo "0")
111
+
112
+ echo -e "${BOLD}Active:${RESET} ${CYAN}${active_pipelines}${RESET} pipelines | ${BOLD}Queued:${RESET} ${YELLOW}${queued_agents}${RESET} jobs | ${BOLD}Repos:${RESET} ${PURPLE}${repos}${RESET}"
113
+ echo ""
114
+
115
+ # Per-repo breakdown
116
+ if [[ "$(jq '.active_jobs | length' "$FLEET_STATE" 2>/dev/null || echo "0")" -gt 0 ]]; then
117
+ echo -e "${BOLD}Repos:${RESET}"
118
+ echo ""
119
+
120
+ jq -r '.active_jobs[]? | .repo' "$FLEET_STATE" 2>/dev/null | sort -u | while read -r repo; do
121
+ local repo_active repo_queued health
122
+ repo_active=$(jq "[.active_jobs[]? | select(.repo==\"$repo\" and .status==\"running\")] | length" "$FLEET_STATE" 2>/dev/null || echo "0")
123
+ repo_queued=$(jq "[.active_jobs[]? | select(.repo==\"$repo\" and .status==\"queued\")] | length" "$FLEET_STATE" 2>/dev/null || echo "0")
124
+ health=$(get_health_status "$repo")
125
+
126
+ echo -e " ${CYAN}$(basename "$repo")${RESET} ${repo_active} active ${repo_queued} queued [$(color_health "$health")]"
127
+ done
128
+ else
129
+ echo -e "${DIM}No active pipelines${RESET}"
130
+ fi
131
+
132
+ echo ""
133
+ }
134
+
135
+ # ─── Workers Subcommand ────────────────────────────────────────────────────
136
+ show_workers() {
137
+ if ! command -v jq &>/dev/null; then
138
+ error "jq is required for fleet visualization"
139
+ exit 1
140
+ fi
141
+
142
+ [[ ! -f "$FLEET_STATE" ]] && {
143
+ warn "No fleet state found at $FLEET_STATE"
144
+ return
145
+ }
146
+
147
+ [[ ! -f "$MACHINES_FILE" ]] && {
148
+ warn "No machines file found at $MACHINES_FILE"
149
+ return
150
+ }
151
+
152
+ echo ""
153
+ echo -e "${PURPLE}${BOLD}━━━ Worker Allocation ━━━${RESET}"
154
+ echo ""
155
+
156
+ # Get per-repo worker counts
157
+ local total_workers_allocated=0
158
+ jq -r '.active_jobs[]? | .repo' "$FLEET_STATE" 2>/dev/null | sort -u | while read -r repo; do
159
+ local worker_count job_count utilization
160
+ worker_count=$(jq "[.active_jobs[]? | select(.repo==\"$repo\")] | map(.worker_id) | unique | length" "$FLEET_STATE" 2>/dev/null || echo "0")
161
+ job_count=$(jq "[.active_jobs[]? | select(.repo==\"$repo\")] | length" "$FLEET_STATE" 2>/dev/null || echo "0")
162
+
163
+ if [[ "$worker_count" -gt 0 ]]; then
164
+ utilization=$((job_count * 100 / worker_count))
165
+ else
166
+ utilization=0
167
+ fi
168
+
169
+ echo -e " ${CYAN}$(basename "$repo")${RESET} ${worker_count} workers ${job_count} jobs ${utilization}% util"
170
+ done
171
+
172
+ # Remote machines
173
+ echo ""
174
+ echo -e "${BOLD}Remote Machines:${RESET}"
175
+ echo ""
176
+
177
+ if jq '.machines[]?' "$MACHINES_FILE" 2>/dev/null | grep -q .; then
178
+ jq -r '.machines[]? | "\(.name) (\(.hostname)) — \(.status) — \(.active_jobs // 0) active"' "$MACHINES_FILE" 2>/dev/null | while read -r machine; do
179
+ echo -e " ${machine}"
180
+ done
181
+ else
182
+ echo -e " ${DIM}No remote machines configured${RESET}"
183
+ fi
184
+
185
+ echo ""
186
+ }
187
+
188
+ # ─── Insights Subcommand ───────────────────────────────────────────────────
189
+ show_insights() {
190
+ if ! command -v jq &>/dev/null; then
191
+ error "jq is required for fleet visualization"
192
+ exit 1
193
+ fi
194
+
195
+ [[ ! -f "$EVENTS_FILE" ]] && {
196
+ warn "No events found at $EVENTS_FILE"
197
+ return
198
+ }
199
+
200
+ echo ""
201
+ echo -e "${PURPLE}${BOLD}━━━ Fleet Insights ━━━${RESET}"
202
+ echo ""
203
+
204
+ # Fleet-wide success rate (last 30 days)
205
+ local total_pipelines successful_pipelines
206
+ total_pipelines=$(grep '"type":"pipeline_complete"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || echo "0")
207
+ successful_pipelines=$(grep '"type":"pipeline_complete".*"status":"success"' "$EVENTS_FILE" 2>/dev/null | tail -5000 | wc -l || echo "0")
208
+
209
+ local success_rate=0
210
+ if [[ "$total_pipelines" -gt 0 ]]; then
211
+ success_rate=$((successful_pipelines * 100 / total_pipelines))
212
+ fi
213
+
214
+ echo -e "${BOLD}Success Rate:${RESET} ${success_rate}% (${successful_pipelines}/${total_pipelines})"
215
+
216
+ # Most expensive repo
217
+ if [[ -f "$COSTS_FILE" ]]; then
218
+ echo ""
219
+ echo -e "${BOLD}Cost Leaders:${RESET}"
220
+ jq -r '.entries[]? | select(.repo != null) | .repo as $repo | {repo: $repo, cost: .cost} | @csv' "$COSTS_FILE" 2>/dev/null | \
221
+ awk -F, '{r=$1; gsub(/"/, "", r); sum[r]+=$2} END {for (r in sum) print r, sum[r]}' | sort -k2 -nr | head -5 | while read -r repo cost; do
222
+ printf " ${CYAN}%s${RESET} — \$%.2f\n" "$repo" "$cost"
223
+ done
224
+ fi
225
+
226
+ echo ""
227
+ }
228
+
229
+ # ─── Queue Subcommand ──────────────────────────────────────────────────────
230
+ show_queue() {
231
+ if ! command -v jq &>/dev/null; then
232
+ error "jq is required for fleet visualization"
233
+ exit 1
234
+ fi
235
+
236
+ [[ ! -f "$FLEET_STATE" ]] && {
237
+ warn "No fleet state found at $FLEET_STATE"
238
+ return
239
+ }
240
+
241
+ echo ""
242
+ echo -e "${PURPLE}${BOLD}━━━ Issue Queue ━━━${RESET}"
243
+ echo ""
244
+
245
+ local queued_count
246
+ queued_count=$(jq '[.active_jobs[]? | select(.status=="queued")] | length' "$FLEET_STATE" 2>/dev/null || echo "0")
247
+
248
+ if [[ "$queued_count" -eq 0 ]]; then
249
+ echo -e "${GREEN}✓${RESET} No queued issues"
250
+ echo ""
251
+ return
252
+ fi
253
+
254
+ echo -e "${BOLD}${queued_count} Issues Queued:${RESET}"
255
+ echo ""
256
+ echo -e " ${BOLD}Repo${RESET} ${BOLD}Issue${RESET} ${BOLD}Priority${RESET} ${BOLD}Wait Time${RESET}"
257
+ echo -e " ${DIM}─────────────────────────────────────────────────${RESET}"
258
+
259
+ jq -r '.active_jobs[]? | select(.status=="queued") | "\(.repo) #\(.issue_number) \(.priority // "normal") \(.queued_for // "0s")"' "$FLEET_STATE" 2>/dev/null | while read -r repo issue priority wait; do
260
+ local priority_color
261
+ case "$priority" in
262
+ urgent|hotfix) priority_color="${RED}${priority}${RESET}" ;;
263
+ high) priority_color="${YELLOW}${priority}${RESET}" ;;
264
+ *) priority_color="${DIM}${priority}${RESET}" ;;
265
+ esac
266
+ printf " %-20s %-8s %-10b %-12s\n" "$(basename "$repo")" "$issue" "$priority_color" "$wait"
267
+ done
268
+
269
+ echo ""
270
+ }
271
+
272
+ # ─── Costs Subcommand ──────────────────────────────────────────────────────
273
+ show_costs() {
274
+ if ! command -v jq &>/dev/null; then
275
+ error "jq is required for fleet visualization"
276
+ exit 1
277
+ fi
278
+
279
+ [[ ! -f "$COSTS_FILE" ]] && {
280
+ warn "No cost data found at $COSTS_FILE"
281
+ return
282
+ }
283
+
284
+ echo ""
285
+ echo -e "${PURPLE}${BOLD}━━━ Fleet Costs ━━━${RESET}"
286
+ echo ""
287
+
288
+ # Total spend
289
+ local total_spend
290
+ total_spend=$(jq '[.entries[]? | .cost // 0] | add // 0' "$COSTS_FILE" 2>/dev/null || echo "0")
291
+ printf "${BOLD}Total Spend:${RESET} \$%.2f\n" "$total_spend"
292
+
293
+ # Per-repo breakdown
294
+ echo ""
295
+ echo -e "${BOLD}Per-Repo:${RESET}"
296
+ jq -r '.entries[]? | select(.repo != null) | "\(.repo) \(.cost // 0) \(.model // "unknown")"' "$COSTS_FILE" 2>/dev/null | \
297
+ awk '{repo=$1; split(repo,a,"/"); r=a[length(a)]; cost=$2; model=$3; sum[r]+=cost} END {for (r in sum) print r, sum[r]}' | \
298
+ sort -k2 -nr | while read -r repo cost; do
299
+ printf " %-20s \$%.2f\n" "$repo" "$cost"
300
+ done
301
+
302
+ # Per-model breakdown
303
+ echo ""
304
+ echo -e "${BOLD}Per-Model:${RESET}"
305
+ jq -r '.entries[]? | .model // "unknown"' "$COSTS_FILE" 2>/dev/null | sort | uniq -c | sort -rn | while read -r count model; do
306
+ local model_cost
307
+ model_cost=$(jq "[.entries[]? | select(.model==\"$model\") | .cost // 0] | add // 0" "$COSTS_FILE" 2>/dev/null || echo "0")
308
+ printf " %-20s %d uses \$%.2f\n" "$model" "$count" "$model_cost"
309
+ done
310
+
311
+ echo ""
312
+ }
313
+
314
+ # ─── Export Subcommand ─────────────────────────────────────────────────────
315
+ show_export() {
316
+ if ! command -v jq &>/dev/null; then
317
+ error "jq is required for fleet visualization"
318
+ exit 1
319
+ fi
320
+
321
+ [[ ! -f "$FLEET_STATE" ]] && {
322
+ error "No fleet state found at $FLEET_STATE"
323
+ exit 1
324
+ }
325
+
326
+ # Merge all available data into single JSON export
327
+ local export_json
328
+ export_json=$(cat "$FLEET_STATE" 2>/dev/null || echo '{}')
329
+
330
+ if [[ -f "$COSTS_FILE" ]]; then
331
+ export_json=$(echo "$export_json" | jq --slurpfile costs "$COSTS_FILE" '.costs = $costs[0]')
332
+ fi
333
+
334
+ if [[ -f "$MACHINES_FILE" ]]; then
335
+ export_json=$(echo "$export_json" | jq --slurpfile machines "$MACHINES_FILE" '.machines = $machines[0]')
336
+ fi
337
+
338
+ echo "$export_json" | jq .
339
+ }
340
+
341
+ # ─── Help ──────────────────────────────────────────────────────────────────
342
+ show_help() {
343
+ echo ""
344
+ echo -e "${PURPLE}${BOLD}━━━ shipwright fleet-viz v${VERSION} ━━━${RESET}"
345
+ echo ""
346
+ echo -e "${BOLD}USAGE${RESET}"
347
+ echo -e " ${CYAN}shipwright fleet-viz${RESET} <command> [options]"
348
+ echo ""
349
+ echo -e "${BOLD}COMMANDS${RESET}"
350
+ echo -e " ${CYAN}overview${RESET} Show fleet-wide status (pipelines, queues, repos)"
351
+ echo -e " ${CYAN}workers${RESET} Show worker allocation and remote machines"
352
+ echo -e " ${CYAN}insights${RESET} Show cross-repo metrics and trends"
353
+ echo -e " ${CYAN}queue${RESET} Show combined queue across all repos"
354
+ echo -e " ${CYAN}costs${RESET} Show fleet-wide cost breakdown"
355
+ echo -e " ${CYAN}export${RESET} Export fleet state as JSON"
356
+ echo -e " ${CYAN}help${RESET} Show this help message"
357
+ echo ""
358
+ echo -e "${BOLD}EXAMPLES${RESET}"
359
+ echo -e " ${DIM}shipwright fleet-viz overview${RESET} # Fleet dashboard"
360
+ echo -e " ${DIM}shipwright fleet-viz workers${RESET} # Worker status"
361
+ echo -e " ${DIM}shipwright fleet-viz queue${RESET} # Show issue queue"
362
+ echo -e " ${DIM}shipwright fleet-viz export | jq .${RESET} # Export as JSON"
363
+ echo ""
364
+ }
365
+
366
+ # ─── Main Router ───────────────────────────────────────────────────────────
367
+ main() {
368
+ local cmd="${1:-overview}"
369
+ shift 2>/dev/null || true
370
+
371
+ case "$cmd" in
372
+ overview)
373
+ show_overview
374
+ ;;
375
+ workers)
376
+ show_workers
377
+ ;;
378
+ insights)
379
+ show_insights
380
+ ;;
381
+ queue)
382
+ show_queue
383
+ ;;
384
+ costs)
385
+ show_costs
386
+ ;;
387
+ export)
388
+ show_export
389
+ ;;
390
+ help|--help|-h)
391
+ show_help
392
+ ;;
393
+ *)
394
+ error "Unknown command: $cmd"
395
+ echo ""
396
+ show_help
397
+ exit 1
398
+ ;;
399
+ esac
400
+ }
401
+
402
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
403
+ main "$@"
404
+ fi
@@ -6,7 +6,7 @@
6
6
  set -euo pipefail
7
7
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
8
8
 
9
- VERSION="1.10.0"
9
+ VERSION="2.0.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -130,6 +130,7 @@ show_help() {
130
130
  echo -e " ${CYAN}stop${RESET} Stop all fleet daemons"
131
131
  echo -e " ${CYAN}status${RESET} Show fleet dashboard"
132
132
  echo -e " ${CYAN}metrics${RESET} [--period N] [--json] Aggregate DORA metrics across repos"
133
+ echo -e " ${CYAN}discover${RESET} --org <name> [options] Auto-discover repos from GitHub org"
133
134
  echo -e " ${CYAN}init${RESET} Generate fleet-config.json"
134
135
  echo -e " ${CYAN}help${RESET} Show this help"
135
136
  echo ""
@@ -138,6 +139,7 @@ show_help() {
138
139
  echo ""
139
140
  echo -e "${BOLD}EXAMPLES${RESET}"
140
141
  echo -e " ${DIM}shipwright fleet init${RESET} # Generate config"
142
+ echo -e " ${DIM}shipwright fleet discover --org myorg${RESET} # Auto-discover repos"
141
143
  echo -e " ${DIM}shipwright fleet start${RESET} # Start all daemons"
142
144
  echo -e " ${DIM}shipwright fleet start --config my-fleet.json${RESET} # Custom config"
143
145
  echo -e " ${DIM}shipwright fleet status${RESET} # Fleet dashboard"
@@ -1365,6 +1367,11 @@ case "$SUBCOMMAND" in
1365
1367
  metrics)
1366
1368
  fleet_metrics
1367
1369
  ;;
1370
+ discover)
1371
+ # Delegate to fleet-discover script
1372
+ shift 2>/dev/null || true
1373
+ exec "$SCRIPT_DIR/sw-fleet-discover.sh" "$@"
1374
+ ;;
1368
1375
  init)
1369
1376
  fleet_init
1370
1377
  ;;