shipwright-cli 2.4.0 → 3.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 +16 -11
- package/completions/_shipwright +1 -1
- package/completions/shipwright.bash +3 -8
- package/completions/shipwright.fish +1 -1
- package/config/defaults.json +111 -0
- package/config/event-schema.json +81 -0
- package/config/policy.json +13 -18
- package/dashboard/coverage/coverage-summary.json +14 -0
- package/dashboard/public/index.html +1 -1
- package/dashboard/server.ts +306 -17
- package/dashboard/src/components/charts/bar.test.ts +79 -0
- package/dashboard/src/components/charts/donut.test.ts +68 -0
- package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
- package/dashboard/src/components/charts/sparkline.test.ts +125 -0
- package/dashboard/src/core/api.test.ts +309 -0
- package/dashboard/src/core/helpers.test.ts +301 -0
- package/dashboard/src/core/router.test.ts +307 -0
- package/dashboard/src/core/router.ts +7 -0
- package/dashboard/src/core/sse.test.ts +144 -0
- package/dashboard/src/views/metrics.test.ts +186 -0
- package/dashboard/src/views/overview.test.ts +173 -0
- package/dashboard/src/views/pipelines.test.ts +183 -0
- package/dashboard/src/views/team.test.ts +253 -0
- package/dashboard/vitest.config.ts +14 -5
- package/docs/TIPS.md +1 -1
- package/docs/patterns/README.md +1 -1
- package/package.json +5 -7
- package/scripts/adapters/docker-deploy.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +11 -1
- package/scripts/adapters/wezterm-adapter.sh +1 -1
- package/scripts/check-version-consistency.sh +1 -1
- package/scripts/lib/architecture.sh +126 -0
- package/scripts/lib/bootstrap.sh +75 -0
- package/scripts/lib/compat.sh +89 -6
- package/scripts/lib/config.sh +91 -0
- package/scripts/lib/daemon-adaptive.sh +3 -3
- package/scripts/lib/daemon-dispatch.sh +39 -16
- package/scripts/lib/daemon-health.sh +1 -1
- package/scripts/lib/daemon-patrol.sh +24 -12
- package/scripts/lib/daemon-poll.sh +37 -25
- package/scripts/lib/daemon-state.sh +115 -23
- package/scripts/lib/daemon-triage.sh +30 -8
- package/scripts/lib/fleet-failover.sh +63 -0
- package/scripts/lib/helpers.sh +30 -6
- package/scripts/lib/pipeline-detection.sh +2 -2
- package/scripts/lib/pipeline-github.sh +9 -9
- package/scripts/lib/pipeline-intelligence.sh +85 -35
- package/scripts/lib/pipeline-quality-checks.sh +16 -16
- package/scripts/lib/pipeline-quality.sh +1 -1
- package/scripts/lib/pipeline-stages.sh +242 -28
- package/scripts/lib/pipeline-state.sh +40 -4
- package/scripts/lib/test-helpers.sh +247 -0
- package/scripts/postinstall.mjs +3 -11
- package/scripts/sw +10 -4
- package/scripts/sw-activity.sh +1 -11
- package/scripts/sw-adaptive.sh +109 -85
- package/scripts/sw-adversarial.sh +4 -14
- package/scripts/sw-architecture-enforcer.sh +1 -11
- package/scripts/sw-auth.sh +8 -17
- package/scripts/sw-autonomous.sh +111 -49
- package/scripts/sw-changelog.sh +1 -11
- package/scripts/sw-checkpoint.sh +144 -20
- package/scripts/sw-ci.sh +2 -12
- package/scripts/sw-cleanup.sh +13 -17
- package/scripts/sw-code-review.sh +16 -36
- package/scripts/sw-connect.sh +5 -12
- package/scripts/sw-context.sh +9 -26
- package/scripts/sw-cost.sh +6 -16
- package/scripts/sw-daemon.sh +75 -70
- package/scripts/sw-dashboard.sh +57 -17
- package/scripts/sw-db.sh +506 -15
- package/scripts/sw-decompose.sh +1 -11
- package/scripts/sw-deps.sh +15 -25
- package/scripts/sw-developer-simulation.sh +1 -11
- package/scripts/sw-discovery.sh +112 -30
- package/scripts/sw-doc-fleet.sh +7 -17
- package/scripts/sw-docs-agent.sh +6 -16
- package/scripts/sw-docs.sh +4 -12
- package/scripts/sw-doctor.sh +134 -43
- package/scripts/sw-dora.sh +11 -19
- package/scripts/sw-durable.sh +35 -52
- package/scripts/sw-e2e-orchestrator.sh +11 -27
- package/scripts/sw-eventbus.sh +115 -115
- package/scripts/sw-evidence.sh +114 -30
- package/scripts/sw-feedback.sh +3 -13
- package/scripts/sw-fix.sh +2 -20
- package/scripts/sw-fleet-discover.sh +1 -11
- package/scripts/sw-fleet-viz.sh +10 -18
- package/scripts/sw-fleet.sh +13 -17
- package/scripts/sw-github-app.sh +6 -16
- package/scripts/sw-github-checks.sh +1 -11
- package/scripts/sw-github-deploy.sh +1 -11
- package/scripts/sw-github-graphql.sh +2 -12
- package/scripts/sw-guild.sh +1 -11
- package/scripts/sw-heartbeat.sh +49 -12
- package/scripts/sw-hygiene.sh +45 -43
- package/scripts/sw-incident.sh +48 -74
- package/scripts/sw-init.sh +35 -37
- package/scripts/sw-instrument.sh +1 -11
- package/scripts/sw-intelligence.sh +362 -51
- package/scripts/sw-jira.sh +5 -14
- package/scripts/sw-launchd.sh +2 -12
- package/scripts/sw-linear.sh +8 -17
- package/scripts/sw-logs.sh +4 -12
- package/scripts/sw-loop.sh +641 -90
- package/scripts/sw-memory.sh +243 -17
- package/scripts/sw-mission-control.sh +2 -12
- package/scripts/sw-model-router.sh +73 -34
- package/scripts/sw-otel.sh +11 -21
- package/scripts/sw-oversight.sh +1 -11
- package/scripts/sw-patrol-meta.sh +5 -11
- package/scripts/sw-pipeline-composer.sh +7 -17
- package/scripts/sw-pipeline-vitals.sh +1 -11
- package/scripts/sw-pipeline.sh +478 -122
- package/scripts/sw-pm.sh +2 -12
- package/scripts/sw-pr-lifecycle.sh +27 -25
- package/scripts/sw-predictive.sh +16 -22
- package/scripts/sw-prep.sh +6 -16
- package/scripts/sw-ps.sh +1 -11
- package/scripts/sw-public-dashboard.sh +2 -12
- package/scripts/sw-quality.sh +77 -10
- package/scripts/sw-reaper.sh +1 -11
- package/scripts/sw-recruit.sh +15 -25
- package/scripts/sw-regression.sh +11 -21
- package/scripts/sw-release-manager.sh +19 -28
- package/scripts/sw-release.sh +8 -16
- package/scripts/sw-remote.sh +1 -11
- package/scripts/sw-replay.sh +48 -44
- package/scripts/sw-retro.sh +70 -92
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +109 -32
- package/scripts/sw-security-audit.sh +12 -22
- package/scripts/sw-self-optimize.sh +239 -23
- package/scripts/sw-session.sh +3 -13
- package/scripts/sw-setup.sh +8 -18
- package/scripts/sw-standup.sh +5 -15
- package/scripts/sw-status.sh +32 -23
- package/scripts/sw-strategic.sh +129 -13
- package/scripts/sw-stream.sh +1 -11
- package/scripts/sw-swarm.sh +76 -36
- package/scripts/sw-team-stages.sh +10 -20
- package/scripts/sw-templates.sh +4 -14
- package/scripts/sw-testgen.sh +3 -13
- package/scripts/sw-tmux-pipeline.sh +1 -19
- package/scripts/sw-tmux-role-color.sh +0 -10
- package/scripts/sw-tmux-status.sh +3 -11
- package/scripts/sw-tmux.sh +2 -20
- package/scripts/sw-trace.sh +1 -19
- package/scripts/sw-tracker-github.sh +0 -10
- package/scripts/sw-tracker-jira.sh +1 -11
- package/scripts/sw-tracker-linear.sh +1 -11
- package/scripts/sw-tracker.sh +7 -24
- package/scripts/sw-triage.sh +24 -34
- package/scripts/sw-upgrade.sh +5 -23
- package/scripts/sw-ux.sh +1 -19
- package/scripts/sw-webhook.sh +18 -32
- package/scripts/sw-widgets.sh +3 -21
- package/scripts/sw-worktree.sh +11 -27
- package/scripts/update-homebrew-sha.sh +67 -0
- package/templates/pipelines/tdd.json +72 -0
- package/scripts/sw-pipeline.sh.mock +0 -7
package/scripts/sw-incident.sh
CHANGED
|
@@ -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="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -16,6 +16,10 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
16
16
|
# Canonical helpers (colors, output, events)
|
|
17
17
|
# shellcheck source=lib/helpers.sh
|
|
18
18
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
19
|
+
# DB layer for dual-read (SQLite + JSONL fallback)
|
|
20
|
+
# shellcheck source=sw-db.sh
|
|
21
|
+
[[ -f "$SCRIPT_DIR/sw-db.sh" ]] && source "$SCRIPT_DIR/sw-db.sh"
|
|
22
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
19
23
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
20
24
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
21
25
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -25,24 +29,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
25
29
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
30
|
now_epoch() { date +%s; }
|
|
27
31
|
fi
|
|
28
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
29
|
-
emit_event() {
|
|
30
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
31
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
32
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
33
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
34
|
-
}
|
|
35
|
-
fi
|
|
36
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
37
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
38
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
39
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
40
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
41
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
42
|
-
DIM="${DIM:-\033[2m}"
|
|
43
|
-
BOLD="${BOLD:-\033[1m}"
|
|
44
|
-
RESET="${RESET:-\033[0m}"
|
|
45
|
-
|
|
46
32
|
format_duration() {
|
|
47
33
|
local secs="$1"
|
|
48
34
|
if [[ "$secs" -ge 3600 ]]; then
|
|
@@ -93,50 +79,25 @@ detect_pipeline_failures() {
|
|
|
93
79
|
local since="${1:-3600}" # Last N seconds
|
|
94
80
|
local cutoff_time=$(($(now_epoch) - since))
|
|
95
81
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
awk -v cutoff="$cutoff_time" -F'"' '
|
|
99
|
-
BEGIN { count=0 }
|
|
100
|
-
/pipeline\.failed|stage\.failed|test\.failed|deploy\.failed/ {
|
|
101
|
-
for (i=1; i<=NF; i++) {
|
|
102
|
-
if ($i ~ /ts_epoch/) {
|
|
103
|
-
ts_epoch_val=$(i+2)
|
|
104
|
-
gsub(/^[^0-9]*/, "", ts_epoch_val)
|
|
105
|
-
gsub(/[^0-9].*/, "", ts_epoch_val)
|
|
106
|
-
if (ts_epoch_val+0 > cutoff) {
|
|
107
|
-
print $0
|
|
108
|
-
count++
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
END { exit (count > 0 ? 0 : 1) }
|
|
114
|
-
' "$EVENTS_FILE"
|
|
82
|
+
db_query_events_since "$cutoff_time" 2>/dev/null | jq -e 'map(select((.type | tostring) | (test("failed") or test("error") or test("timeout")))) | length > 0' >/dev/null 2>/dev/null && return 0 || return 1
|
|
115
83
|
}
|
|
116
84
|
|
|
117
85
|
get_recent_failures() {
|
|
118
86
|
local since="${1:-3600}"
|
|
119
87
|
local cutoff_time=$(($(now_epoch) - since))
|
|
120
88
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
issue: .issue,
|
|
134
|
-
stage: .stage,
|
|
135
|
-
reason: .reason,
|
|
136
|
-
error: .error
|
|
137
|
-
}
|
|
138
|
-
)
|
|
139
|
-
' "$EVENTS_FILE" 2>/dev/null || echo "[]"
|
|
89
|
+
db_query_events_since "$cutoff_time" 2>/dev/null | jq '
|
|
90
|
+
map(select((.type | tostring) | (test("failed") or test("error") or test("timeout")))) |
|
|
91
|
+
map({
|
|
92
|
+
ts: .ts,
|
|
93
|
+
ts_epoch: .ts_epoch,
|
|
94
|
+
type: .type,
|
|
95
|
+
issue: .issue,
|
|
96
|
+
stage: .stage,
|
|
97
|
+
reason: .reason,
|
|
98
|
+
error: .error
|
|
99
|
+
})
|
|
100
|
+
' 2>/dev/null || echo "[]"
|
|
140
101
|
}
|
|
141
102
|
|
|
142
103
|
# ─── Severity Classification ───────────────────────────────────────────────
|
|
@@ -226,7 +187,7 @@ create_hotfix_issue() {
|
|
|
226
187
|
local severity="$2"
|
|
227
188
|
local root_cause="$3"
|
|
228
189
|
|
|
229
|
-
if ! command -v gh
|
|
190
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
230
191
|
warn "gh CLI not found, skipping GitHub issue creation"
|
|
231
192
|
return 1
|
|
232
193
|
fi
|
|
@@ -245,7 +206,7 @@ This issue was automatically created by the incident commander.
|
|
|
245
206
|
|
|
246
207
|
# shipwright label so daemon picks up; hotfix for routing
|
|
247
208
|
local issue_url
|
|
248
|
-
issue_url=$(gh issue create --title "$title" --body "$body" --label "hotfix,shipwright" 2>/dev/null || echo "")
|
|
209
|
+
issue_url=$(gh issue create --title "$title" --body "$body" --label "$(_config_get "labels.incident_labels" "hotfix,shipwright")" 2>/dev/null || echo "")
|
|
249
210
|
|
|
250
211
|
if [[ -n "$issue_url" ]]; then
|
|
251
212
|
success "Created hotfix issue: $issue_url"
|
|
@@ -314,7 +275,7 @@ cmd_watch() {
|
|
|
314
275
|
local failures_json
|
|
315
276
|
failures_json=$(get_recent_failures "$interval")
|
|
316
277
|
local failure_count
|
|
317
|
-
failure_count=$(echo "$failures_json" | jq 'length')
|
|
278
|
+
failure_count=$(echo "$failures_json" | jq 'length' 2>/dev/null || echo "0")
|
|
318
279
|
|
|
319
280
|
if [[ "$failure_count" -gt 0 ]]; then
|
|
320
281
|
info "Detected $failure_count failure(s)"
|
|
@@ -510,14 +471,14 @@ cmd_stats() {
|
|
|
510
471
|
mttr=$(jq -r '.mttr_seconds // 0' "$incident_file" 2>/dev/null || echo "0")
|
|
511
472
|
|
|
512
473
|
case "$sev" in
|
|
513
|
-
P0) ((p0_count
|
|
514
|
-
P1) ((p1_count
|
|
515
|
-
P2) ((p2_count
|
|
516
|
-
*) ((p3_count
|
|
474
|
+
P0) p0_count=$((p0_count + 1)) ;;
|
|
475
|
+
P1) p1_count=$((p1_count + 1)) ;;
|
|
476
|
+
P2) p2_count=$((p2_count + 1)) ;;
|
|
477
|
+
*) p3_count=$((p3_count + 1)) ;;
|
|
517
478
|
esac
|
|
518
479
|
|
|
519
480
|
if [[ "$status" == "resolved" ]]; then
|
|
520
|
-
((resolved_count
|
|
481
|
+
resolved_count=$((resolved_count + 1))
|
|
521
482
|
mttr_sum=$((mttr_sum + mttr))
|
|
522
483
|
fi
|
|
523
484
|
done <<< "$incident_files"
|
|
@@ -629,7 +590,7 @@ EOF
|
|
|
629
590
|
emit_event "harness_gap.created" "gap_id=${gap_id}" "incident=${incident_id}" "sla_hours=${sla_hours}"
|
|
630
591
|
|
|
631
592
|
# Auto-create GitHub issue for gap tracking
|
|
632
|
-
if [[ "$HARNESS_GAP_AUTO_CREATE" == "true" ]] && command -v gh
|
|
593
|
+
if [[ "$HARNESS_GAP_AUTO_CREATE" == "true" ]] && command -v gh >/dev/null 2>&1; then
|
|
633
594
|
local title="[HARNESS GAP] ${severity}: Add test case for ${root_cause}"
|
|
634
595
|
local body="## Harness Gap
|
|
635
596
|
|
|
@@ -656,7 +617,7 @@ must produce a harness test case within the SLA window.
|
|
|
656
617
|
*Auto-generated by Shipwright Code Factory*"
|
|
657
618
|
|
|
658
619
|
local issue_url
|
|
659
|
-
issue_url=$(gh issue create --title "$title" --body "$body" --label "harness-gap,shipwright" 2>/dev/null || echo "")
|
|
620
|
+
issue_url=$(gh issue create --title "$title" --body "$body" --label "$(_config_get "labels.harness_gap_labels" "harness-gap,shipwright")" 2>/dev/null || echo "")
|
|
660
621
|
if [[ -n "$issue_url" ]]; then
|
|
661
622
|
local issue_num
|
|
662
623
|
issue_num=$(echo "$issue_url" | sed -n 's|.*/issues/\([0-9]*\)|\1|p')
|
|
@@ -701,7 +662,7 @@ resolve_harness_gap() {
|
|
|
701
662
|
# Close the GitHub issue if it exists
|
|
702
663
|
local github_issue
|
|
703
664
|
github_issue=$(jq -r '.github_issue // empty' "$gap_file" 2>/dev/null)
|
|
704
|
-
if [[ -n "$github_issue" ]] && command -v gh
|
|
665
|
+
if [[ -n "$github_issue" ]] && command -v gh >/dev/null 2>&1; then
|
|
705
666
|
gh issue close "$github_issue" --comment "Harness gap resolved. Test case: \`${test_case_file:-none}\`" 2>/dev/null || true
|
|
706
667
|
fi
|
|
707
668
|
}
|
|
@@ -768,16 +729,16 @@ cmd_gap() {
|
|
|
768
729
|
|
|
769
730
|
while IFS= read -r gf; do
|
|
770
731
|
[[ -z "$gf" ]] && continue
|
|
771
|
-
((total
|
|
732
|
+
total=$((total + 1))
|
|
772
733
|
local status sla_deadline
|
|
773
734
|
status=$(jq -r '.status // "open"' "$gf" 2>/dev/null)
|
|
774
735
|
sla_deadline=$(jq -r '.sla_deadline_epoch // 0' "$gf" 2>/dev/null)
|
|
775
736
|
|
|
776
737
|
if [[ "$status" == "resolved" ]]; then
|
|
777
|
-
((resolved
|
|
778
|
-
((within_sla
|
|
738
|
+
resolved=$((resolved + 1))
|
|
739
|
+
within_sla=$((within_sla + 1))
|
|
779
740
|
elif [[ "$current_epoch" -gt "$sla_deadline" ]]; then
|
|
780
|
-
((overdue
|
|
741
|
+
overdue=$((overdue + 1))
|
|
781
742
|
fi
|
|
782
743
|
done <<< "$gap_files"
|
|
783
744
|
|
|
@@ -871,8 +832,21 @@ main() {
|
|
|
871
832
|
cmd_gap "$@"
|
|
872
833
|
;;
|
|
873
834
|
config)
|
|
874
|
-
|
|
875
|
-
|
|
835
|
+
local policy="${REPO_DIR}/config/policy.json"
|
|
836
|
+
if [[ ! -f "$policy" ]]; then
|
|
837
|
+
warn "No policy file found at ${policy}"
|
|
838
|
+
echo " Use: shipwright init to create one"
|
|
839
|
+
return 1
|
|
840
|
+
fi
|
|
841
|
+
echo -e "${BOLD}Incident & Harness Gap Configuration${RESET}"
|
|
842
|
+
echo ""
|
|
843
|
+
echo -e " Policy file: ${DIM}${policy}${RESET}"
|
|
844
|
+
echo -e " Harness gap enabled: $(jq -r '.harnessGapPolicy.enabled // false' "$policy" 2>/dev/null)"
|
|
845
|
+
echo -e " P0 SLA (hours): $(jq -r '.harnessGapPolicy.p0SlaHours // 24' "$policy" 2>/dev/null)"
|
|
846
|
+
echo -e " P1 SLA (hours): $(jq -r '.harnessGapPolicy.p1SlaHours // 72' "$policy" 2>/dev/null)"
|
|
847
|
+
echo -e " P2 SLA (hours): $(jq -r '.harnessGapPolicy.p2SlaHours // 168' "$policy" 2>/dev/null)"
|
|
848
|
+
echo -e " Auto-create gap issues: $(jq -r '.harnessGapPolicy.autoCreateGapIssue // true' "$policy" 2>/dev/null)"
|
|
849
|
+
echo -e " Require test before close: $(jq -r '.harnessGapPolicy.requireTestCaseBeforeClose // true' "$policy" 2>/dev/null)"
|
|
876
850
|
;;
|
|
877
851
|
help|--help|-h)
|
|
878
852
|
show_help
|
package/scripts/sw-init.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# ║ ║
|
|
9
9
|
# ║ --deploy Detect platform and generate deployed.json template ║
|
|
10
10
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
11
|
-
VERSION="
|
|
11
|
+
VERSION="3.0.0"
|
|
12
12
|
set -euo pipefail
|
|
13
13
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
14
14
|
trap 'rm -f "${tmp:-}"' EXIT
|
|
@@ -33,24 +33,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
33
33
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
34
34
|
now_epoch() { date +%s; }
|
|
35
35
|
fi
|
|
36
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
37
|
-
emit_event() {
|
|
38
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
39
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
40
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
41
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
42
|
-
}
|
|
43
|
-
fi
|
|
44
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
45
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
46
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
47
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
48
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
49
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
50
|
-
DIM="${DIM:-\033[2m}"
|
|
51
|
-
BOLD="${BOLD:-\033[1m}"
|
|
52
|
-
RESET="${RESET:-\033[0m}"
|
|
53
|
-
|
|
54
36
|
# ─── Flag parsing ───────────────────────────────────────────────────────────
|
|
55
37
|
DEPLOY_SETUP=false
|
|
56
38
|
DEPLOY_PLATFORM=""
|
|
@@ -111,7 +93,7 @@ if [[ "$REPAIR_MODE" == "true" ]]; then
|
|
|
111
93
|
# Strip legacy overlay source lines from user's tmux.conf
|
|
112
94
|
if [[ -f "$HOME/.tmux.conf" ]] && grep -q "claude-teams-overlay" "$HOME/.tmux.conf" 2>/dev/null; then
|
|
113
95
|
tmp=$(mktemp)
|
|
114
|
-
grep -v "claude-teams-overlay" "$HOME/.tmux.conf" > "$tmp" && mv "$tmp" "$HOME/.tmux.conf"
|
|
96
|
+
grep -v "claude-teams-overlay" "$HOME/.tmux.conf" > "$tmp" && mv "$tmp" "$HOME/.tmux.conf" || rm -f "$tmp"
|
|
115
97
|
success "Removed legacy claude-teams-overlay references from ~/.tmux.conf"
|
|
116
98
|
fi
|
|
117
99
|
fi
|
|
@@ -307,14 +289,14 @@ if [[ $_verify_fail -eq 0 ]]; then
|
|
|
307
289
|
fi
|
|
308
290
|
|
|
309
291
|
# ─── CLI Bootstrap (symlinks + PATH) ─────────────────────────────────────────
|
|
310
|
-
# Install sw/shipwright
|
|
292
|
+
# Install sw/shipwright symlinks so the CLI works from anywhere
|
|
311
293
|
BIN_DIR="$HOME/.local/bin"
|
|
312
294
|
mkdir -p "$BIN_DIR"
|
|
313
295
|
|
|
314
296
|
SW_SRC="$SCRIPT_DIR/sw"
|
|
315
297
|
if [[ -f "$SW_SRC" ]]; then
|
|
316
298
|
_cli_changed=false
|
|
317
|
-
for _cmd in sw shipwright
|
|
299
|
+
for _cmd in sw shipwright; do
|
|
318
300
|
_dest="$BIN_DIR/$_cmd"
|
|
319
301
|
if [[ -L "$_dest" ]] && [[ "$(readlink "$_dest")" == "$SW_SRC" ]]; then
|
|
320
302
|
continue
|
|
@@ -322,8 +304,13 @@ if [[ -f "$SW_SRC" ]]; then
|
|
|
322
304
|
ln -sf "$SW_SRC" "$_dest"
|
|
323
305
|
_cli_changed=true
|
|
324
306
|
done
|
|
307
|
+
# Clean up legacy cct symlink if present
|
|
308
|
+
if [[ -L "$BIN_DIR/cct" ]]; then
|
|
309
|
+
rm -f "$BIN_DIR/cct"
|
|
310
|
+
_cli_changed=true
|
|
311
|
+
fi
|
|
325
312
|
if [[ "$_cli_changed" == "true" ]]; then
|
|
326
|
-
success "CLI symlinks: sw, shipwright
|
|
313
|
+
success "CLI symlinks: sw, shipwright → $BIN_DIR"
|
|
327
314
|
else
|
|
328
315
|
success "CLI symlinks already correct"
|
|
329
316
|
fi
|
|
@@ -390,6 +377,13 @@ if [[ -d "$PIPELINES_SRC" ]]; then
|
|
|
390
377
|
success "Installed ${pip_count} pipeline templates → ~/.shipwright/pipelines/"
|
|
391
378
|
fi
|
|
392
379
|
|
|
380
|
+
# ─── Bootstrap optimization & memory (cold-start) ─────────────────────────────
|
|
381
|
+
if [[ -f "$SCRIPT_DIR/lib/bootstrap.sh" ]]; then
|
|
382
|
+
source "$SCRIPT_DIR/lib/bootstrap.sh"
|
|
383
|
+
bootstrap_optimization 2>/dev/null || true
|
|
384
|
+
bootstrap_memory 2>/dev/null || true
|
|
385
|
+
fi
|
|
386
|
+
|
|
393
387
|
# ─── Shell Completions ────────────────────────────────────────────────────────
|
|
394
388
|
# Detect shell type and install completions to the correct location
|
|
395
389
|
# Detect the user's login shell (not the script's running shell).
|
|
@@ -493,11 +487,11 @@ if [[ -f "$SETTINGS_FILE" ]]; then
|
|
|
493
487
|
success "Agent teams already enabled in settings.json"
|
|
494
488
|
else
|
|
495
489
|
# Try to add using jq
|
|
496
|
-
if jq -e '.env' "$SETTINGS_FILE"
|
|
490
|
+
if jq -e '.env' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
497
491
|
tmp=$(mktemp)
|
|
498
492
|
jq '.env["CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS"] = "1"' "$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
499
493
|
success "Enabled agent teams in existing settings.json"
|
|
500
|
-
elif jq -e '.' "$SETTINGS_FILE"
|
|
494
|
+
elif jq -e '.' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
501
495
|
tmp=$(mktemp)
|
|
502
496
|
jq '. + {"env": {"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"}}' "$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
503
497
|
success "Added agent teams env to settings.json"
|
|
@@ -552,17 +546,17 @@ fi
|
|
|
552
546
|
|
|
553
547
|
# ─── Wire Hooks into settings.json ──────────────────────────────────────────
|
|
554
548
|
# Ensure each installed hook has a matching event config in settings.json
|
|
555
|
-
if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE"
|
|
549
|
+
if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
556
550
|
hooks_wired=0
|
|
557
551
|
|
|
558
552
|
# Ensure .hooks object exists
|
|
559
|
-
if ! jq -e '.hooks' "$SETTINGS_FILE"
|
|
553
|
+
if ! jq -e '.hooks' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
560
554
|
tmp=$(mktemp)
|
|
561
555
|
jq '.hooks = {}' "$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
562
556
|
fi
|
|
563
557
|
|
|
564
558
|
# TeammateIdle
|
|
565
|
-
if [[ -f "$CLAUDE_DIR/hooks/teammate-idle.sh" ]] && ! jq -e '.hooks.TeammateIdle' "$SETTINGS_FILE"
|
|
559
|
+
if [[ -f "$CLAUDE_DIR/hooks/teammate-idle.sh" ]] && ! jq -e '.hooks.TeammateIdle' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
566
560
|
tmp=$(mktemp)
|
|
567
561
|
jq '.hooks.TeammateIdle = [{"hooks": [{"type": "command", "command": "~/.claude/hooks/teammate-idle.sh", "timeout": 30, "statusMessage": "Running typecheck before idle..."}]}]' \
|
|
568
562
|
"$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
@@ -570,7 +564,7 @@ if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE" &>/dev/null; then
|
|
|
570
564
|
fi
|
|
571
565
|
|
|
572
566
|
# TaskCompleted
|
|
573
|
-
if [[ -f "$CLAUDE_DIR/hooks/task-completed.sh" ]] && ! jq -e '.hooks.TaskCompleted' "$SETTINGS_FILE"
|
|
567
|
+
if [[ -f "$CLAUDE_DIR/hooks/task-completed.sh" ]] && ! jq -e '.hooks.TaskCompleted' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
574
568
|
tmp=$(mktemp)
|
|
575
569
|
jq '.hooks.TaskCompleted = [{"hooks": [{"type": "command", "command": "~/.claude/hooks/task-completed.sh", "timeout": 60, "statusMessage": "Running quality checks..."}]}]' \
|
|
576
570
|
"$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
@@ -578,7 +572,7 @@ if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE" &>/dev/null; then
|
|
|
578
572
|
fi
|
|
579
573
|
|
|
580
574
|
# Notification
|
|
581
|
-
if [[ -f "$CLAUDE_DIR/hooks/notify-idle.sh" ]] && ! jq -e '.hooks.Notification' "$SETTINGS_FILE"
|
|
575
|
+
if [[ -f "$CLAUDE_DIR/hooks/notify-idle.sh" ]] && ! jq -e '.hooks.Notification' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
582
576
|
tmp=$(mktemp)
|
|
583
577
|
jq '.hooks.Notification = [{"hooks": [{"type": "command", "command": "~/.claude/hooks/notify-idle.sh", "async": true}]}]' \
|
|
584
578
|
"$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
@@ -586,7 +580,7 @@ if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE" &>/dev/null; then
|
|
|
586
580
|
fi
|
|
587
581
|
|
|
588
582
|
# PreCompact
|
|
589
|
-
if [[ -f "$CLAUDE_DIR/hooks/pre-compact-save.sh" ]] && ! jq -e '.hooks.PreCompact' "$SETTINGS_FILE"
|
|
583
|
+
if [[ -f "$CLAUDE_DIR/hooks/pre-compact-save.sh" ]] && ! jq -e '.hooks.PreCompact' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
590
584
|
tmp=$(mktemp)
|
|
591
585
|
jq '.hooks.PreCompact = [{"matcher": "auto", "hooks": [{"type": "command", "command": "~/.claude/hooks/pre-compact-save.sh", "statusMessage": "Saving context before compaction..."}]}]' \
|
|
592
586
|
"$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
@@ -594,7 +588,7 @@ if [[ -f "$SETTINGS_FILE" ]] && jq -e '.' "$SETTINGS_FILE" &>/dev/null; then
|
|
|
594
588
|
fi
|
|
595
589
|
|
|
596
590
|
# SessionStart
|
|
597
|
-
if [[ -f "$CLAUDE_DIR/hooks/session-start.sh" ]] && ! jq -e '.hooks.SessionStart' "$SETTINGS_FILE"
|
|
591
|
+
if [[ -f "$CLAUDE_DIR/hooks/session-start.sh" ]] && ! jq -e '.hooks.SessionStart' "$SETTINGS_FILE" >/dev/null 2>&1; then
|
|
598
592
|
tmp=$(mktemp)
|
|
599
593
|
jq '.hooks.SessionStart = [{"hooks": [{"type": "command", "command": "~/.claude/hooks/session-start.sh", "timeout": 5}]}]' \
|
|
600
594
|
"$SETTINGS_FILE" > "$tmp" && mv "$tmp" "$SETTINGS_FILE"
|
|
@@ -644,8 +638,8 @@ fi
|
|
|
644
638
|
|
|
645
639
|
# ─── GitHub CLI Authentication ────────────────────────────────────────────────
|
|
646
640
|
# gh auth is required for daemon, pipeline, PR creation, and issue management
|
|
647
|
-
if command -v gh
|
|
648
|
-
if gh auth status
|
|
641
|
+
if command -v gh >/dev/null 2>&1; then
|
|
642
|
+
if gh auth status >/dev/null 2>&1; then
|
|
649
643
|
success "GitHub CLI authenticated"
|
|
650
644
|
else
|
|
651
645
|
warn "GitHub CLI installed but not authenticated"
|
|
@@ -669,13 +663,13 @@ if [[ -n "${TMUX:-}" ]]; then
|
|
|
669
663
|
fi
|
|
670
664
|
|
|
671
665
|
# ─── Bun (required for dashboard) ──────────────────────────────────────────
|
|
672
|
-
if command -v bun
|
|
666
|
+
if command -v bun >/dev/null 2>&1 || [[ -x "$HOME/.bun/bin/bun" ]]; then
|
|
673
667
|
_bun_cmd="bun"
|
|
674
668
|
[[ -x "$HOME/.bun/bin/bun" ]] && _bun_cmd="$HOME/.bun/bin/bun"
|
|
675
669
|
success "Bun $($_bun_cmd --version 2>/dev/null || echo "installed") — dashboard ready"
|
|
676
670
|
else
|
|
677
671
|
info "Installing Bun (required for ${BOLD}shipwright dashboard${RESET})..."
|
|
678
|
-
if curl -fsSL https://bun.sh/install | bash 2>/dev/null; then
|
|
672
|
+
if curl -fsSL --connect-timeout 10 --max-time 120 https://bun.sh/install | bash 2>/dev/null; then
|
|
679
673
|
export PATH="$HOME/.bun/bin:$PATH"
|
|
680
674
|
success "Bun installed — dashboard ready"
|
|
681
675
|
else
|
|
@@ -781,7 +775,11 @@ else
|
|
|
781
775
|
fi
|
|
782
776
|
|
|
783
777
|
# Confirm with user
|
|
784
|
-
|
|
778
|
+
if [[ -t 0 ]]; then
|
|
779
|
+
read -rp "$(echo -e "${CYAN}${BOLD}▸${RESET} Configure deploy for ${BOLD}${DEPLOY_PLATFORM}${RESET}? [Y/n] ")" confirm
|
|
780
|
+
else
|
|
781
|
+
confirm="y"
|
|
782
|
+
fi
|
|
785
783
|
if [[ "$(echo "$confirm" | tr '[:upper:]' '[:lower:]')" == "n" ]]; then
|
|
786
784
|
info "Aborted. Use --platform to specify manually."
|
|
787
785
|
exit 0
|
package/scripts/sw-instrument.sh
CHANGED
|
@@ -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="
|
|
9
|
+
VERSION="3.0.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
12
|
|
|
@@ -34,16 +34,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
34
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
35
|
}
|
|
36
36
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
37
|
format_duration() {
|
|
48
38
|
local secs="$1"
|
|
49
39
|
if [[ "$secs" -ge 3600 ]]; then
|