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.
- package/README.md +114 -36
- package/completions/_shipwright +212 -32
- package/completions/shipwright.bash +97 -25
- package/docs/strategy/01-market-research.md +619 -0
- package/docs/strategy/02-mission-and-brand.md +587 -0
- package/docs/strategy/03-gtm-and-roadmap.md +759 -0
- package/docs/strategy/QUICK-START.txt +289 -0
- package/docs/strategy/README.md +172 -0
- package/package.json +4 -2
- package/scripts/sw +208 -1
- package/scripts/sw-activity.sh +500 -0
- package/scripts/sw-adaptive.sh +925 -0
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +613 -0
- package/scripts/sw-autonomous.sh +664 -0
- package/scripts/sw-changelog.sh +704 -0
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +602 -0
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +637 -0
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +605 -0
- package/scripts/sw-cost.sh +1 -1
- package/scripts/sw-daemon.sh +432 -130
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +540 -0
- package/scripts/sw-decompose.sh +539 -0
- package/scripts/sw-deps.sh +551 -0
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +412 -0
- package/scripts/sw-docs-agent.sh +539 -0
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +59 -1
- package/scripts/sw-dora.sh +615 -0
- package/scripts/sw-durable.sh +710 -0
- package/scripts/sw-e2e-orchestrator.sh +535 -0
- package/scripts/sw-eventbus.sh +393 -0
- package/scripts/sw-feedback.sh +471 -0
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +567 -0
- package/scripts/sw-fleet-viz.sh +404 -0
- package/scripts/sw-fleet.sh +8 -1
- package/scripts/sw-github-app.sh +596 -0
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +569 -0
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +559 -0
- package/scripts/sw-incident.sh +617 -0
- package/scripts/sw-init.sh +88 -1
- package/scripts/sw-instrument.sh +699 -0
- package/scripts/sw-intelligence.sh +1 -1
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +363 -28
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +64 -3
- package/scripts/sw-memory.sh +1 -1
- package/scripts/sw-mission-control.sh +487 -0
- package/scripts/sw-model-router.sh +545 -0
- package/scripts/sw-otel.sh +596 -0
- package/scripts/sw-oversight.sh +689 -0
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +687 -24
- package/scripts/sw-pm.sh +693 -0
- package/scripts/sw-pr-lifecycle.sh +522 -0
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +798 -0
- package/scripts/sw-quality.sh +595 -0
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-recruit.sh +573 -0
- package/scripts/sw-regression.sh +642 -0
- package/scripts/sw-release-manager.sh +736 -0
- package/scripts/sw-release.sh +706 -0
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +520 -0
- package/scripts/sw-retro.sh +691 -0
- package/scripts/sw-scale.sh +444 -0
- package/scripts/sw-security-audit.sh +505 -0
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +1 -1
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +712 -0
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +658 -0
- package/scripts/sw-stream.sh +450 -0
- package/scripts/sw-swarm.sh +583 -0
- package/scripts/sw-team-stages.sh +511 -0
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +515 -0
- package/scripts/sw-tmux-pipeline.sh +554 -0
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +485 -0
- package/scripts/sw-tracker-github.sh +188 -0
- package/scripts/sw-tracker-jira.sh +172 -0
- package/scripts/sw-tracker-linear.sh +251 -0
- package/scripts/sw-tracker.sh +117 -2
- package/scripts/sw-triage.sh +603 -0
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +677 -0
- package/scripts/sw-webhook.sh +627 -0
- package/scripts/sw-widgets.sh +530 -0
- package/scripts/sw-worktree.sh +1 -1
package/scripts/sw-jira.sh
CHANGED
package/scripts/sw-launchd.sh
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
-
# ║ shipwright launchd — Process supervision
|
|
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="
|
|
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
|
|
|
@@ -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
|
-
# ───
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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
|
|
61
|
-
echo -e " ${CYAN}uninstall${RESET} Remove
|
|
62
|
-
echo -e " ${CYAN}status${RESET} Check status of
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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"
|
|
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"
|
|
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
|
-
|
|
697
|
+
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
698
|
+
main "$@"
|
|
699
|
+
fi
|
package/scripts/sw-linear.sh
CHANGED
package/scripts/sw-logs.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# ║ ║
|
|
5
5
|
# ║ Captures tmux pane scrollback and provides log browsing/search. ║
|
|
6
6
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
7
|
-
VERSION="
|
|
7
|
+
VERSION="2.0.0"
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
package/scripts/sw-loop.sh
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
12
12
|
|
|
13
|
+
# Allow spawning Claude CLI from within a Claude Code session (daemon, fleet, etc.)
|
|
14
|
+
unset CLAUDECODE 2>/dev/null || true
|
|
15
|
+
# Ignore SIGHUP so tmux attach/detach doesn't kill long-running agent sessions
|
|
16
|
+
trap '' HUP
|
|
17
|
+
|
|
13
18
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
19
|
|
|
15
20
|
# ─── Colors (matches shipwright theme) ──────────────────────────────────────────────
|
|
@@ -52,7 +57,7 @@ MAX_ITERATIONS_EXPLICIT=false
|
|
|
52
57
|
MAX_RESTARTS=0
|
|
53
58
|
SESSION_RESTART=false
|
|
54
59
|
RESTART_COUNT=0
|
|
55
|
-
VERSION="
|
|
60
|
+
VERSION="2.0.0"
|
|
56
61
|
|
|
57
62
|
# ─── Flexible Iteration Defaults ────────────────────────────────────────────
|
|
58
63
|
AUTO_EXTEND=true # Auto-extend iterations when work is incomplete
|
|
@@ -668,11 +673,50 @@ git_auto_commit() {
|
|
|
668
673
|
return 0
|
|
669
674
|
}
|
|
670
675
|
|
|
676
|
+
# ─── Fatal Error Detection ────────────────────────────────────────────────────
|
|
677
|
+
|
|
678
|
+
check_fatal_error() {
|
|
679
|
+
local log_file="$1"
|
|
680
|
+
local cli_exit_code="${2:-0}"
|
|
681
|
+
[[ -f "$log_file" ]] || return 1
|
|
682
|
+
|
|
683
|
+
# Known fatal error patterns from Claude CLI / Anthropic API
|
|
684
|
+
local fatal_patterns="Invalid API key|invalid_api_key|authentication_error|API key expired"
|
|
685
|
+
fatal_patterns="${fatal_patterns}|rate_limit_error|overloaded_error|billing"
|
|
686
|
+
fatal_patterns="${fatal_patterns}|Could not resolve host|connection refused|ECONNREFUSED"
|
|
687
|
+
fatal_patterns="${fatal_patterns}|ANTHROPIC_API_KEY.*not set|No API key"
|
|
688
|
+
|
|
689
|
+
if grep -qiE "$fatal_patterns" "$log_file" 2>/dev/null; then
|
|
690
|
+
local match
|
|
691
|
+
match=$(grep -iE "$fatal_patterns" "$log_file" 2>/dev/null | head -1 | cut -c1-120)
|
|
692
|
+
error "Fatal CLI error: $match"
|
|
693
|
+
return 0 # fatal error detected
|
|
694
|
+
fi
|
|
695
|
+
|
|
696
|
+
# Non-zero exit + tiny output = likely CLI crash
|
|
697
|
+
if [[ "$cli_exit_code" -ne 0 ]]; then
|
|
698
|
+
local line_count
|
|
699
|
+
line_count=$(grep -cv '^$' "$log_file" 2>/dev/null || echo 0)
|
|
700
|
+
if [[ "$line_count" -lt 3 ]]; then
|
|
701
|
+
local content
|
|
702
|
+
content=$(head -3 "$log_file" 2>/dev/null | cut -c1-120)
|
|
703
|
+
error "CLI exited $cli_exit_code with minimal output: $content"
|
|
704
|
+
return 0
|
|
705
|
+
fi
|
|
706
|
+
fi
|
|
707
|
+
|
|
708
|
+
return 1 # no fatal error
|
|
709
|
+
}
|
|
710
|
+
|
|
671
711
|
# ─── Progress & Circuit Breaker ───────────────────────────────────────────────
|
|
672
712
|
|
|
673
713
|
check_progress() {
|
|
674
714
|
local changes
|
|
675
|
-
|
|
715
|
+
# Exclude loop bookkeeping files — only count real code changes as progress
|
|
716
|
+
changes="$(git -C "$PROJECT_ROOT" diff --stat HEAD~1 \
|
|
717
|
+
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
718
|
+
':!**/progress.md' ':!**/error-summary.json' \
|
|
719
|
+
2>/dev/null | tail -1 || echo "")"
|
|
676
720
|
local insertions
|
|
677
721
|
insertions="$(echo "$changes" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo 0)"
|
|
678
722
|
if [[ "${insertions:-0}" -lt "$MIN_PROGRESS_LINES" ]]; then
|
|
@@ -1545,7 +1589,14 @@ extract_summary() {
|
|
|
1545
1589
|
local summary
|
|
1546
1590
|
summary="$(grep -v '^$' "$log_file" | tail -5 | head -3 2>/dev/null || echo "(no output)")"
|
|
1547
1591
|
# Truncate long lines
|
|
1548
|
-
echo "$summary" | cut -c1-120
|
|
1592
|
+
summary="$(echo "$summary" | cut -c1-120)"
|
|
1593
|
+
|
|
1594
|
+
# Sanitize: if summary is just a CLI/API error, replace with generic text
|
|
1595
|
+
if echo "$summary" | grep -qiE 'Invalid API key|authentication_error|rate_limit|API key expired|ANTHROPIC_API_KEY'; then
|
|
1596
|
+
summary="(CLI error — no useful output this iteration)"
|
|
1597
|
+
fi
|
|
1598
|
+
|
|
1599
|
+
echo "$summary"
|
|
1549
1600
|
}
|
|
1550
1601
|
|
|
1551
1602
|
# ─── Display Helpers ─────────────────────────────────────────────────────────
|
|
@@ -2009,6 +2060,16 @@ ${GOAL}"
|
|
|
2009
2060
|
|
|
2010
2061
|
local log_file="$LOG_DIR/iteration-${ITERATION}.log"
|
|
2011
2062
|
|
|
2063
|
+
# Detect fatal CLI errors (API key, auth, network) — abort immediately
|
|
2064
|
+
if check_fatal_error "$log_file" "$exit_code"; then
|
|
2065
|
+
STATUS="error"
|
|
2066
|
+
write_state
|
|
2067
|
+
write_progress
|
|
2068
|
+
error "Fatal CLI error detected — aborting loop (see iteration log)"
|
|
2069
|
+
show_summary
|
|
2070
|
+
return 1
|
|
2071
|
+
fi
|
|
2072
|
+
|
|
2012
2073
|
# Mid-loop memory refresh — re-query with current error context after iteration 3
|
|
2013
2074
|
if [[ "$ITERATION" -ge 3 ]] && type memory_inject_context &>/dev/null 2>&1; then
|
|
2014
2075
|
local refresh_ctx
|
package/scripts/sw-memory.sh
CHANGED