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-tracker.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
|
|
|
@@ -17,6 +17,7 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
17
17
|
# Canonical helpers (colors, output, events)
|
|
18
18
|
# shellcheck source=lib/helpers.sh
|
|
19
19
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
20
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
20
21
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
21
22
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
22
23
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -26,24 +27,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
26
27
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
27
28
|
now_epoch() { date +%s; }
|
|
28
29
|
fi
|
|
29
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
30
|
-
emit_event() {
|
|
31
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
32
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
33
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
34
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
|
-
}
|
|
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
30
|
# ─── Configuration ─────────────────────────────────────────────────────────
|
|
48
31
|
CONFIG_DIR="${HOME}/.shipwright"
|
|
49
32
|
TRACKER_CONFIG="${CONFIG_DIR}/tracker-config.json"
|
|
@@ -127,7 +110,7 @@ _dispatch_provider() {
|
|
|
127
110
|
local provider_func="provider_${func}"
|
|
128
111
|
|
|
129
112
|
# Provider scripts define provider_* functions
|
|
130
|
-
if type "$provider_func"
|
|
113
|
+
if type "$provider_func" >/dev/null 2>&1; then
|
|
131
114
|
"$provider_func" "$@"
|
|
132
115
|
return $?
|
|
133
116
|
else
|
|
@@ -137,7 +120,7 @@ _dispatch_provider() {
|
|
|
137
120
|
# Try GitHub provider
|
|
138
121
|
if [[ -f "$SCRIPT_DIR/sw-tracker-github.sh" ]]; then
|
|
139
122
|
source "$SCRIPT_DIR/sw-tracker-github.sh"
|
|
140
|
-
if type "$provider_func"
|
|
123
|
+
if type "$provider_func" >/dev/null 2>&1; then
|
|
141
124
|
"$provider_func" "$@"
|
|
142
125
|
return $?
|
|
143
126
|
fi
|
|
@@ -221,7 +204,7 @@ tracker_notify() {
|
|
|
221
204
|
fi
|
|
222
205
|
|
|
223
206
|
# Provider scripts define provider_notify()
|
|
224
|
-
if type provider_notify
|
|
207
|
+
if type provider_notify >/dev/null 2>&1; then
|
|
225
208
|
provider_notify "$event" "$gh_issue" "$detail"
|
|
226
209
|
else
|
|
227
210
|
warn "Provider '$TRACKER_PROVIDER' loaded but provider_notify() not defined"
|
|
@@ -330,7 +313,7 @@ _init_linear() {
|
|
|
330
313
|
local payload
|
|
331
314
|
payload=$(jq -n --arg q 'query { viewer { id name } }' '{query: $q}')
|
|
332
315
|
local response
|
|
333
|
-
response=$(curl -sf -X POST "https://api.linear.app/graphql" \
|
|
316
|
+
response=$(curl -sf --connect-timeout "$(_config_get_int "network.connect_timeout" 10)" --max-time "$(_config_get_int "network.max_time" 30)" -X POST "$(_config_get "urls.linear_api" "https://api.linear.app/graphql")" \
|
|
334
317
|
-H "Authorization: $api_key" \
|
|
335
318
|
-H "Content-Type: application/json" \
|
|
336
319
|
-d "$payload" 2>&1) || {
|
|
@@ -405,7 +388,7 @@ _init_jira() {
|
|
|
405
388
|
local auth
|
|
406
389
|
auth=$(printf '%s:%s' "$email" "$api_token" | base64)
|
|
407
390
|
local response
|
|
408
|
-
response=$(curl -sf -X GET "${base_url}/rest/api/3/myself" \
|
|
391
|
+
response=$(curl -sf --connect-timeout "$(_config_get_int "network.connect_timeout" 10)" --max-time "$(_config_get_int "network.max_time" 30)" -X GET "${base_url}/rest/api/3/myself" \
|
|
409
392
|
-H "Authorization: Basic $auth" \
|
|
410
393
|
-H "Content-Type: application/json" 2>&1) || {
|
|
411
394
|
warn "Could not validate connection — check your credentials"
|
package/scripts/sw-triage.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,7 +16,15 @@ 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
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
19
20
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
21
|
+
[[ -z "${CYAN:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && CYAN='\033[38;2;0;212;255m' || CYAN=''; } || true
|
|
22
|
+
[[ -z "${RESET:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && RESET='\033[0m' || RESET=''; } || true
|
|
23
|
+
[[ -z "${BOLD:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && BOLD='\033[1m' || BOLD=''; } || true
|
|
24
|
+
[[ -z "${DIM:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && DIM='\033[2m' || DIM=''; } || true
|
|
25
|
+
[[ -z "${GREEN:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && GREEN='\033[38;2;74;222;128m' || GREEN=''; } || true
|
|
26
|
+
[[ -z "${RED:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && RED='\033[38;2;248;113;113m' || RED=''; } || true
|
|
27
|
+
[[ -z "${YELLOW:-}" ]] && { [[ -z "${NO_COLOR:-}" ]] && YELLOW='\033[38;2;250;204;21m' || YELLOW=''; } || true
|
|
20
28
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
21
29
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
22
30
|
[[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
|
|
@@ -25,24 +33,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
25
33
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
34
|
now_epoch() { date +%s; }
|
|
27
35
|
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
36
|
# ─── GitHub API (safe when NO_GITHUB set) ──────────────────────────────────
|
|
47
37
|
|
|
48
38
|
check_gh() {
|
|
@@ -50,7 +40,7 @@ check_gh() {
|
|
|
50
40
|
error "GitHub access disabled (NO_GITHUB=1)"
|
|
51
41
|
exit 1
|
|
52
42
|
fi
|
|
53
|
-
if ! command -v gh
|
|
43
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
54
44
|
error "gh CLI not found. Install: https://cli.github.com"
|
|
55
45
|
exit 1
|
|
56
46
|
fi
|
|
@@ -178,7 +168,7 @@ analyze_with_ai() {
|
|
|
178
168
|
if [[ ! -f "${SCRIPT_DIR}/sw-intelligence.sh" ]]; then
|
|
179
169
|
return 1
|
|
180
170
|
fi
|
|
181
|
-
if ! command -v claude
|
|
171
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
182
172
|
return 1
|
|
183
173
|
fi
|
|
184
174
|
|
|
@@ -210,7 +200,7 @@ Return JSON with exactly these fields:
|
|
|
210
200
|
local result
|
|
211
201
|
result=$(_intelligence_call_claude "$prompt" "$cache_key" 2>/dev/null) || true
|
|
212
202
|
|
|
213
|
-
if [[ -z "$result" ]] || echo "$result" | jq -e '.'
|
|
203
|
+
if [[ -z "$result" ]] || echo "$result" | jq -e '.' >/dev/null 2>&1; then
|
|
214
204
|
: # result is empty or valid JSON
|
|
215
205
|
else
|
|
216
206
|
return 1
|
|
@@ -225,7 +215,7 @@ Return JSON with exactly these fields:
|
|
|
225
215
|
labels_val=$(echo "$result" | jq -r '.labels // []' 2>/dev/null)
|
|
226
216
|
|
|
227
217
|
# Reject if we got an error object
|
|
228
|
-
if echo "$result" | jq -e '.error'
|
|
218
|
+
if echo "$result" | jq -e '.error' >/dev/null 2>&1; then
|
|
229
219
|
return 1
|
|
230
220
|
fi
|
|
231
221
|
|
|
@@ -445,8 +435,8 @@ cmd_label() {
|
|
|
445
435
|
labels_str=$(echo "$analysis" | jq -r '.suggested_labels | join(" ")')
|
|
446
436
|
|
|
447
437
|
# Apply labels via gh CLI
|
|
448
|
-
local label_array
|
|
449
|
-
|
|
438
|
+
local label_array=()
|
|
439
|
+
while IFS= read -r _l; do [[ -n "$_l" ]] && label_array+=("$_l"); done <<< "$(echo "$labels_str" | tr ' ' '\n')"
|
|
450
440
|
|
|
451
441
|
for label in "${label_array[@]}"; do
|
|
452
442
|
[[ -z "$label" ]] && continue
|
|
@@ -469,7 +459,7 @@ cmd_prioritize() {
|
|
|
469
459
|
|
|
470
460
|
# Fetch all open issues
|
|
471
461
|
local issues_json
|
|
472
|
-
issues_json=$(gh issue list --state open --json number,title,body,labels,createdAt,reactions --limit 100 2>/dev/null || echo "[]")
|
|
462
|
+
issues_json=$(gh issue list --state open --json number,title,body,labels,createdAt,reactions --limit "$(_config_get_int "limits.triage_issues" 100)" 2>/dev/null || echo "[]")
|
|
473
463
|
|
|
474
464
|
local issue_count
|
|
475
465
|
issue_count=$(echo "$issues_json" | jq 'length')
|
|
@@ -540,13 +530,13 @@ cmd_prioritize() {
|
|
|
540
530
|
echo -e "${BOLD}Prioritized Backlog${RESET}"
|
|
541
531
|
echo "─────────────────────────────────────────────────────────────────"
|
|
542
532
|
echo ""
|
|
543
|
-
echo "$output_json" | jq -r '.[] | "
|
|
533
|
+
echo "$output_json" | jq -r '.[] | "#\(.number) \(.score): \(.title) [type:\(.type) complexity:\(.complexity) risk:\(.risk)]"' | while IFS= read -r line; do
|
|
544
534
|
local number score rest
|
|
545
|
-
number=$(echo "$line" |
|
|
546
|
-
score=$(echo "$line" | cut -d
|
|
535
|
+
number=$(echo "$line" | cut -d' ' -f1)
|
|
536
|
+
score=$(echo "$line" | cut -d' ' -f2 | tr -d ':')
|
|
547
537
|
rest=$(echo "$line" | cut -d' ' -f3-)
|
|
548
538
|
|
|
549
|
-
echo -e " ${CYAN}
|
|
539
|
+
echo -e " ${CYAN}${number}${RESET} ${BOLD}${score}${RESET} ${rest}"
|
|
550
540
|
done
|
|
551
541
|
|
|
552
542
|
echo ""
|
|
@@ -565,7 +555,7 @@ cmd_team() {
|
|
|
565
555
|
|
|
566
556
|
# Determine if GitHub is available (don't exit — allow offline fallback)
|
|
567
557
|
local gh_available=false
|
|
568
|
-
if [[ "${NO_GITHUB:-}" != "1" ]] && command -v gh
|
|
558
|
+
if [[ "${NO_GITHUB:-}" != "1" ]] && command -v gh >/dev/null 2>&1; then
|
|
569
559
|
gh_available=true
|
|
570
560
|
fi
|
|
571
561
|
|
|
@@ -595,7 +585,7 @@ cmd_team() {
|
|
|
595
585
|
|
|
596
586
|
local recruit_result
|
|
597
587
|
recruit_result=$(bash "$SCRIPT_DIR/sw-recruit.sh" team --json "$issue_title" 2>/dev/null) || true
|
|
598
|
-
if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team'
|
|
588
|
+
if [[ -n "$recruit_result" ]] && echo "$recruit_result" | jq -e '.team' >/dev/null 2>&1; then
|
|
599
589
|
model=$(echo "$recruit_result" | jq -r '.model // "sonnet"')
|
|
600
590
|
agents=$(echo "$recruit_result" | jq -r '.agents // 2')
|
|
601
591
|
template=$(echo "$recruit_result" | jq -r '.template // ""')
|
|
@@ -675,7 +665,7 @@ cmd_batch() {
|
|
|
675
665
|
|
|
676
666
|
# Fetch unlabeled open issues
|
|
677
667
|
local issues_json
|
|
678
|
-
issues_json=$(gh issue list --state open --search "no:label" --json number --limit 50 2>/dev/null || echo "[]")
|
|
668
|
+
issues_json=$(gh issue list --state open --search "no:label" --json number --limit "$(_config_get_int "limits.triage_unlabeled" 50)" 2>/dev/null || echo "[]")
|
|
679
669
|
|
|
680
670
|
local issue_count
|
|
681
671
|
issue_count=$(echo "$issues_json" | jq 'length')
|
|
@@ -701,7 +691,7 @@ cmd_report() {
|
|
|
701
691
|
|
|
702
692
|
# Fetch labeled issues
|
|
703
693
|
local issues_json
|
|
704
|
-
issues_json=$(gh issue list --state open --json labels,title,number --limit 100 2>/dev/null || echo "[]")
|
|
694
|
+
issues_json=$(gh issue list --state open --json labels,title,number --limit "$(_config_get_int "limits.triage_issues" 100)" 2>/dev/null || echo "[]")
|
|
705
695
|
|
|
706
696
|
local type_counts complexity_counts priority_counts
|
|
707
697
|
type_counts='{}'
|
package/scripts/sw-upgrade.sh
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
3
|
# ║ sw upgrade — Detect and apply updates from the repo ║
|
|
4
4
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
5
|
-
VERSION="
|
|
5
|
+
VERSION="3.0.0"
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
8
|
|
|
@@ -25,24 +25,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
25
25
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
26
|
now_epoch() { date +%s; }
|
|
27
27
|
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
28
|
# ─── Parse flags ───────────────────────────────────────────────────────────
|
|
47
29
|
APPLY=false
|
|
48
30
|
REPO_OVERRIDE=""
|
|
@@ -70,8 +52,8 @@ find_repo() {
|
|
|
70
52
|
fi
|
|
71
53
|
|
|
72
54
|
# 2. Environment variable
|
|
73
|
-
if [[ -n "${
|
|
74
|
-
echo "$
|
|
55
|
+
if [[ -n "${SHIPWRIGHT_REPO_PATH:-}" ]]; then
|
|
56
|
+
echo "$SHIPWRIGHT_REPO_PATH"
|
|
75
57
|
return
|
|
76
58
|
fi
|
|
77
59
|
|
|
@@ -102,7 +84,7 @@ REPO_PATH="$(find_repo)" || {
|
|
|
102
84
|
error "Cannot locate the Shipwright repo."
|
|
103
85
|
echo ""
|
|
104
86
|
echo -e " Try one of:"
|
|
105
|
-
echo -e " ${DIM}export
|
|
87
|
+
echo -e " ${DIM}export SHIPWRIGHT_REPO_PATH=/path/to/shipwright${RESET}"
|
|
106
88
|
echo -e " ${DIM}shipwright upgrade --repo-path /path/to/shipwright${RESET}"
|
|
107
89
|
exit 1
|
|
108
90
|
}
|
|
@@ -277,7 +259,7 @@ bootstrap_manifest() {
|
|
|
277
259
|
IFS='|' read -r key _ dest _ _ <<< "$entry"
|
|
278
260
|
if [[ -f "$dest" ]]; then
|
|
279
261
|
echo -e " ${GREEN}✓${RESET} ${DIM}$key${RESET} → $dest"
|
|
280
|
-
((found
|
|
262
|
+
found=$((found + 1))
|
|
281
263
|
fi
|
|
282
264
|
done
|
|
283
265
|
|
package/scripts/sw-ux.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
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -26,24 +26,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
26
26
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
27
27
|
now_epoch() { date +%s; }
|
|
28
28
|
fi
|
|
29
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
30
|
-
emit_event() {
|
|
31
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
32
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
33
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
34
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
|
-
}
|
|
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
29
|
# ─── Structured Event Log ──────────────────────────────────────────────────
|
|
48
30
|
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
49
31
|
|
package/scripts/sw-webhook.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
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -16,6 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && 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
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
19
20
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
20
21
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
21
22
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -25,29 +26,11 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
25
26
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
26
27
|
now_epoch() { date +%s; }
|
|
27
28
|
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
29
|
# ─── Constants ──────────────────────────────────────────────────────────────
|
|
47
30
|
SHIPWRIGHT_DIR="$HOME/.shipwright"
|
|
48
31
|
WEBHOOK_SECRET_FILE="$SHIPWRIGHT_DIR/webhook-secret"
|
|
49
32
|
WEBHOOK_EVENTS_FILE="$SHIPWRIGHT_DIR/webhook-events.jsonl"
|
|
50
|
-
WEBHOOK_PORT="${WEBHOOK_PORT
|
|
33
|
+
WEBHOOK_PORT="${WEBHOOK_PORT:-$(_config_get_int "webhook.port" 8765 2>/dev/null || echo 8765)}"
|
|
51
34
|
WEBHOOK_PID_FILE="$SHIPWRIGHT_DIR/webhook.pid"
|
|
52
35
|
WEBHOOK_LOG="$SHIPWRIGHT_DIR/webhook.log"
|
|
53
36
|
|
|
@@ -141,7 +124,7 @@ process_webhook_event() {
|
|
|
141
124
|
|
|
142
125
|
# Check if nc (netcat) is available
|
|
143
126
|
check_nc() {
|
|
144
|
-
if ! command -v nc
|
|
127
|
+
if ! command -v nc >/dev/null 2>&1; then
|
|
145
128
|
error "netcat (nc) is required but not installed"
|
|
146
129
|
echo -e " ${DIM}brew install netcat${RESET} (macOS)"
|
|
147
130
|
echo -e " ${DIM}sudo apt install netcat-openbsd${RESET} (Ubuntu/Debian)"
|
|
@@ -254,17 +237,20 @@ webhook_server_bash() {
|
|
|
254
237
|
local method path protocol
|
|
255
238
|
read -r method path protocol <<< "$request_line"
|
|
256
239
|
|
|
257
|
-
# Read headers until blank line
|
|
258
|
-
local -A headers
|
|
240
|
+
# Read headers until blank line (Bash 3.2 compatible — no associative arrays)
|
|
259
241
|
local header_line content_length=0
|
|
242
|
+
local hdr_signature="" hdr_event_type=""
|
|
260
243
|
while read -r -u 3 -t 0.1 header_line; do
|
|
261
244
|
[[ -z "$header_line" || "$header_line" == $'\r' ]] && break
|
|
262
245
|
local key="${header_line%%:*}"
|
|
263
246
|
local value="${header_line#*:}"
|
|
264
247
|
value="${value#[[:space:]]}"
|
|
265
248
|
value="${value%$'\r'}"
|
|
266
|
-
|
|
267
|
-
|
|
249
|
+
local key_lower
|
|
250
|
+
key_lower=$(printf '%s' "$key" | tr '[:upper:]' '[:lower:]')
|
|
251
|
+
[[ "$key_lower" == "content-length" ]] && content_length="$value"
|
|
252
|
+
[[ "$key_lower" == "x-hub-signature-256" ]] && hdr_signature="$value"
|
|
253
|
+
[[ "$key_lower" == "x-github-event" ]] && hdr_event_type="$value"
|
|
268
254
|
done 2>/dev/null || true
|
|
269
255
|
|
|
270
256
|
# Read body if content-length > 0
|
|
@@ -275,8 +261,8 @@ webhook_server_bash() {
|
|
|
275
261
|
|
|
276
262
|
# Process webhook if method is POST
|
|
277
263
|
if [[ "$method" == "POST" && "$path" == "/webhook" ]]; then
|
|
278
|
-
local signature="$
|
|
279
|
-
local event_type="$
|
|
264
|
+
local signature="$hdr_signature"
|
|
265
|
+
local event_type="$hdr_event_type"
|
|
280
266
|
|
|
281
267
|
if validate_webhook_signature "$body" "$signature"; then
|
|
282
268
|
if process_webhook_event "$body" "$event_type"; then
|
|
@@ -331,7 +317,7 @@ cmd_setup() {
|
|
|
331
317
|
info "Webhook endpoint: http://localhost:${WEBHOOK_PORT}/webhook"
|
|
332
318
|
|
|
333
319
|
# Check if gh CLI is available
|
|
334
|
-
if ! command -v gh
|
|
320
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
335
321
|
error "GitHub CLI (gh) is required but not installed"
|
|
336
322
|
return 1
|
|
337
323
|
fi
|
|
@@ -345,7 +331,7 @@ cmd_setup() {
|
|
|
345
331
|
-f "url=http://localhost:${WEBHOOK_PORT}/webhook" \
|
|
346
332
|
-F "events=issues" \
|
|
347
333
|
-f "config[content_type]=json" \
|
|
348
|
-
-f "config[secret]=${secret}" 2>&1); then
|
|
334
|
+
-f "config[secret]=${secret}" --timeout 30 2>&1); then
|
|
349
335
|
|
|
350
336
|
local hook_id
|
|
351
337
|
hook_id=$(echo "$webhook_response" | jq -r '.id // empty' 2>/dev/null || true)
|
|
@@ -403,7 +389,7 @@ cmd_test() {
|
|
|
403
389
|
return 1
|
|
404
390
|
fi
|
|
405
391
|
|
|
406
|
-
if ! command -v gh
|
|
392
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
407
393
|
error "GitHub CLI (gh) is required"
|
|
408
394
|
return 1
|
|
409
395
|
fi
|
|
@@ -440,7 +426,7 @@ cmd_test() {
|
|
|
440
426
|
if gh api "repos/${org_repo}/hooks/tests" \
|
|
441
427
|
-H "Accept: application/vnd.github+json" \
|
|
442
428
|
-X POST \
|
|
443
|
-
2>&1 | grep -q "Test hook sent"; then
|
|
429
|
+
--timeout 30 2>&1 | grep -q "Test hook sent"; then
|
|
444
430
|
success "Test ping sent to GitHub"
|
|
445
431
|
else
|
|
446
432
|
warn "Could not send test via GitHub API, but payload is valid:"
|
|
@@ -534,7 +520,7 @@ cmd_secret() {
|
|
|
534
520
|
echo "$new_secret" > "$WEBHOOK_SECRET_FILE"
|
|
535
521
|
chmod 600 "$WEBHOOK_SECRET_FILE"
|
|
536
522
|
success "Webhook secret regenerated"
|
|
537
|
-
info "
|
|
523
|
+
info "Secret: ${new_secret:0:8}... (full value in ${WEBHOOK_SECRET_FILE})"
|
|
538
524
|
;;
|
|
539
525
|
*)
|
|
540
526
|
error "Unknown secret action: $action"
|
package/scripts/sw-widgets.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
11
|
-
VERSION="
|
|
11
|
+
VERSION="3.0.0"
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
13
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
14
14
|
|
|
@@ -29,24 +29,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
29
29
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
30
30
|
now_epoch() { date +%s; }
|
|
31
31
|
fi
|
|
32
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
33
|
-
emit_event() {
|
|
34
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
35
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
36
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
37
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
38
|
-
}
|
|
39
|
-
fi
|
|
40
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
41
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
42
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
43
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
44
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
45
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
46
|
-
DIM="${DIM:-\033[2m}"
|
|
47
|
-
BOLD="${BOLD:-\033[1m}"
|
|
48
|
-
RESET="${RESET:-\033[0m}"
|
|
49
|
-
|
|
50
32
|
# ─── Configuration ─────────────────────────────────────────────────────────
|
|
51
33
|
CONFIG_DIR="${HOME}/.shipwright"
|
|
52
34
|
CONFIG_FILE="${CONFIG_DIR}/widgets-config.json"
|
|
@@ -301,8 +283,8 @@ cmd_slack() {
|
|
|
301
283
|
)
|
|
302
284
|
|
|
303
285
|
# Send to webhook
|
|
304
|
-
if command -v curl
|
|
305
|
-
response=$(curl -s -X POST "$webhook_url" \
|
|
286
|
+
if command -v curl >/dev/null 2>&1; then
|
|
287
|
+
response=$(curl -s --connect-timeout 10 --max-time 30 -X POST "$webhook_url" \
|
|
306
288
|
-H 'Content-Type: application/json' \
|
|
307
289
|
-d "$message_json" 2>&1)
|
|
308
290
|
|
package/scripts/sw-worktree.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ║ Each agent gets its own worktree so parallel agents don't clobber ║
|
|
6
6
|
# ║ each other's files. Worktrees live in .worktrees/ relative to root. ║
|
|
7
7
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
8
|
-
VERSION="
|
|
8
|
+
VERSION="3.0.0"
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
11
11
|
|
|
@@ -23,24 +23,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
23
23
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
24
24
|
now_epoch() { date +%s; }
|
|
25
25
|
fi
|
|
26
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
27
|
-
emit_event() {
|
|
28
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
29
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
30
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
31
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
32
|
-
}
|
|
33
|
-
fi
|
|
34
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
35
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
36
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
37
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
38
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
39
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
40
|
-
DIM="${DIM:-\033[2m}"
|
|
41
|
-
BOLD="${BOLD:-\033[1m}"
|
|
42
|
-
RESET="${RESET:-\033[0m}"
|
|
43
|
-
|
|
44
26
|
# ─── Repo root ─────────────────────────────────────────────────────────────
|
|
45
27
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
|
46
28
|
error "Not inside a git repository."
|
|
@@ -122,7 +104,7 @@ worktree_list() {
|
|
|
122
104
|
|
|
123
105
|
printf " ${CYAN}%-16s${RESET} ${PURPLE}%-22s${RESET} %b ${DIM}.worktrees/%s/${RESET}\n" \
|
|
124
106
|
"$name" "$branch" "$status_str" "$name"
|
|
125
|
-
((found
|
|
107
|
+
found=$((found + 1))
|
|
126
108
|
done
|
|
127
109
|
|
|
128
110
|
if [[ $found -eq 0 ]]; then
|
|
@@ -167,8 +149,8 @@ worktree_sync_all() {
|
|
|
167
149
|
[[ -d "$dir" ]] || continue
|
|
168
150
|
local name
|
|
169
151
|
name="$(basename "$dir")"
|
|
170
|
-
worktree_sync "$name" || ((failed
|
|
171
|
-
((count
|
|
152
|
+
worktree_sync "$name" || failed=$((failed + 1))
|
|
153
|
+
count=$((count + 1))
|
|
172
154
|
done
|
|
173
155
|
|
|
174
156
|
echo ""
|
|
@@ -185,7 +167,7 @@ worktree_merge() {
|
|
|
185
167
|
local current_branch
|
|
186
168
|
current_branch="$(git branch --show-current)"
|
|
187
169
|
|
|
188
|
-
if ! git rev-parse --verify "$branch"
|
|
170
|
+
if ! git rev-parse --verify "$branch" >/dev/null 2>&1; then
|
|
189
171
|
error "Branch '$branch' does not exist."
|
|
190
172
|
return 1
|
|
191
173
|
fi
|
|
@@ -219,7 +201,7 @@ worktree_merge_all() {
|
|
|
219
201
|
echo -e " ${DIM}Resolve the conflict, then re-run: shipwright worktree merge-all${RESET}"
|
|
220
202
|
return 1
|
|
221
203
|
}
|
|
222
|
-
((count
|
|
204
|
+
count=$((count + 1))
|
|
223
205
|
done
|
|
224
206
|
|
|
225
207
|
echo ""
|
|
@@ -234,7 +216,9 @@ worktree_remove() {
|
|
|
234
216
|
if [[ -d "$worktree_path" ]]; then
|
|
235
217
|
git worktree remove "$worktree_path" --force 2>/dev/null || {
|
|
236
218
|
warn "Could not cleanly remove worktree $name, forcing..."
|
|
237
|
-
|
|
219
|
+
if [[ -n "$worktree_path" && "$worktree_path" == "$WORKTREE_DIR/"* ]]; then
|
|
220
|
+
rm -rf "$worktree_path"
|
|
221
|
+
fi
|
|
238
222
|
git worktree prune 2>/dev/null || true
|
|
239
223
|
}
|
|
240
224
|
fi
|
|
@@ -257,7 +241,7 @@ worktree_cleanup() {
|
|
|
257
241
|
local name
|
|
258
242
|
name="$(basename "$dir")"
|
|
259
243
|
worktree_remove "$name"
|
|
260
|
-
((count
|
|
244
|
+
count=$((count + 1))
|
|
261
245
|
done
|
|
262
246
|
|
|
263
247
|
# Prune stale worktree references
|
|
@@ -302,7 +286,7 @@ worktree_status() {
|
|
|
302
286
|
|
|
303
287
|
printf " ${CYAN}%-16s${RESET} ${PURPLE}%-22s${RESET} ${GREEN}%s ahead${RESET}, ${YELLOW}%s behind${RESET}%b\n" \
|
|
304
288
|
"$name" "$branch" "$ahead" "$behind" "$dirty"
|
|
305
|
-
((found
|
|
289
|
+
found=$((found + 1))
|
|
306
290
|
done
|
|
307
291
|
|
|
308
292
|
if [[ $found -eq 0 ]]; then
|