shipwright-cli 1.10.0 → 2.1.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 (121) hide show
  1. package/README.md +221 -55
  2. package/completions/_shipwright +264 -32
  3. package/completions/shipwright.bash +118 -26
  4. package/completions/shipwright.fish +80 -2
  5. package/dashboard/server.ts +208 -0
  6. package/docs/strategy/01-market-research.md +619 -0
  7. package/docs/strategy/02-mission-and-brand.md +587 -0
  8. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  9. package/docs/strategy/QUICK-START.txt +289 -0
  10. package/docs/strategy/README.md +172 -0
  11. package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
  12. package/docs/tmux-research/TMUX-AUDIT.md +925 -0
  13. package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
  14. package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
  15. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
  16. package/package.json +4 -2
  17. package/scripts/lib/helpers.sh +7 -0
  18. package/scripts/sw +323 -2
  19. package/scripts/sw-activity.sh +500 -0
  20. package/scripts/sw-adaptive.sh +925 -0
  21. package/scripts/sw-adversarial.sh +1 -1
  22. package/scripts/sw-architecture-enforcer.sh +1 -1
  23. package/scripts/sw-auth.sh +613 -0
  24. package/scripts/sw-autonomous.sh +754 -0
  25. package/scripts/sw-changelog.sh +704 -0
  26. package/scripts/sw-checkpoint.sh +1 -1
  27. package/scripts/sw-ci.sh +602 -0
  28. package/scripts/sw-cleanup.sh +1 -1
  29. package/scripts/sw-code-review.sh +698 -0
  30. package/scripts/sw-connect.sh +1 -1
  31. package/scripts/sw-context.sh +605 -0
  32. package/scripts/sw-cost.sh +44 -3
  33. package/scripts/sw-daemon.sh +568 -138
  34. package/scripts/sw-dashboard.sh +1 -1
  35. package/scripts/sw-db.sh +1380 -0
  36. package/scripts/sw-decompose.sh +539 -0
  37. package/scripts/sw-deps.sh +551 -0
  38. package/scripts/sw-developer-simulation.sh +1 -1
  39. package/scripts/sw-discovery.sh +412 -0
  40. package/scripts/sw-docs-agent.sh +539 -0
  41. package/scripts/sw-docs.sh +1 -1
  42. package/scripts/sw-doctor.sh +107 -1
  43. package/scripts/sw-dora.sh +615 -0
  44. package/scripts/sw-durable.sh +710 -0
  45. package/scripts/sw-e2e-orchestrator.sh +535 -0
  46. package/scripts/sw-eventbus.sh +393 -0
  47. package/scripts/sw-feedback.sh +479 -0
  48. package/scripts/sw-fix.sh +1 -1
  49. package/scripts/sw-fleet-discover.sh +567 -0
  50. package/scripts/sw-fleet-viz.sh +404 -0
  51. package/scripts/sw-fleet.sh +8 -1
  52. package/scripts/sw-github-app.sh +596 -0
  53. package/scripts/sw-github-checks.sh +4 -4
  54. package/scripts/sw-github-deploy.sh +1 -1
  55. package/scripts/sw-github-graphql.sh +1 -1
  56. package/scripts/sw-guild.sh +569 -0
  57. package/scripts/sw-heartbeat.sh +1 -1
  58. package/scripts/sw-hygiene.sh +559 -0
  59. package/scripts/sw-incident.sh +656 -0
  60. package/scripts/sw-init.sh +237 -24
  61. package/scripts/sw-instrument.sh +699 -0
  62. package/scripts/sw-intelligence.sh +1 -1
  63. package/scripts/sw-jira.sh +1 -1
  64. package/scripts/sw-launchd.sh +363 -28
  65. package/scripts/sw-linear.sh +1 -1
  66. package/scripts/sw-logs.sh +1 -1
  67. package/scripts/sw-loop.sh +267 -21
  68. package/scripts/sw-memory.sh +18 -1
  69. package/scripts/sw-mission-control.sh +487 -0
  70. package/scripts/sw-model-router.sh +545 -0
  71. package/scripts/sw-otel.sh +596 -0
  72. package/scripts/sw-oversight.sh +764 -0
  73. package/scripts/sw-pipeline-composer.sh +1 -1
  74. package/scripts/sw-pipeline-vitals.sh +1 -1
  75. package/scripts/sw-pipeline.sh +947 -35
  76. package/scripts/sw-pm.sh +758 -0
  77. package/scripts/sw-pr-lifecycle.sh +522 -0
  78. package/scripts/sw-predictive.sh +8 -1
  79. package/scripts/sw-prep.sh +1 -1
  80. package/scripts/sw-ps.sh +1 -1
  81. package/scripts/sw-public-dashboard.sh +798 -0
  82. package/scripts/sw-quality.sh +595 -0
  83. package/scripts/sw-reaper.sh +1 -1
  84. package/scripts/sw-recruit.sh +2248 -0
  85. package/scripts/sw-regression.sh +642 -0
  86. package/scripts/sw-release-manager.sh +736 -0
  87. package/scripts/sw-release.sh +706 -0
  88. package/scripts/sw-remote.sh +1 -1
  89. package/scripts/sw-replay.sh +520 -0
  90. package/scripts/sw-retro.sh +691 -0
  91. package/scripts/sw-scale.sh +444 -0
  92. package/scripts/sw-security-audit.sh +505 -0
  93. package/scripts/sw-self-optimize.sh +1 -1
  94. package/scripts/sw-session.sh +1 -1
  95. package/scripts/sw-setup.sh +263 -127
  96. package/scripts/sw-standup.sh +712 -0
  97. package/scripts/sw-status.sh +44 -2
  98. package/scripts/sw-strategic.sh +806 -0
  99. package/scripts/sw-stream.sh +450 -0
  100. package/scripts/sw-swarm.sh +620 -0
  101. package/scripts/sw-team-stages.sh +511 -0
  102. package/scripts/sw-templates.sh +4 -4
  103. package/scripts/sw-testgen.sh +566 -0
  104. package/scripts/sw-tmux-pipeline.sh +554 -0
  105. package/scripts/sw-tmux-role-color.sh +58 -0
  106. package/scripts/sw-tmux-status.sh +128 -0
  107. package/scripts/sw-tmux.sh +1 -1
  108. package/scripts/sw-trace.sh +485 -0
  109. package/scripts/sw-tracker-github.sh +188 -0
  110. package/scripts/sw-tracker-jira.sh +172 -0
  111. package/scripts/sw-tracker-linear.sh +251 -0
  112. package/scripts/sw-tracker.sh +117 -2
  113. package/scripts/sw-triage.sh +627 -0
  114. package/scripts/sw-upgrade.sh +1 -1
  115. package/scripts/sw-ux.sh +677 -0
  116. package/scripts/sw-webhook.sh +627 -0
  117. package/scripts/sw-widgets.sh +530 -0
  118. package/scripts/sw-worktree.sh +1 -1
  119. package/templates/pipelines/autonomous.json +2 -2
  120. package/tmux/shipwright-overlay.conf +35 -17
  121. package/tmux/tmux.conf +23 -21
@@ -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.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -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.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env bash
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
- # ║ shipwright launchd — Process supervision on macOS
4
- # ║ Auto-start daemon + dashboard on boot via launchd
3
+ # ║ shipwright launchd — Process supervision (macOS + Linux)
4
+ # ║ Auto-start daemon + dashboard on boot via launchd (macOS) or systemd
5
5
  # ╚═══════════════════════════════════════════════════════════════════════════╝
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.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -31,48 +31,111 @@ success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
31
31
  warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
32
32
  error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
33
33
 
34
- # ─── Constants ──────────────────────────────────────────────────────────────
35
- PLIST_DIR="$HOME/Library/LaunchAgents"
36
- DAEMON_PLIST="$PLIST_DIR/com.shipwright.daemon.plist"
37
- DASHBOARD_PLIST="$PLIST_DIR/com.shipwright.dashboard.plist"
38
- CONNECT_PLIST="$PLIST_DIR/com.shipwright.connect.plist"
39
- LOG_DIR="$HOME/.shipwright/logs"
40
- TEAM_CONFIG="$HOME/.shipwright/team-config.json"
41
-
42
- # ─── Check macOS ─────────────────────────────────────────────────────────────
43
- check_macos() {
44
- if [[ "$OSTYPE" != "darwin"* ]]; then
45
- error "launchd is only available on macOS"
46
- exit 1
34
+ # ─── OS Detection ───────────────────────────────────────────────────────────
35
+ detect_os() {
36
+ if [[ "$OSTYPE" == "darwin"* ]]; then
37
+ echo "macos"
38
+ elif [[ "$OSTYPE" == "linux-gnu"* ]] || [[ "$OSTYPE" == "linux"* ]]; then
39
+ echo "linux"
40
+ else
41
+ echo "unknown"
47
42
  fi
48
43
  }
49
44
 
45
+ OS_TYPE=$(detect_os)
46
+
47
+ # ─── Platform-specific Constants ────────────────────────────────────────────
48
+ if [[ "$OS_TYPE" == "macos" ]]; then
49
+ PLIST_DIR="$HOME/Library/LaunchAgents"
50
+ DAEMON_PLIST="$PLIST_DIR/com.shipwright.daemon.plist"
51
+ DASHBOARD_PLIST="$PLIST_DIR/com.shipwright.dashboard.plist"
52
+ CONNECT_PLIST="$PLIST_DIR/com.shipwright.connect.plist"
53
+ LOG_DIR="$HOME/.shipwright/logs"
54
+ elif [[ "$OS_TYPE" == "linux" ]]; then
55
+ SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
56
+ DAEMON_SERVICE="$SYSTEMD_USER_DIR/shipwright-daemon.service"
57
+ DASHBOARD_SERVICE="$SYSTEMD_USER_DIR/shipwright-dashboard.service"
58
+ CONNECT_SERVICE="$SYSTEMD_USER_DIR/shipwright-connect.service"
59
+ LOG_DIR="$HOME/.shipwright/logs"
60
+ fi
61
+
62
+ TEAM_CONFIG="$HOME/.shipwright/team-config.json"
63
+
50
64
  # ─── Help ───────────────────────────────────────────────────────────────────
51
65
  show_help() {
52
66
  echo ""
53
- echo -e "${CYAN}${BOLD} Shipwright launchd${RESET} ${DIM}v${VERSION}${RESET}"
67
+ echo -e "${CYAN}${BOLD} Shipwright Supervisor${RESET} ${DIM}v${VERSION}${RESET}"
54
68
  echo -e "${DIM} ════════════════════════════════════════════${RESET}"
55
69
  echo ""
56
70
  echo -e " ${BOLD}USAGE${RESET}"
57
71
  echo -e " shipwright launchd <command>"
58
72
  echo ""
73
+ echo -e " ${BOLD}PLATFORM${RESET}: $OS_TYPE"
74
+ echo ""
59
75
  echo -e " ${BOLD}COMMANDS${RESET}"
60
- echo -e " ${CYAN}install${RESET} Install launchd agents for daemon and dashboard (auto-start on boot)"
61
- echo -e " ${CYAN}uninstall${RESET} Remove launchd agents and stop services"
62
- echo -e " ${CYAN}status${RESET} Check status of launchd services"
76
+ echo -e " ${CYAN}install${RESET} Install services for daemon and dashboard (auto-start on boot)"
77
+ echo -e " ${CYAN}uninstall${RESET} Remove services and stop running daemons"
78
+ echo -e " ${CYAN}status${RESET} Check status of services"
79
+ echo -e " ${CYAN}logs${RESET} Tail service logs (systemd only)"
63
80
  echo -e " ${CYAN}help${RESET} Show this help message"
64
81
  echo ""
65
82
  echo -e " ${BOLD}EXAMPLES${RESET}"
66
83
  echo -e " ${DIM}shipwright launchd install${RESET} # Set up auto-start on boot"
67
84
  echo -e " ${DIM}shipwright launchd status${RESET} # Check if services are running"
85
+ echo -e " ${DIM}shipwright launchd logs${RESET} # View service logs"
68
86
  echo -e " ${DIM}shipwright launchd uninstall${RESET} # Remove auto-start"
69
87
  echo ""
70
88
  }
71
89
 
90
+ # ─── Systemd Unit File Generator (Linux) ──────────────────────────────────
91
+ create_systemd_unit() {
92
+ local unit_name="$1"
93
+ local description="$2"
94
+ local exec_start="$3"
95
+ local output_file="$4"
96
+
97
+ mkdir -p "$(dirname "$output_file")"
98
+
99
+ cat > "$output_file" <<EOF
100
+ [Unit]
101
+ Description=${description}
102
+ After=network.target
103
+ Wants=network-online.target
104
+
105
+ [Service]
106
+ Type=simple
107
+ ExecStart=${exec_start}
108
+ Restart=on-failure
109
+ RestartSec=10
110
+ StandardOutput=journal
111
+ StandardError=journal
112
+ SyslogIdentifier=${unit_name}
113
+ Environment="PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:\$HOME/.local/bin"
114
+ Environment="HOME=\$HOME"
115
+
116
+ [Install]
117
+ WantedBy=default.target
118
+ EOF
119
+
120
+ chmod 644 "$output_file"
121
+ }
122
+
72
123
  # ─── Install Command ─────────────────────────────────────────────────────────
73
124
  cmd_install() {
74
- check_macos
125
+ if [[ "$OS_TYPE" == "unknown" ]]; then
126
+ error "Unsupported OS type: $OSTYPE"
127
+ exit 1
128
+ fi
129
+
130
+ if [[ "$OS_TYPE" == "macos" ]]; then
131
+ cmd_install_macos
132
+ else
133
+ cmd_install_linux
134
+ fi
135
+ }
75
136
 
137
+ # ─── Install macOS launchd agents ──────────────────────────────────────────
138
+ cmd_install_macos() {
76
139
  info "Installing launchd agents..."
77
140
 
78
141
  # Create directories
@@ -250,10 +313,135 @@ EOF
250
313
  info "Uninstall: ${DIM}shipwright launchd uninstall${RESET}"
251
314
  }
252
315
 
316
+ # ─── Install Linux systemd user services ───────────────────────────────────
317
+ cmd_install_linux() {
318
+ info "Installing systemd user services..."
319
+
320
+ # Create directories
321
+ mkdir -p "$SYSTEMD_USER_DIR" "$LOG_DIR"
322
+
323
+ # Find the full path to the sw CLI
324
+ local sw_bin
325
+ if [[ -x "$SCRIPT_DIR/sw" ]]; then
326
+ sw_bin="$SCRIPT_DIR/sw"
327
+ else
328
+ # Try to find it via PATH
329
+ sw_bin=$(command -v sw 2>/dev/null || echo "")
330
+ if [[ -z "$sw_bin" ]]; then
331
+ error "Could not find 'sw' binary — make sure Shipwright is installed"
332
+ exit 1
333
+ fi
334
+ fi
335
+
336
+ # Resolve symlinks in case sw is symlinked
337
+ sw_bin=$(cd "$(dirname "$sw_bin")" && pwd)/$(basename "$sw_bin")
338
+
339
+ # ─── Create Daemon Service ─────────────────────────────────────────────────
340
+ create_systemd_unit "shipwright-daemon" \
341
+ "Shipwright Daemon - Autonomous Issue Processor" \
342
+ "env -u CLAUDECODE ${sw_bin} daemon start --detach" \
343
+ "$DAEMON_SERVICE"
344
+
345
+ success "Created daemon service: ${DAEMON_SERVICE}"
346
+
347
+ # ─── Create Dashboard Service ──────────────────────────────────────────────
348
+ local bun_bin
349
+ bun_bin=$(command -v bun 2>/dev/null || echo "bun")
350
+
351
+ local server_file="$REPO_DIR/dashboard/server.ts"
352
+ if [[ ! -f "$server_file" ]]; then
353
+ warn "server.ts not found at $server_file — dashboard service will reference a missing file"
354
+ fi
355
+
356
+ create_systemd_unit "shipwright-dashboard" \
357
+ "Shipwright Dashboard - Real-time Team Status" \
358
+ "${bun_bin} run ${server_file}" \
359
+ "$DASHBOARD_SERVICE"
360
+
361
+ success "Created dashboard service: ${DASHBOARD_SERVICE}"
362
+
363
+ # ─── Create Connect Service (only if team-config.json exists) ──────────────
364
+ if [[ -f "$TEAM_CONFIG" ]]; then
365
+ create_systemd_unit "shipwright-connect" \
366
+ "Shipwright Connect - Team Sync Service" \
367
+ "env -u CLAUDECODE ${sw_bin} connect start" \
368
+ "$CONNECT_SERVICE"
369
+
370
+ success "Created connect service: ${CONNECT_SERVICE}"
371
+ else
372
+ info "Skipping connect service — ${TEAM_CONFIG} not found"
373
+ fi
374
+
375
+ # ─── Enable and Start Services ─────────────────────────────────────────────
376
+ info "Enabling systemd user services..."
377
+
378
+ if systemctl --user enable shipwright-daemon.service 2>/dev/null; then
379
+ success "Enabled daemon service"
380
+ else
381
+ warn "Could not enable daemon service"
382
+ fi
383
+
384
+ if systemctl --user enable shipwright-dashboard.service 2>/dev/null; then
385
+ success "Enabled dashboard service"
386
+ else
387
+ warn "Could not enable dashboard service"
388
+ fi
389
+
390
+ if [[ -f "$CONNECT_SERVICE" ]]; then
391
+ if systemctl --user enable shipwright-connect.service 2>/dev/null; then
392
+ success "Enabled connect service"
393
+ else
394
+ warn "Could not enable connect service"
395
+ fi
396
+ fi
397
+
398
+ # Start services immediately
399
+ info "Starting systemd user services..."
400
+
401
+ if systemctl --user start shipwright-daemon.service 2>/dev/null; then
402
+ success "Started daemon service"
403
+ else
404
+ warn "Could not start daemon service — enable user lingering first: loginctl enable-linger"
405
+ fi
406
+
407
+ if systemctl --user start shipwright-dashboard.service 2>/dev/null; then
408
+ success "Started dashboard service"
409
+ else
410
+ warn "Could not start dashboard service"
411
+ fi
412
+
413
+ if [[ -f "$CONNECT_SERVICE" ]]; then
414
+ if systemctl --user start shipwright-connect.service 2>/dev/null; then
415
+ success "Started connect service"
416
+ else
417
+ warn "Could not start connect service"
418
+ fi
419
+ fi
420
+
421
+ echo ""
422
+ info "Services will auto-start on next login (with systemd lingering enabled)"
423
+ info "Enable lingering: ${DIM}loginctl enable-linger${RESET}"
424
+ info "View logs: ${DIM}journalctl --user -u shipwright-daemon -f${RESET}"
425
+ info "View all logs: ${DIM}journalctl --user -u shipwright-* -f${RESET}"
426
+ info "Uninstall: ${DIM}shipwright launchd uninstall${RESET}"
427
+ }
428
+
253
429
  # ─── Uninstall Command ──────────────────────────────────────────────────────
254
430
  cmd_uninstall() {
255
- check_macos
431
+ if [[ "$OS_TYPE" == "unknown" ]]; then
432
+ error "Unsupported OS type: $OSTYPE"
433
+ exit 1
434
+ fi
435
+
436
+ if [[ "$OS_TYPE" == "macos" ]]; then
437
+ cmd_uninstall_macos
438
+ else
439
+ cmd_uninstall_linux
440
+ fi
441
+ }
256
442
 
443
+ # ─── Uninstall macOS launchd agents ────────────────────────────────────────
444
+ cmd_uninstall_macos() {
257
445
  info "Uninstalling launchd agents..."
258
446
 
259
447
  # Unload daemon
@@ -293,31 +481,112 @@ cmd_uninstall() {
293
481
  success "Uninstalled all launchd agents"
294
482
  }
295
483
 
484
+ # ─── Uninstall Linux systemd user services ────────────────────────────────
485
+ cmd_uninstall_linux() {
486
+ info "Uninstalling systemd user services..."
487
+
488
+ # Stop and disable daemon
489
+ if systemctl --user is-active --quiet shipwright-daemon.service 2>/dev/null; then
490
+ if systemctl --user stop shipwright-daemon.service 2>/dev/null; then
491
+ success "Stopped daemon service"
492
+ else
493
+ warn "Could not stop daemon service"
494
+ fi
495
+ fi
496
+
497
+ if systemctl --user is-enabled --quiet shipwright-daemon.service 2>/dev/null; then
498
+ if systemctl --user disable shipwright-daemon.service 2>/dev/null; then
499
+ success "Disabled daemon service"
500
+ else
501
+ warn "Could not disable daemon service"
502
+ fi
503
+ fi
504
+
505
+ # Stop and disable dashboard
506
+ if systemctl --user is-active --quiet shipwright-dashboard.service 2>/dev/null; then
507
+ if systemctl --user stop shipwright-dashboard.service 2>/dev/null; then
508
+ success "Stopped dashboard service"
509
+ else
510
+ warn "Could not stop dashboard service"
511
+ fi
512
+ fi
513
+
514
+ if systemctl --user is-enabled --quiet shipwright-dashboard.service 2>/dev/null; then
515
+ if systemctl --user disable shipwright-dashboard.service 2>/dev/null; then
516
+ success "Disabled dashboard service"
517
+ else
518
+ warn "Could not disable dashboard service"
519
+ fi
520
+ fi
521
+
522
+ # Stop and disable connect
523
+ if systemctl --user is-active --quiet shipwright-connect.service 2>/dev/null; then
524
+ if systemctl --user stop shipwright-connect.service 2>/dev/null; then
525
+ success "Stopped connect service"
526
+ else
527
+ warn "Could not stop connect service"
528
+ fi
529
+ fi
530
+
531
+ if systemctl --user is-enabled --quiet shipwright-connect.service 2>/dev/null; then
532
+ if systemctl --user disable shipwright-connect.service 2>/dev/null; then
533
+ success "Disabled connect service"
534
+ else
535
+ warn "Could not disable connect service"
536
+ fi
537
+ fi
538
+
539
+ # Remove service files
540
+ [[ -f "$DAEMON_SERVICE" ]] && rm -f "$DAEMON_SERVICE" && success "Removed daemon service file"
541
+ [[ -f "$DASHBOARD_SERVICE" ]] && rm -f "$DASHBOARD_SERVICE" && success "Removed dashboard service file"
542
+ [[ -f "$CONNECT_SERVICE" ]] && rm -f "$CONNECT_SERVICE" && success "Removed connect service file"
543
+
544
+ # Reload systemd daemon
545
+ if systemctl --user daemon-reload 2>/dev/null; then
546
+ success "Reloaded systemd user daemon"
547
+ fi
548
+
549
+ echo ""
550
+ success "Uninstalled all systemd user services"
551
+ }
552
+
296
553
  # ─── Status Command ─────────────────────────────────────────────────────────
297
554
  cmd_status() {
298
- check_macos
555
+ if [[ "$OS_TYPE" == "unknown" ]]; then
556
+ error "Unsupported OS type: $OSTYPE"
557
+ exit 1
558
+ fi
299
559
 
560
+ if [[ "$OS_TYPE" == "macos" ]]; then
561
+ cmd_status_macos
562
+ else
563
+ cmd_status_linux
564
+ fi
565
+ }
566
+
567
+ # ─── Status macOS launchd services ─────────────────────────────────────────
568
+ cmd_status_macos() {
300
569
  echo ""
301
570
  echo -e "${CYAN}${BOLD}Launchd Services${RESET}"
302
571
  echo -e "${DIM}════════════════════════════════════════════${RESET}"
303
572
  echo ""
304
573
 
305
574
  # Check daemon
306
- if launchctl list | grep -q "com.shipwright.daemon" 2>/dev/null; then
575
+ if launchctl list 2>/dev/null | grep -q "com.shipwright.daemon"; then
307
576
  echo -e " ${GREEN}●${RESET} Daemon service is ${GREEN}loaded${RESET}"
308
577
  else
309
578
  echo -e " ${RED}○${RESET} Daemon service is ${RED}not loaded${RESET}"
310
579
  fi
311
580
 
312
581
  # Check dashboard
313
- if launchctl list | grep -q "com.shipwright.dashboard" 2>/dev/null; then
582
+ if launchctl list 2>/dev/null | grep -q "com.shipwright.dashboard"; then
314
583
  echo -e " ${GREEN}●${RESET} Dashboard service is ${GREEN}loaded${RESET}"
315
584
  else
316
585
  echo -e " ${RED}○${RESET} Dashboard service is ${RED}not loaded${RESET}"
317
586
  fi
318
587
 
319
588
  # Check connect
320
- if launchctl list | grep -q "com.shipwright.connect" 2>/dev/null; then
589
+ if launchctl list 2>/dev/null | grep -q "com.shipwright.connect"; then
321
590
  echo -e " ${GREEN}●${RESET} Connect service is ${GREEN}loaded${RESET}"
322
591
  else
323
592
  echo -e " ${RED}○${RESET} Connect service is ${RED}not loaded${RESET}"
@@ -330,11 +599,72 @@ cmd_status() {
330
599
  # Show recent log entries
331
600
  if [[ -f "$LOG_DIR/daemon.stdout.log" ]]; then
332
601
  echo -e "${DIM}Recent daemon logs:${RESET}"
333
- tail -3 "$LOG_DIR/daemon.stdout.log" | sed 's/^/ /'
602
+ tail -3 "$LOG_DIR/daemon.stdout.log" 2>/dev/null | sed 's/^/ /'
334
603
  echo ""
335
604
  fi
336
605
  }
337
606
 
607
+ # ─── Status Linux systemd services ────────────────────────────────────────
608
+ cmd_status_linux() {
609
+ echo ""
610
+ echo -e "${CYAN}${BOLD}Systemd User Services${RESET}"
611
+ echo -e "${DIM}════════════════════════════════════════════${RESET}"
612
+ echo ""
613
+
614
+ # Check daemon
615
+ if systemctl --user is-active --quiet shipwright-daemon.service 2>/dev/null; then
616
+ echo -e " ${GREEN}●${RESET} Daemon service is ${GREEN}running${RESET}"
617
+ else
618
+ echo -e " ${RED}○${RESET} Daemon service is ${RED}not running${RESET}"
619
+ fi
620
+
621
+ # Check dashboard
622
+ if systemctl --user is-active --quiet shipwright-dashboard.service 2>/dev/null; then
623
+ echo -e " ${GREEN}●${RESET} Dashboard service is ${GREEN}running${RESET}"
624
+ else
625
+ echo -e " ${RED}○${RESET} Dashboard service is ${RED}not running${RESET}"
626
+ fi
627
+
628
+ # Check connect
629
+ if systemctl --user is-active --quiet shipwright-connect.service 2>/dev/null; then
630
+ echo -e " ${GREEN}●${RESET} Connect service is ${GREEN}running${RESET}"
631
+ else
632
+ echo -e " ${RED}○${RESET} Connect service is ${RED}not running${RESET}"
633
+ fi
634
+
635
+ echo ""
636
+ echo -e " Services: ${DIM}${SYSTEMD_USER_DIR}${RESET}"
637
+ echo -e " Logs: ${DIM}journalctl --user${RESET}"
638
+ echo ""
639
+
640
+ # Show recent journal entries for daemon
641
+ if command -v journalctl &>/dev/null; then
642
+ echo -e "${DIM}Recent daemon logs:${RESET}"
643
+ journalctl --user -u shipwright-daemon.service -n 3 --no-pager 2>/dev/null | tail -3 | sed 's/^/ /' || true
644
+ echo ""
645
+ fi
646
+ }
647
+
648
+ # ─── Logs Command (systemd only) ────────────────────────────────────────────
649
+ cmd_logs() {
650
+ if [[ "$OS_TYPE" != "linux" ]]; then
651
+ error "logs command is only available on Linux (systemd)"
652
+ exit 1
653
+ fi
654
+
655
+ local service="${1:-shipwright-daemon.service}"
656
+
657
+ if ! systemctl --user is-active --quiet "$service" 2>/dev/null; then
658
+ warn "Service $service is not running"
659
+ fi
660
+
661
+ echo ""
662
+ info "Tailing logs for $service (Ctrl-C to stop)..."
663
+ echo ""
664
+
665
+ journalctl --user -u "$service" -f --no-pager
666
+ }
667
+
338
668
  # ─── Main ───────────────────────────────────────────────────────────────────
339
669
  main() {
340
670
  local cmd="${1:-help}"
@@ -349,6 +679,9 @@ main() {
349
679
  status)
350
680
  cmd_status
351
681
  ;;
682
+ logs)
683
+ cmd_logs "${2:-}"
684
+ ;;
352
685
  help|--help|-h)
353
686
  show_help
354
687
  ;;
@@ -361,4 +694,6 @@ main() {
361
694
  esac
362
695
  }
363
696
 
364
- main "$@"
697
+ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
698
+ main "$@"
699
+ 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.1.0"
10
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
12
12
 
@@ -4,7 +4,7 @@
4
4
  # ║ ║
5
5
  # ║ Captures tmux pane scrollback and provides log browsing/search. ║
6
6
  # ╚═══════════════════════════════════════════════════════════════════════════╝
7
- VERSION="1.10.0"
7
+ VERSION="2.1.0"
8
8
  set -euo pipefail
9
9
  trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
10
10