shipwright-cli 1.7.1 → 1.9.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/.claude/agents/code-reviewer.md +90 -0
- package/.claude/agents/devops-engineer.md +142 -0
- package/.claude/agents/pipeline-agent.md +80 -0
- package/.claude/agents/shell-script-specialist.md +150 -0
- package/.claude/agents/test-specialist.md +196 -0
- package/.claude/hooks/post-tool-use.sh +38 -0
- package/.claude/hooks/pre-tool-use.sh +25 -0
- package/.claude/hooks/session-started.sh +37 -0
- package/README.md +212 -814
- package/claude-code/CLAUDE.md.shipwright +54 -0
- package/claude-code/hooks/notify-idle.sh +2 -2
- package/claude-code/hooks/session-start.sh +24 -0
- package/claude-code/hooks/task-completed.sh +6 -2
- package/claude-code/settings.json.template +12 -0
- package/dashboard/public/app.js +4422 -0
- package/dashboard/public/index.html +816 -0
- package/dashboard/public/styles.css +4755 -0
- package/dashboard/server.ts +4315 -0
- package/docs/KNOWN-ISSUES.md +18 -10
- package/docs/TIPS.md +38 -26
- package/docs/patterns/README.md +33 -23
- package/package.json +9 -5
- package/scripts/adapters/iterm2-adapter.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +52 -23
- package/scripts/adapters/wezterm-adapter.sh +26 -14
- package/scripts/lib/compat.sh +200 -0
- package/scripts/lib/helpers.sh +72 -0
- package/scripts/postinstall.mjs +72 -13
- package/scripts/{cct → sw} +109 -21
- package/scripts/sw-adversarial.sh +274 -0
- package/scripts/sw-architecture-enforcer.sh +330 -0
- package/scripts/sw-checkpoint.sh +390 -0
- package/scripts/{cct-cleanup.sh → sw-cleanup.sh} +3 -1
- package/scripts/sw-connect.sh +619 -0
- package/scripts/{cct-cost.sh → sw-cost.sh} +368 -34
- package/scripts/{cct-daemon.sh → sw-daemon.sh} +2217 -204
- package/scripts/sw-dashboard.sh +477 -0
- package/scripts/sw-developer-simulation.sh +252 -0
- package/scripts/sw-docs.sh +635 -0
- package/scripts/sw-doctor.sh +907 -0
- package/scripts/{cct-fix.sh → sw-fix.sh} +10 -6
- package/scripts/{cct-fleet.sh → sw-fleet.sh} +498 -22
- package/scripts/sw-github-checks.sh +521 -0
- package/scripts/sw-github-deploy.sh +533 -0
- package/scripts/sw-github-graphql.sh +972 -0
- package/scripts/sw-heartbeat.sh +293 -0
- package/scripts/{cct-init.sh → sw-init.sh} +144 -11
- package/scripts/sw-intelligence.sh +1196 -0
- package/scripts/sw-jira.sh +643 -0
- package/scripts/sw-launchd.sh +364 -0
- package/scripts/sw-linear.sh +648 -0
- package/scripts/{cct-logs.sh → sw-logs.sh} +72 -2
- package/scripts/{cct-loop.sh → sw-loop.sh} +534 -44
- package/scripts/{cct-memory.sh → sw-memory.sh} +321 -38
- package/scripts/sw-patrol-meta.sh +417 -0
- package/scripts/sw-pipeline-composer.sh +455 -0
- package/scripts/{cct-pipeline.sh → sw-pipeline.sh} +2319 -178
- package/scripts/sw-predictive.sh +820 -0
- package/scripts/{cct-prep.sh → sw-prep.sh} +339 -49
- package/scripts/{cct-ps.sh → sw-ps.sh} +6 -4
- package/scripts/{cct-reaper.sh → sw-reaper.sh} +6 -4
- package/scripts/sw-remote.sh +687 -0
- package/scripts/sw-self-optimize.sh +947 -0
- package/scripts/sw-session.sh +519 -0
- package/scripts/sw-setup.sh +234 -0
- package/scripts/sw-status.sh +605 -0
- package/scripts/{cct-templates.sh → sw-templates.sh} +9 -4
- package/scripts/sw-tmux.sh +591 -0
- package/scripts/sw-tracker-jira.sh +277 -0
- package/scripts/sw-tracker-linear.sh +292 -0
- package/scripts/sw-tracker.sh +409 -0
- package/scripts/{cct-upgrade.sh → sw-upgrade.sh} +103 -46
- package/scripts/{cct-worktree.sh → sw-worktree.sh} +3 -0
- package/templates/pipelines/autonomous.json +27 -5
- package/templates/pipelines/full.json +12 -0
- package/templates/pipelines/standard.json +12 -0
- package/tmux/{claude-teams-overlay.conf → shipwright-overlay.conf} +27 -9
- package/tmux/templates/accessibility.json +34 -0
- package/tmux/templates/api-design.json +35 -0
- package/tmux/templates/architecture.json +1 -0
- package/tmux/templates/bug-fix.json +9 -0
- package/tmux/templates/code-review.json +1 -0
- package/tmux/templates/compliance.json +36 -0
- package/tmux/templates/data-pipeline.json +36 -0
- package/tmux/templates/debt-paydown.json +34 -0
- package/tmux/templates/devops.json +1 -0
- package/tmux/templates/documentation.json +1 -0
- package/tmux/templates/exploration.json +1 -0
- package/tmux/templates/feature-dev.json +1 -0
- package/tmux/templates/full-stack.json +8 -0
- package/tmux/templates/i18n.json +34 -0
- package/tmux/templates/incident-response.json +36 -0
- package/tmux/templates/migration.json +1 -0
- package/tmux/templates/observability.json +35 -0
- package/tmux/templates/onboarding.json +33 -0
- package/tmux/templates/performance.json +35 -0
- package/tmux/templates/refactor.json +1 -0
- package/tmux/templates/release.json +35 -0
- package/tmux/templates/security-audit.json +8 -0
- package/tmux/templates/spike.json +34 -0
- package/tmux/templates/testing.json +1 -0
- package/tmux/tmux.conf +98 -9
- package/scripts/cct-doctor.sh +0 -414
- package/scripts/cct-session.sh +0 -284
- package/scripts/cct-status.sh +0 -169
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ shipwright tracker — Provider Router for Issue Tracker Integration ║
|
|
4
|
+
# ║ Route notifications · Configure providers · Linear & Jira support ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
8
|
+
|
|
9
|
+
VERSION="1.9.0"
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
+
|
|
13
|
+
# ─── Colors (matches Seth's tmux theme) ─────────────────────────────────────
|
|
14
|
+
CYAN='\033[38;2;0;212;255m' # #00d4ff — primary accent
|
|
15
|
+
PURPLE='\033[38;2;124;58;237m' # #7c3aed — secondary
|
|
16
|
+
BLUE='\033[38;2;0;102;255m' # #0066ff — tertiary
|
|
17
|
+
GREEN='\033[38;2;74;222;128m' # success
|
|
18
|
+
YELLOW='\033[38;2;250;204;21m' # warning
|
|
19
|
+
RED='\033[38;2;248;113;113m' # error
|
|
20
|
+
DIM='\033[2m'
|
|
21
|
+
BOLD='\033[1m'
|
|
22
|
+
RESET='\033[0m'
|
|
23
|
+
|
|
24
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
25
|
+
# shellcheck source=lib/compat.sh
|
|
26
|
+
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
27
|
+
# ─── Output Helpers ─────────────────────────────────────────────────────────
|
|
28
|
+
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
29
|
+
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
30
|
+
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
31
|
+
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
|
|
32
|
+
|
|
33
|
+
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
34
|
+
now_epoch() { date +%s; }
|
|
35
|
+
|
|
36
|
+
# ─── Structured Event Log ──────────────────────────────────────────────────
|
|
37
|
+
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
38
|
+
|
|
39
|
+
emit_event() {
|
|
40
|
+
local event_type="$1"
|
|
41
|
+
shift
|
|
42
|
+
local json_fields=""
|
|
43
|
+
for kv in "$@"; do
|
|
44
|
+
local key="${kv%%=*}"
|
|
45
|
+
local val="${kv#*=}"
|
|
46
|
+
if [[ "$val" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
|
|
47
|
+
json_fields="${json_fields},\"${key}\":${val}"
|
|
48
|
+
else
|
|
49
|
+
val="${val//\"/\\\"}"
|
|
50
|
+
json_fields="${json_fields},\"${key}\":\"${val}\""
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
mkdir -p "${HOME}/.shipwright"
|
|
54
|
+
echo "{\"ts\":\"$(now_iso)\",\"ts_epoch\":$(now_epoch),\"type\":\"${event_type}\"${json_fields}}" >> "$EVENTS_FILE"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# ─── Configuration ─────────────────────────────────────────────────────────
|
|
58
|
+
CONFIG_DIR="${HOME}/.shipwright"
|
|
59
|
+
TRACKER_CONFIG="${CONFIG_DIR}/tracker-config.json"
|
|
60
|
+
TRACKER_PROVIDER=""
|
|
61
|
+
|
|
62
|
+
# Load tracker config: reads provider, then sources the right provider script
|
|
63
|
+
load_tracker_config() {
|
|
64
|
+
# Already loaded?
|
|
65
|
+
if [[ -n "$TRACKER_PROVIDER" ]]; then
|
|
66
|
+
return 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Check env var first
|
|
70
|
+
local provider="${TRACKER_PROVIDER_OVERRIDE:-}"
|
|
71
|
+
|
|
72
|
+
# Fall back to tracker-config.json
|
|
73
|
+
if [[ -z "$provider" && -f "$TRACKER_CONFIG" ]]; then
|
|
74
|
+
provider=$(jq -r '.provider // "none"' "$TRACKER_CONFIG" 2>/dev/null || echo "none")
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Fall back to daemon-config.json tracker block
|
|
78
|
+
if [[ -z "$provider" || "$provider" == "none" ]]; then
|
|
79
|
+
local daemon_cfg=".claude/daemon-config.json"
|
|
80
|
+
if [[ -f "$daemon_cfg" ]]; then
|
|
81
|
+
provider=$(jq -r '.tracker.provider // "none"' "$daemon_cfg" 2>/dev/null || echo "none")
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
TRACKER_PROVIDER="${provider:-none}"
|
|
86
|
+
|
|
87
|
+
case "$TRACKER_PROVIDER" in
|
|
88
|
+
linear)
|
|
89
|
+
if [[ -f "$SCRIPT_DIR/sw-tracker-linear.sh" ]]; then
|
|
90
|
+
source "$SCRIPT_DIR/sw-tracker-linear.sh"
|
|
91
|
+
return 0
|
|
92
|
+
else
|
|
93
|
+
warn "Linear provider script not found: $SCRIPT_DIR/sw-tracker-linear.sh"
|
|
94
|
+
fi
|
|
95
|
+
;;
|
|
96
|
+
jira)
|
|
97
|
+
if [[ -f "$SCRIPT_DIR/sw-tracker-jira.sh" ]]; then
|
|
98
|
+
source "$SCRIPT_DIR/sw-tracker-jira.sh"
|
|
99
|
+
return 0
|
|
100
|
+
else
|
|
101
|
+
warn "Jira provider script not found: $SCRIPT_DIR/sw-tracker-jira.sh"
|
|
102
|
+
fi
|
|
103
|
+
;;
|
|
104
|
+
none|"") return 0 ;;
|
|
105
|
+
*)
|
|
106
|
+
warn "Unknown tracker provider: $TRACKER_PROVIDER"
|
|
107
|
+
TRACKER_PROVIDER="none"
|
|
108
|
+
;;
|
|
109
|
+
esac
|
|
110
|
+
return 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Check if a tracker is configured and available
|
|
114
|
+
tracker_available() {
|
|
115
|
+
load_tracker_config
|
|
116
|
+
[[ "$TRACKER_PROVIDER" != "none" && -n "$TRACKER_PROVIDER" ]]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Route notification to the active provider
|
|
120
|
+
# Usage: tracker_notify <event> <gh_issue> [detail]
|
|
121
|
+
# Events: spawn, started, stage_complete, stage_failed, review, pr-created, completed, done, failed
|
|
122
|
+
tracker_notify() {
|
|
123
|
+
local event="$1"
|
|
124
|
+
local gh_issue="${2:-}"
|
|
125
|
+
local detail="${3:-}"
|
|
126
|
+
|
|
127
|
+
load_tracker_config
|
|
128
|
+
|
|
129
|
+
if [[ "$TRACKER_PROVIDER" == "none" || -z "$TRACKER_PROVIDER" ]]; then
|
|
130
|
+
return 0 # silently skip when no provider configured
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Provider scripts define provider_notify()
|
|
134
|
+
if type provider_notify &>/dev/null; then
|
|
135
|
+
provider_notify "$event" "$gh_issue" "$detail"
|
|
136
|
+
else
|
|
137
|
+
warn "Provider '$TRACKER_PROVIDER' loaded but provider_notify() not defined"
|
|
138
|
+
fi
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# ─── Interactive Init ──────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
cmd_init() {
|
|
144
|
+
echo -e "${PURPLE}${BOLD}━━━ Tracker Integration Setup ━━━${RESET}"
|
|
145
|
+
echo ""
|
|
146
|
+
|
|
147
|
+
mkdir -p "$CONFIG_DIR"
|
|
148
|
+
|
|
149
|
+
echo -e " ${BOLD}Select a tracker provider:${RESET}"
|
|
150
|
+
echo ""
|
|
151
|
+
echo -e " ${CYAN}1${RESET}) Linear"
|
|
152
|
+
echo -e " ${CYAN}2${RESET}) Jira"
|
|
153
|
+
echo -e " ${CYAN}3${RESET}) None (disable)"
|
|
154
|
+
echo ""
|
|
155
|
+
read -rp " Choice [1-3]: " choice
|
|
156
|
+
|
|
157
|
+
local provider="none"
|
|
158
|
+
case "$choice" in
|
|
159
|
+
1) provider="linear" ;;
|
|
160
|
+
2) provider="jira" ;;
|
|
161
|
+
3) provider="none" ;;
|
|
162
|
+
*)
|
|
163
|
+
error "Invalid choice"
|
|
164
|
+
exit 1
|
|
165
|
+
;;
|
|
166
|
+
esac
|
|
167
|
+
|
|
168
|
+
if [[ "$provider" == "none" ]]; then
|
|
169
|
+
# Write minimal config
|
|
170
|
+
local tmp_config="${TRACKER_CONFIG}.tmp"
|
|
171
|
+
jq -n --arg provider "none" --arg updated "$(now_iso)" \
|
|
172
|
+
'{provider: $provider, updated_at: $updated}' > "$tmp_config"
|
|
173
|
+
mv "$tmp_config" "$TRACKER_CONFIG"
|
|
174
|
+
success "Tracker disabled"
|
|
175
|
+
return 0
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
if [[ "$provider" == "linear" ]]; then
|
|
179
|
+
_init_linear
|
|
180
|
+
elif [[ "$provider" == "jira" ]]; then
|
|
181
|
+
_init_jira
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
emit_event "tracker.init" "provider=$provider"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_init_linear() {
|
|
188
|
+
echo ""
|
|
189
|
+
echo -e " ${CYAN}1.${RESET} Go to ${DIM}https://linear.app/settings/api${RESET}"
|
|
190
|
+
echo -e " ${CYAN}2.${RESET} Create a personal API key"
|
|
191
|
+
echo -e " ${CYAN}3.${RESET} Paste it below"
|
|
192
|
+
echo ""
|
|
193
|
+
read -rp " Linear API Key: " api_key
|
|
194
|
+
if [[ -z "$api_key" ]]; then
|
|
195
|
+
error "API key is required"
|
|
196
|
+
exit 1
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
read -rp " Team ID [press Enter for default]: " team_id
|
|
200
|
+
team_id="${team_id:-}"
|
|
201
|
+
read -rp " Project ID [press Enter for default]: " project_id
|
|
202
|
+
project_id="${project_id:-}"
|
|
203
|
+
|
|
204
|
+
local tmp_config="${TRACKER_CONFIG}.tmp"
|
|
205
|
+
jq -n \
|
|
206
|
+
--arg provider "linear" \
|
|
207
|
+
--arg api_key "$api_key" \
|
|
208
|
+
--arg team_id "$team_id" \
|
|
209
|
+
--arg project_id "$project_id" \
|
|
210
|
+
--arg updated "$(now_iso)" \
|
|
211
|
+
'{
|
|
212
|
+
provider: $provider,
|
|
213
|
+
linear: {
|
|
214
|
+
api_key: $api_key,
|
|
215
|
+
team_id: $team_id,
|
|
216
|
+
project_id: $project_id
|
|
217
|
+
},
|
|
218
|
+
updated_at: $updated
|
|
219
|
+
}' > "$tmp_config"
|
|
220
|
+
mv "$tmp_config" "$TRACKER_CONFIG"
|
|
221
|
+
chmod 600 "$TRACKER_CONFIG"
|
|
222
|
+
|
|
223
|
+
success "Linear tracker configured"
|
|
224
|
+
echo ""
|
|
225
|
+
|
|
226
|
+
# Validate connection
|
|
227
|
+
info "Validating API key..."
|
|
228
|
+
local payload
|
|
229
|
+
payload=$(jq -n --arg q 'query { viewer { id name } }' '{query: $q}')
|
|
230
|
+
local response
|
|
231
|
+
response=$(curl -sf -X POST "https://api.linear.app/graphql" \
|
|
232
|
+
-H "Authorization: $api_key" \
|
|
233
|
+
-H "Content-Type: application/json" \
|
|
234
|
+
-d "$payload" 2>&1) || {
|
|
235
|
+
warn "Could not validate API key — check your key"
|
|
236
|
+
return 0
|
|
237
|
+
}
|
|
238
|
+
local viewer_name
|
|
239
|
+
viewer_name=$(echo "$response" | jq -r '.data.viewer.name // "Unknown"' 2>/dev/null || echo "Unknown")
|
|
240
|
+
success "Authenticated as: ${viewer_name}"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
_init_jira() {
|
|
244
|
+
echo ""
|
|
245
|
+
echo -e " ${CYAN}1.${RESET} Enter your Jira instance URL (e.g. https://myteam.atlassian.net)"
|
|
246
|
+
echo -e " ${CYAN}2.${RESET} Create an API token at ${DIM}https://id.atlassian.com/manage-profile/security/api-tokens${RESET}"
|
|
247
|
+
echo ""
|
|
248
|
+
read -rp " Jira Base URL: " base_url
|
|
249
|
+
if [[ -z "$base_url" ]]; then
|
|
250
|
+
error "Base URL is required"
|
|
251
|
+
exit 1
|
|
252
|
+
fi
|
|
253
|
+
# Strip trailing slash
|
|
254
|
+
base_url="${base_url%/}"
|
|
255
|
+
|
|
256
|
+
read -rp " Jira Email: " email
|
|
257
|
+
if [[ -z "$email" ]]; then
|
|
258
|
+
error "Email is required"
|
|
259
|
+
exit 1
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
read -rp " Jira API Token: " api_token
|
|
263
|
+
if [[ -z "$api_token" ]]; then
|
|
264
|
+
error "API token is required"
|
|
265
|
+
exit 1
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
read -rp " Project Key (e.g. PROJ): " project_key
|
|
269
|
+
project_key="${project_key:-}"
|
|
270
|
+
|
|
271
|
+
local tmp_config="${TRACKER_CONFIG}.tmp"
|
|
272
|
+
jq -n \
|
|
273
|
+
--arg provider "jira" \
|
|
274
|
+
--arg base_url "$base_url" \
|
|
275
|
+
--arg email "$email" \
|
|
276
|
+
--arg api_token "$api_token" \
|
|
277
|
+
--arg project_key "$project_key" \
|
|
278
|
+
--arg updated "$(now_iso)" \
|
|
279
|
+
'{
|
|
280
|
+
provider: $provider,
|
|
281
|
+
jira: {
|
|
282
|
+
base_url: $base_url,
|
|
283
|
+
email: $email,
|
|
284
|
+
api_token: $api_token,
|
|
285
|
+
project_key: $project_key
|
|
286
|
+
},
|
|
287
|
+
updated_at: $updated
|
|
288
|
+
}' > "$tmp_config"
|
|
289
|
+
mv "$tmp_config" "$TRACKER_CONFIG"
|
|
290
|
+
chmod 600 "$TRACKER_CONFIG"
|
|
291
|
+
|
|
292
|
+
success "Jira tracker configured"
|
|
293
|
+
echo ""
|
|
294
|
+
|
|
295
|
+
# Validate connection
|
|
296
|
+
info "Validating connection..."
|
|
297
|
+
local auth
|
|
298
|
+
auth=$(printf '%s:%s' "$email" "$api_token" | base64)
|
|
299
|
+
local response
|
|
300
|
+
response=$(curl -sf -X GET "${base_url}/rest/api/3/myself" \
|
|
301
|
+
-H "Authorization: Basic $auth" \
|
|
302
|
+
-H "Content-Type: application/json" 2>&1) || {
|
|
303
|
+
warn "Could not validate connection — check your credentials"
|
|
304
|
+
return 0
|
|
305
|
+
}
|
|
306
|
+
local display_name
|
|
307
|
+
display_name=$(echo "$response" | jq -r '.displayName // "Unknown"' 2>/dev/null || echo "Unknown")
|
|
308
|
+
success "Authenticated as: ${display_name}"
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
# ─── Status ────────────────────────────────────────────────────────────────
|
|
312
|
+
|
|
313
|
+
cmd_status() {
|
|
314
|
+
load_tracker_config
|
|
315
|
+
|
|
316
|
+
echo -e "${PURPLE}${BOLD}━━━ Tracker Status ━━━${RESET}"
|
|
317
|
+
echo ""
|
|
318
|
+
echo -e " ${BOLD}Provider:${RESET} ${CYAN}${TRACKER_PROVIDER}${RESET}"
|
|
319
|
+
|
|
320
|
+
if [[ -f "$TRACKER_CONFIG" ]]; then
|
|
321
|
+
local updated
|
|
322
|
+
updated=$(jq -r '.updated_at // "unknown"' "$TRACKER_CONFIG" 2>/dev/null || echo "unknown")
|
|
323
|
+
echo -e " ${BOLD}Config:${RESET} ${DIM}${TRACKER_CONFIG}${RESET}"
|
|
324
|
+
echo -e " ${BOLD}Updated:${RESET} ${DIM}${updated}${RESET}"
|
|
325
|
+
else
|
|
326
|
+
echo -e " ${BOLD}Config:${RESET} ${DIM}(not configured — run 'shipwright tracker init')${RESET}"
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
echo ""
|
|
330
|
+
|
|
331
|
+
# Show recent tracker events
|
|
332
|
+
if [[ -f "$EVENTS_FILE" ]]; then
|
|
333
|
+
local recent
|
|
334
|
+
recent=$(grep '"type":"tracker\.' "$EVENTS_FILE" 2>/dev/null | tail -5 || true)
|
|
335
|
+
if [[ -n "$recent" ]]; then
|
|
336
|
+
echo -e "${BOLD}Recent Tracker Activity${RESET}"
|
|
337
|
+
echo "$recent" | while IFS= read -r line; do
|
|
338
|
+
local ts type
|
|
339
|
+
ts=$(echo "$line" | jq -r '.ts' 2>/dev/null || true)
|
|
340
|
+
type=$(echo "$line" | jq -r '.type' 2>/dev/null || true)
|
|
341
|
+
echo -e " ${DIM}${ts}${RESET} ${type}"
|
|
342
|
+
done
|
|
343
|
+
echo ""
|
|
344
|
+
fi
|
|
345
|
+
fi
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
# ─── Help ──────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
show_help() {
|
|
351
|
+
echo -e "${CYAN}${BOLD}shipwright tracker${RESET} — Issue Tracker Provider Router"
|
|
352
|
+
echo ""
|
|
353
|
+
echo -e "${BOLD}USAGE${RESET}"
|
|
354
|
+
echo -e " ${CYAN}shipwright tracker${RESET} <command> [options]"
|
|
355
|
+
echo ""
|
|
356
|
+
echo -e "${BOLD}COMMANDS${RESET}"
|
|
357
|
+
echo -e " ${CYAN}init${RESET} Configure tracker provider"
|
|
358
|
+
echo -e " ${CYAN}status${RESET} Show tracker configuration"
|
|
359
|
+
echo -e " ${CYAN}available${RESET} Check if a tracker is configured"
|
|
360
|
+
echo -e " ${CYAN}notify${RESET} <event> <issue> [detail] Send notification to tracker"
|
|
361
|
+
echo -e " ${CYAN}help${RESET} Show this help"
|
|
362
|
+
echo ""
|
|
363
|
+
echo -e "${BOLD}PROVIDERS${RESET}"
|
|
364
|
+
echo -e " ${CYAN}linear${RESET} Linear.app (GraphQL API)"
|
|
365
|
+
echo -e " ${CYAN}jira${RESET} Atlassian Jira (REST API v3)"
|
|
366
|
+
echo ""
|
|
367
|
+
echo -e "${BOLD}NOTIFICATION EVENTS${RESET}"
|
|
368
|
+
echo -e " ${CYAN}spawn${RESET} Pipeline started"
|
|
369
|
+
echo -e " ${CYAN}stage_complete${RESET} Stage finished (detail: stage_id|duration|description)"
|
|
370
|
+
echo -e " ${CYAN}stage_failed${RESET} Stage failed (detail: stage_id|error_context)"
|
|
371
|
+
echo -e " ${CYAN}review${RESET} PR created (detail: pr_url)"
|
|
372
|
+
echo -e " ${CYAN}completed${RESET} Pipeline done"
|
|
373
|
+
echo -e " ${CYAN}failed${RESET} Pipeline failed (detail: error_message)"
|
|
374
|
+
echo ""
|
|
375
|
+
echo -e "${BOLD}CONFIGURATION${RESET}"
|
|
376
|
+
echo -e " Config file: ${DIM}~/.shipwright/tracker-config.json${RESET}"
|
|
377
|
+
echo -e " Env override: ${DIM}TRACKER_PROVIDER_OVERRIDE=linear|jira|none${RESET}"
|
|
378
|
+
echo -e " Daemon block: ${DIM}.claude/daemon-config.json → .tracker.provider${RESET}"
|
|
379
|
+
echo ""
|
|
380
|
+
echo -e "${BOLD}EXAMPLES${RESET}"
|
|
381
|
+
echo -e " ${DIM}shipwright tracker init${RESET} # Configure provider"
|
|
382
|
+
echo -e " ${DIM}shipwright tracker status${RESET} # Show config"
|
|
383
|
+
echo -e " ${DIM}shipwright tracker notify spawn 42${RESET} # Notify pipeline started"
|
|
384
|
+
echo -e " ${DIM}shipwright tracker notify completed 42${RESET} # Notify pipeline done"
|
|
385
|
+
echo -e " ${DIM}shipwright tracker notify review 42 'https://...'${RESET} # Notify PR created"
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
# ─── Command Router ─────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
main() {
|
|
391
|
+
local cmd="${1:-help}"
|
|
392
|
+
shift 2>/dev/null || true
|
|
393
|
+
|
|
394
|
+
case "$cmd" in
|
|
395
|
+
notify) tracker_notify "$@" ;;
|
|
396
|
+
available) tracker_available && echo "true" || echo "false" ;;
|
|
397
|
+
init) cmd_init "$@" ;;
|
|
398
|
+
status) cmd_status "$@" ;;
|
|
399
|
+
help|--help|-h) show_help ;;
|
|
400
|
+
*)
|
|
401
|
+
error "Unknown command: ${cmd}"
|
|
402
|
+
echo ""
|
|
403
|
+
show_help
|
|
404
|
+
exit 1
|
|
405
|
+
;;
|
|
406
|
+
esac
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
main "$@"
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
-
# ║
|
|
3
|
+
# ║ sw upgrade — Detect and apply updates from the repo ║
|
|
4
4
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
5
|
+
VERSION="1.9.0"
|
|
5
6
|
set -euo pipefail
|
|
7
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
6
8
|
|
|
7
9
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
-
MANIFEST_DIR="$HOME/.
|
|
10
|
+
MANIFEST_DIR="$HOME/.shipwright"
|
|
9
11
|
MANIFEST="$MANIFEST_DIR/manifest.json"
|
|
10
12
|
|
|
11
13
|
# ─── Colors ────────────────────────────────────────────────────────────────
|
|
@@ -19,6 +21,9 @@ DIM='\033[2m'
|
|
|
19
21
|
BOLD='\033[1m'
|
|
20
22
|
RESET='\033[0m'
|
|
21
23
|
|
|
24
|
+
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
25
|
+
# shellcheck source=lib/compat.sh
|
|
26
|
+
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
22
27
|
info() { echo -e "${CYAN}${BOLD}▸${RESET} $*"; }
|
|
23
28
|
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
|
|
24
29
|
warn() { echo -e "${YELLOW}${BOLD}⚠${RESET} $*"; }
|
|
@@ -59,7 +64,7 @@ find_repo() {
|
|
|
59
64
|
# 3. Walk up from script dir
|
|
60
65
|
local dir="$SCRIPT_DIR"
|
|
61
66
|
while [[ "$dir" != "/" ]]; do
|
|
62
|
-
if [[ -f "$dir/install.sh" && -d "$dir/scripts" && -f "$dir/scripts/
|
|
67
|
+
if [[ -f "$dir/install.sh" && -d "$dir/scripts" && -f "$dir/scripts/sw" ]]; then
|
|
63
68
|
echo "$dir"
|
|
64
69
|
return
|
|
65
70
|
fi
|
|
@@ -94,52 +99,104 @@ BIN_DIR="$HOME/.local/bin"
|
|
|
94
99
|
|
|
95
100
|
FILES=(
|
|
96
101
|
"tmux.conf|tmux/tmux.conf|$HOME/.tmux.conf|false|false"
|
|
97
|
-
"
|
|
102
|
+
"shipwright-overlay.conf|tmux/shipwright-overlay.conf|$HOME/.tmux/shipwright-overlay.conf|false|false"
|
|
98
103
|
"settings.json.template|claude-code/settings.json.template|$HOME/.claude/settings.json.template|false|false"
|
|
99
104
|
"settings.json||$HOME/.claude/settings.json|true|false"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
105
|
+
"sw|scripts/sw|$BIN_DIR/sw|false|true"
|
|
106
|
+
"sw-session.sh|scripts/sw-session.sh|$BIN_DIR/sw-session.sh|false|true"
|
|
107
|
+
"sw-status.sh|scripts/sw-status.sh|$BIN_DIR/sw-status.sh|false|true"
|
|
108
|
+
"sw-cleanup.sh|scripts/sw-cleanup.sh|$BIN_DIR/sw-cleanup.sh|false|true"
|
|
109
|
+
"sw-upgrade.sh|scripts/sw-upgrade.sh|$BIN_DIR/sw-upgrade.sh|false|true"
|
|
110
|
+
"sw-doctor.sh|scripts/sw-doctor.sh|$BIN_DIR/sw-doctor.sh|false|true"
|
|
111
|
+
"sw-logs.sh|scripts/sw-logs.sh|$BIN_DIR/sw-logs.sh|false|true"
|
|
112
|
+
"sw-ps.sh|scripts/sw-ps.sh|$BIN_DIR/sw-ps.sh|false|true"
|
|
113
|
+
"sw-templates.sh|scripts/sw-templates.sh|$BIN_DIR/sw-templates.sh|false|true"
|
|
114
|
+
"sw-loop.sh|scripts/sw-loop.sh|$BIN_DIR/sw-loop.sh|false|true"
|
|
115
|
+
"sw-pipeline.sh|scripts/sw-pipeline.sh|$BIN_DIR/sw-pipeline.sh|false|true"
|
|
116
|
+
"sw-pipeline-test.sh|scripts/sw-pipeline-test.sh|$BIN_DIR/sw-pipeline-test.sh|false|true"
|
|
117
|
+
"sw-worktree.sh|scripts/sw-worktree.sh|$BIN_DIR/sw-worktree.sh|false|true"
|
|
118
|
+
"sw-init.sh|scripts/sw-init.sh|$BIN_DIR/sw-init.sh|false|true"
|
|
119
|
+
"sw-setup.sh|scripts/sw-setup.sh|$BIN_DIR/sw-setup.sh|false|true"
|
|
120
|
+
"sw-prep.sh|scripts/sw-prep.sh|$BIN_DIR/sw-prep.sh|false|true"
|
|
121
|
+
"sw-daemon.sh|scripts/sw-daemon.sh|$BIN_DIR/sw-daemon.sh|false|true"
|
|
122
|
+
"sw-daemon-test.sh|scripts/sw-daemon-test.sh|$BIN_DIR/sw-daemon-test.sh|false|true"
|
|
123
|
+
"sw-prep-test.sh|scripts/sw-prep-test.sh|$BIN_DIR/sw-prep-test.sh|false|true"
|
|
124
|
+
"sw-memory.sh|scripts/sw-memory.sh|$BIN_DIR/sw-memory.sh|false|true"
|
|
125
|
+
"sw-memory-test.sh|scripts/sw-memory-test.sh|$BIN_DIR/sw-memory-test.sh|false|true"
|
|
126
|
+
"sw-cost.sh|scripts/sw-cost.sh|$BIN_DIR/sw-cost.sh|false|true"
|
|
127
|
+
"sw-fleet.sh|scripts/sw-fleet.sh|$BIN_DIR/sw-fleet.sh|false|true"
|
|
128
|
+
"sw-fleet-test.sh|scripts/sw-fleet-test.sh|$BIN_DIR/sw-fleet-test.sh|false|true"
|
|
129
|
+
"sw-fix.sh|scripts/sw-fix.sh|$BIN_DIR/sw-fix.sh|false|true"
|
|
130
|
+
"sw-fix-test.sh|scripts/sw-fix-test.sh|$BIN_DIR/sw-fix-test.sh|false|true"
|
|
131
|
+
"sw-reaper.sh|scripts/sw-reaper.sh|$BIN_DIR/sw-reaper.sh|false|true"
|
|
132
|
+
"sw-dashboard.sh|scripts/sw-dashboard.sh|$BIN_DIR/sw-dashboard.sh|false|true"
|
|
133
|
+
"sw-docs.sh|scripts/sw-docs.sh|$BIN_DIR/sw-docs.sh|false|true"
|
|
134
|
+
"sw-tmux.sh|scripts/sw-tmux.sh|$BIN_DIR/sw-tmux.sh|false|true"
|
|
135
|
+
"sw-connect.sh|scripts/sw-connect.sh|$BIN_DIR/sw-connect.sh|false|true"
|
|
136
|
+
"sw-tracker.sh|scripts/sw-tracker.sh|$BIN_DIR/sw-tracker.sh|false|true"
|
|
137
|
+
"sw-linear.sh|scripts/sw-linear.sh|$BIN_DIR/sw-linear.sh|false|true"
|
|
138
|
+
"sw-jira.sh|scripts/sw-jira.sh|$BIN_DIR/sw-jira.sh|false|true"
|
|
139
|
+
"sw-launchd.sh|scripts/sw-launchd.sh|$BIN_DIR/sw-launchd.sh|false|true"
|
|
140
|
+
"sw-checkpoint.sh|scripts/sw-checkpoint.sh|$BIN_DIR/sw-checkpoint.sh|false|true"
|
|
141
|
+
"sw-heartbeat.sh|scripts/sw-heartbeat.sh|$BIN_DIR/sw-heartbeat.sh|false|true"
|
|
142
|
+
"sw-intelligence.sh|scripts/sw-intelligence.sh|$BIN_DIR/sw-intelligence.sh|false|true"
|
|
143
|
+
"sw-pipeline-composer.sh|scripts/sw-pipeline-composer.sh|$BIN_DIR/sw-pipeline-composer.sh|false|true"
|
|
144
|
+
"sw-self-optimize.sh|scripts/sw-self-optimize.sh|$BIN_DIR/sw-self-optimize.sh|false|true"
|
|
145
|
+
"sw-predictive.sh|scripts/sw-predictive.sh|$BIN_DIR/sw-predictive.sh|false|true"
|
|
146
|
+
"sw-adversarial.sh|scripts/sw-adversarial.sh|$BIN_DIR/sw-adversarial.sh|false|true"
|
|
147
|
+
"sw-developer-simulation.sh|scripts/sw-developer-simulation.sh|$BIN_DIR/sw-developer-simulation.sh|false|true"
|
|
148
|
+
"sw-architecture-enforcer.sh|scripts/sw-architecture-enforcer.sh|$BIN_DIR/sw-architecture-enforcer.sh|false|true"
|
|
149
|
+
"sw-patrol-meta.sh|scripts/sw-patrol-meta.sh|$BIN_DIR/sw-patrol-meta.sh|false|true"
|
|
150
|
+
# GitHub API modules
|
|
151
|
+
"sw-github-graphql.sh|scripts/sw-github-graphql.sh|$BIN_DIR/sw-github-graphql.sh|false|true"
|
|
152
|
+
"sw-github-checks.sh|scripts/sw-github-checks.sh|$BIN_DIR/sw-github-checks.sh|false|true"
|
|
153
|
+
"sw-github-deploy.sh|scripts/sw-github-deploy.sh|$BIN_DIR/sw-github-deploy.sh|false|true"
|
|
154
|
+
# Tracker adapters
|
|
155
|
+
"sw-tracker-linear.sh|scripts/sw-tracker-linear.sh|$BIN_DIR/sw-tracker-linear.sh|false|true"
|
|
156
|
+
"sw-tracker-jira.sh|scripts/sw-tracker-jira.sh|$BIN_DIR/sw-tracker-jira.sh|false|true"
|
|
157
|
+
# Test suites
|
|
158
|
+
"sw-connect-test.sh|scripts/sw-connect-test.sh|$BIN_DIR/sw-connect-test.sh|false|true"
|
|
159
|
+
"sw-intelligence-test.sh|scripts/sw-intelligence-test.sh|$BIN_DIR/sw-intelligence-test.sh|false|true"
|
|
160
|
+
"sw-frontier-test.sh|scripts/sw-frontier-test.sh|$BIN_DIR/sw-frontier-test.sh|false|true"
|
|
161
|
+
"sw-self-optimize-test.sh|scripts/sw-self-optimize-test.sh|$BIN_DIR/sw-self-optimize-test.sh|false|true"
|
|
162
|
+
"sw-pipeline-composer-test.sh|scripts/sw-pipeline-composer-test.sh|$BIN_DIR/sw-pipeline-composer-test.sh|false|true"
|
|
163
|
+
"sw-predictive-test.sh|scripts/sw-predictive-test.sh|$BIN_DIR/sw-predictive-test.sh|false|true"
|
|
164
|
+
"sw-heartbeat-test.sh|scripts/sw-heartbeat-test.sh|$BIN_DIR/sw-heartbeat-test.sh|false|true"
|
|
165
|
+
"sw-github-graphql-test.sh|scripts/sw-github-graphql-test.sh|$BIN_DIR/sw-github-graphql-test.sh|false|true"
|
|
166
|
+
"sw-github-checks-test.sh|scripts/sw-github-checks-test.sh|$BIN_DIR/sw-github-checks-test.sh|false|true"
|
|
167
|
+
"sw-github-deploy-test.sh|scripts/sw-github-deploy-test.sh|$BIN_DIR/sw-github-deploy-test.sh|false|true"
|
|
168
|
+
"sw-tracker-test.sh|scripts/sw-tracker-test.sh|$BIN_DIR/sw-tracker-test.sh|false|true"
|
|
169
|
+
"sw-init-test.sh|scripts/sw-init-test.sh|$BIN_DIR/sw-init-test.sh|false|true"
|
|
170
|
+
"sw-session-test.sh|scripts/sw-session-test.sh|$BIN_DIR/sw-session-test.sh|false|true"
|
|
171
|
+
"sw-remote-test.sh|scripts/sw-remote-test.sh|$BIN_DIR/sw-remote-test.sh|false|true"
|
|
172
|
+
# Shared libraries
|
|
173
|
+
"compat.sh|scripts/lib/compat.sh|$BIN_DIR/lib/compat.sh|false|false"
|
|
126
174
|
"CLAUDE.md.shipwright|claude-code/CLAUDE.md.shipwright|$HOME/.claude/CLAUDE.md|true|false"
|
|
127
175
|
"teammate-idle.sh|claude-code/hooks/teammate-idle.sh|$HOME/.claude/hooks/teammate-idle.sh|false|true"
|
|
128
176
|
"task-completed.sh|claude-code/hooks/task-completed.sh|$HOME/.claude/hooks/task-completed.sh|false|true"
|
|
129
177
|
"notify-idle.sh|claude-code/hooks/notify-idle.sh|$HOME/.claude/hooks/notify-idle.sh|false|true"
|
|
130
178
|
"pre-compact-save.sh|claude-code/hooks/pre-compact-save.sh|$HOME/.claude/hooks/pre-compact-save.sh|false|true"
|
|
131
|
-
"feature-dev.json|tmux/templates/feature-dev.json|$HOME/.
|
|
132
|
-
"code-review.json|tmux/templates/code-review.json|$HOME/.
|
|
133
|
-
"refactor.json|tmux/templates/refactor.json|$HOME/.
|
|
134
|
-
"exploration.json|tmux/templates/exploration.json|$HOME/.
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
179
|
+
"feature-dev.json|tmux/templates/feature-dev.json|$HOME/.shipwright/templates/feature-dev.json|false|false"
|
|
180
|
+
"code-review.json|tmux/templates/code-review.json|$HOME/.shipwright/templates/code-review.json|false|false"
|
|
181
|
+
"refactor.json|tmux/templates/refactor.json|$HOME/.shipwright/templates/refactor.json|false|false"
|
|
182
|
+
"exploration.json|tmux/templates/exploration.json|$HOME/.shipwright/templates/exploration.json|false|false"
|
|
183
|
+
"bug-fix.json|tmux/templates/bug-fix.json|$HOME/.shipwright/templates/bug-fix.json|false|false"
|
|
184
|
+
"testing.json|tmux/templates/testing.json|$HOME/.shipwright/templates/testing.json|false|false"
|
|
185
|
+
"full-stack.json|tmux/templates/full-stack.json|$HOME/.shipwright/templates/full-stack.json|false|false"
|
|
186
|
+
"security-audit.json|tmux/templates/security-audit.json|$HOME/.shipwright/templates/security-audit.json|false|false"
|
|
187
|
+
"migration.json|tmux/templates/migration.json|$HOME/.shipwright/templates/migration.json|false|false"
|
|
188
|
+
"documentation.json|tmux/templates/documentation.json|$HOME/.shipwright/templates/documentation.json|false|false"
|
|
189
|
+
"devops.json|tmux/templates/devops.json|$HOME/.shipwright/templates/devops.json|false|false"
|
|
190
|
+
"architecture.json|tmux/templates/architecture.json|$HOME/.shipwright/templates/architecture.json|false|false"
|
|
191
|
+
"definition-of-done.example.md|docs/definition-of-done.example.md|$HOME/.shipwright/templates/definition-of-done.example.md|false|false"
|
|
192
|
+
"pipeline-standard.json|templates/pipelines/standard.json|$HOME/.shipwright/pipelines/standard.json|false|false"
|
|
193
|
+
"pipeline-fast.json|templates/pipelines/fast.json|$HOME/.shipwright/pipelines/fast.json|false|false"
|
|
194
|
+
"pipeline-full.json|templates/pipelines/full.json|$HOME/.shipwright/pipelines/full.json|false|false"
|
|
195
|
+
"pipeline-hotfix.json|templates/pipelines/hotfix.json|$HOME/.shipwright/pipelines/hotfix.json|false|false"
|
|
196
|
+
"pipeline-autonomous.json|templates/pipelines/autonomous.json|$HOME/.shipwright/pipelines/autonomous.json|false|false"
|
|
197
|
+
"pipeline-cost-aware.json|templates/pipelines/cost-aware.json|$HOME/.shipwright/pipelines/cost-aware.json|false|false"
|
|
198
|
+
"pipeline-enterprise.json|templates/pipelines/enterprise.json|$HOME/.shipwright/pipelines/enterprise.json|false|false"
|
|
199
|
+
"pipeline-deployed.json|templates/pipelines/deployed.json|$HOME/.shipwright/pipelines/deployed.json|false|false"
|
|
143
200
|
)
|
|
144
201
|
|
|
145
202
|
# ─── Checksum helper ──────────────────────────────────────────────────────
|
|
@@ -353,7 +410,7 @@ if ! $APPLY; then
|
|
|
353
410
|
fi
|
|
354
411
|
|
|
355
412
|
# Apply upgrades
|
|
356
|
-
|
|
413
|
+
SW_SELF_UPGRADED=false
|
|
357
414
|
|
|
358
415
|
echo -e "${BOLD}Applying...${RESET}"
|
|
359
416
|
echo ""
|
|
@@ -387,8 +444,8 @@ if [[ ${#UPGRADEABLE[@]} -gt 0 ]]; then
|
|
|
387
444
|
for item in "${UPGRADEABLE[@]}"; do
|
|
388
445
|
IFS='|' read -r key src dest executable <<< "$item"
|
|
389
446
|
apply_file "$key" "$src" "$dest" "$executable"
|
|
390
|
-
if [[ "$key" ==
|
|
391
|
-
|
|
447
|
+
if [[ "$key" == sw* ]]; then
|
|
448
|
+
SW_SELF_UPGRADED=true
|
|
392
449
|
fi
|
|
393
450
|
done
|
|
394
451
|
fi
|
|
@@ -408,7 +465,7 @@ write_manifest
|
|
|
408
465
|
success "Manifest updated: $MANIFEST"
|
|
409
466
|
|
|
410
467
|
# Self-upgrade warning
|
|
411
|
-
if $
|
|
468
|
+
if $SW_SELF_UPGRADED; then
|
|
412
469
|
echo ""
|
|
413
470
|
echo -e "${YELLOW}${BOLD}⚠${RESET} The Shipwright CLI itself was upgraded."
|
|
414
471
|
echo -e " Your current command completed, but re-run to use the new version."
|
|
@@ -5,7 +5,9 @@
|
|
|
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="1.9.0"
|
|
8
9
|
set -euo pipefail
|
|
10
|
+
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
11
|
|
|
10
12
|
# ─── Colors ────────────────────────────────────────────────────────────────
|
|
11
13
|
CYAN='\033[38;2;0;212;255m'
|
|
@@ -216,6 +218,7 @@ worktree_remove() {
|
|
|
216
218
|
git worktree remove "$worktree_path" --force 2>/dev/null || {
|
|
217
219
|
warn "Could not cleanly remove worktree $name, forcing..."
|
|
218
220
|
rm -rf "$worktree_path"
|
|
221
|
+
git worktree prune 2>/dev/null || true
|
|
219
222
|
}
|
|
220
223
|
fi
|
|
221
224
|
|