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-autonomous.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
9
|
|
|
10
|
-
VERSION="
|
|
10
|
+
VERSION="3.0.0"
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
13
13
|
|
|
@@ -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
|
|
@@ -266,7 +256,7 @@ ingest_strategic_findings() {
|
|
|
266
256
|
complexity=$(echo "$line" | jq -r '.complexity // "standard"' 2>/dev/null) || true
|
|
267
257
|
|
|
268
258
|
local issue_num=""
|
|
269
|
-
if [[ "${NO_GITHUB:-false}" != "true" ]] && command -v gh
|
|
259
|
+
if [[ "${NO_GITHUB:-false}" != "true" ]] && command -v gh >/dev/null 2>&1; then
|
|
270
260
|
issue_num=$(gh issue list --state open --search "$title" --json number -q '.[0].number' 2>/dev/null || echo "")
|
|
271
261
|
fi
|
|
272
262
|
|
|
@@ -285,6 +275,100 @@ ingest_strategic_findings() {
|
|
|
285
275
|
echo "$findings"
|
|
286
276
|
}
|
|
287
277
|
|
|
278
|
+
# ─── Codebase Analysis (Claude fallback) ────────────────────────────────────
|
|
279
|
+
# When Claude is unavailable, scan the codebase for real findings.
|
|
280
|
+
# Outputs pipe-delimited lines: title|type|complexity (converted to JSON by caller).
|
|
281
|
+
autonomous_heuristic_analysis() {
|
|
282
|
+
local findings=()
|
|
283
|
+
local repo_root
|
|
284
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
|
|
285
|
+
|
|
286
|
+
# 1. Find scripts with no test files
|
|
287
|
+
local untested_scripts=0
|
|
288
|
+
for script in "$repo_root"/scripts/sw-*.sh; do
|
|
289
|
+
[[ -e "$script" ]] || continue
|
|
290
|
+
[[ "$script" == *-test.sh ]] && continue
|
|
291
|
+
local base
|
|
292
|
+
base=$(basename "$script" .sh)
|
|
293
|
+
if [[ ! -f "$repo_root/scripts/${base}-test.sh" ]]; then
|
|
294
|
+
untested_scripts=$((untested_scripts + 1))
|
|
295
|
+
if [[ "$untested_scripts" -le 2 ]]; then
|
|
296
|
+
findings+=("Add test coverage for ${base}.sh|testing|5")
|
|
297
|
+
fi
|
|
298
|
+
fi
|
|
299
|
+
done
|
|
300
|
+
|
|
301
|
+
# 2. Find large files that could be refactored
|
|
302
|
+
while IFS= read -r line; do
|
|
303
|
+
[[ -z "$line" ]] && continue
|
|
304
|
+
local lcount file
|
|
305
|
+
lcount=$(echo "$line" | awk '{print $1}')
|
|
306
|
+
file=$(echo "$line" | awk '{$1=""; print substr($0,2)}')
|
|
307
|
+
if [[ "$lcount" =~ ^[0-9]+$ ]] && [[ "$lcount" -gt 1500 ]]; then
|
|
308
|
+
local base
|
|
309
|
+
base=$(basename "$file")
|
|
310
|
+
findings+=("Refactor ${base} (${lcount} lines) into smaller modules|refactor|7")
|
|
311
|
+
fi
|
|
312
|
+
done < <(find "$repo_root/scripts" -name "*.sh" -not -name "*-test.sh" -exec wc -l {} \; 2>/dev/null | sort -rn | head -5)
|
|
313
|
+
|
|
314
|
+
# 3. Check for shellcheck warnings
|
|
315
|
+
if command -v shellcheck &>/dev/null; then
|
|
316
|
+
local sc_warnings
|
|
317
|
+
sc_warnings=$(shellcheck -S warning "$repo_root"/scripts/sw-*.sh 2>/dev/null | grep -c "^In " || true)
|
|
318
|
+
if [[ "${sc_warnings:-0}" -gt 10 ]]; then
|
|
319
|
+
findings+=("Fix ${sc_warnings} shellcheck warnings across scripts|quality|4")
|
|
320
|
+
fi
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
# 4. Check for TODO/FIXME in production code
|
|
324
|
+
local todo_count
|
|
325
|
+
todo_count=$(find "$repo_root/scripts" -name "sw-*.sh" ! -name "*-test.sh" -exec grep -oh 'TODO\|FIXME\|HACK\|XXX' {} \; 2>/dev/null | wc -l | tr -d ' ')
|
|
326
|
+
if [[ "${todo_count:-0}" -gt 5 ]]; then
|
|
327
|
+
findings+=("Address ${todo_count} TODO/FIXME items in production scripts|quality|3")
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
# 5. Check recent failures from events
|
|
331
|
+
local events_file="$HOME/.shipwright/events.jsonl"
|
|
332
|
+
if [[ -f "$events_file" ]]; then
|
|
333
|
+
local recent_failures
|
|
334
|
+
recent_failures=$(tail -200 "$events_file" 2>/dev/null | grep '"result":"failure"' | wc -l | tr -d ' ')
|
|
335
|
+
if [[ "${recent_failures:-0}" -gt 3 ]]; then
|
|
336
|
+
local common_stage
|
|
337
|
+
common_stage=$(tail -200 "$events_file" 2>/dev/null | grep '"result":"failure"' | grep -oE '"stage":"[^"]*"' | sort | uniq -c | sort -rn | head -1 | sed 's/.*"stage":"\([^"]*\)".*/\1/' || true)
|
|
338
|
+
findings+=("Investigate ${recent_failures} recent pipeline failures${common_stage:+ (common: ${common_stage})}|bug|6")
|
|
339
|
+
fi
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
# Output findings (at most 5) — pipe-delimited: title|type|complexity
|
|
343
|
+
local count=0
|
|
344
|
+
for f in "${findings[@]}"; do
|
|
345
|
+
[[ "$count" -ge 5 ]] && break
|
|
346
|
+
echo "$f"
|
|
347
|
+
count=$((count + 1))
|
|
348
|
+
done
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# Convert pipe-delimited heuristic output (title|type|complexity) to JSON array.
|
|
352
|
+
autonomous_heuristic_to_json() {
|
|
353
|
+
local json="[]"
|
|
354
|
+
while IFS= read -r line; do
|
|
355
|
+
[[ -z "$line" ]] || [[ "$line" != *"|"* ]] && continue
|
|
356
|
+
local title type complexity
|
|
357
|
+
title="${line%%|*}"
|
|
358
|
+
line="${line#*|}"
|
|
359
|
+
type="${line%%|*}"
|
|
360
|
+
complexity="${line##*|}"
|
|
361
|
+
local effort="M"
|
|
362
|
+
[[ "$complexity" =~ ^[0-9]+$ ]] && {
|
|
363
|
+
[[ "$complexity" -le 4 ]] && effort="S"
|
|
364
|
+
[[ "$complexity" -ge 7 ]] && effort="L"
|
|
365
|
+
}
|
|
366
|
+
json=$(echo "$json" | jq -c --arg t "$title" --arg c "$type" --arg e "$effort" \
|
|
367
|
+
'. + [{title: $t, description: $t, priority: "medium", effort: $e, labels: [$c], category: $c}]' 2>/dev/null || echo "$json")
|
|
368
|
+
done
|
|
369
|
+
echo "$json"
|
|
370
|
+
}
|
|
371
|
+
|
|
288
372
|
# ─── Analysis Cycle ────────────────────────────────────────────────────────
|
|
289
373
|
|
|
290
374
|
run_analysis_cycle() {
|
|
@@ -297,7 +381,7 @@ run_analysis_cycle() {
|
|
|
297
381
|
findings=$(mktemp "${TMPDIR:-/tmp}/sw-autonomous-findings.XXXXXX")
|
|
298
382
|
|
|
299
383
|
# Use Claude to analyze the codebase
|
|
300
|
-
if command -v claude
|
|
384
|
+
if command -v claude >/dev/null 2>&1; then
|
|
301
385
|
info "Running codebase analysis with Claude..."
|
|
302
386
|
|
|
303
387
|
claude -p 'You are Shipwright'"'"'s autonomous PM. Analyze this repository for:
|
|
@@ -313,35 +397,13 @@ For each finding, output JSON with fields: title, description, priority (critica
|
|
|
313
397
|
Output ONLY a JSON array, no other text.' --max-turns 3 > "$findings" 2>/dev/null || true
|
|
314
398
|
|
|
315
399
|
else
|
|
316
|
-
warn "Claude CLI not available, using
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
jq -n \
|
|
324
|
-
--argjson test_count "$has_tests" \
|
|
325
|
-
--argjson script_count "$shell_scripts" \
|
|
326
|
-
'[
|
|
327
|
-
{
|
|
328
|
-
title: "Add comprehensive test coverage for critical paths",
|
|
329
|
-
description: "Several scripts lack unit test coverage",
|
|
330
|
-
priority: "high",
|
|
331
|
-
effort: "L",
|
|
332
|
-
labels: ["test", "quality"],
|
|
333
|
-
category: "test"
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
title: "Simplify error handling in daemon.sh",
|
|
337
|
-
description: "Daemon error handling could be more robust",
|
|
338
|
-
priority: "medium",
|
|
339
|
-
effort: "M",
|
|
340
|
-
labels: ["refactor", "self-improvement"],
|
|
341
|
-
category: "self-improvement"
|
|
342
|
-
}
|
|
343
|
-
]'
|
|
344
|
-
} > "$findings"
|
|
400
|
+
warn "Claude CLI not available, using codebase-aware analysis..."
|
|
401
|
+
|
|
402
|
+
autonomous_heuristic_analysis | autonomous_heuristic_to_json > "$findings"
|
|
403
|
+
if [[ ! -s "$findings" ]] || [[ "$(cat "$findings")" == "[]" ]]; then
|
|
404
|
+
# No findings from heuristic analysis
|
|
405
|
+
echo "[]" > "$findings"
|
|
406
|
+
fi
|
|
345
407
|
fi
|
|
346
408
|
|
|
347
409
|
# Ingest strategic findings and merge with Claude/heuristic findings
|
|
@@ -354,10 +416,10 @@ Output ONLY a JSON array, no other text.' --max-turns 3 > "$findings" 2>/dev/nul
|
|
|
354
416
|
local claude_array
|
|
355
417
|
claude_array=$(jq -c '.' "$findings" 2>/dev/null || echo "[]")
|
|
356
418
|
# Extract JSON array from potential markdown-wrapped output
|
|
357
|
-
if ! echo "$claude_array" | jq -e 'type == "array"'
|
|
419
|
+
if ! echo "$claude_array" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
358
420
|
claude_array=$(sed -n '/^\[/,/^\]/p' "$findings" 2>/dev/null | jq -c '.' 2>/dev/null || echo "[]")
|
|
359
421
|
fi
|
|
360
|
-
if ! echo "$claude_array" | jq -e 'type == "array"'
|
|
422
|
+
if ! echo "$claude_array" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
361
423
|
claude_array="[]"
|
|
362
424
|
fi
|
|
363
425
|
|
|
@@ -485,13 +547,13 @@ trigger_pipeline_for_finding() {
|
|
|
485
547
|
fi
|
|
486
548
|
|
|
487
549
|
if [[ "$daemon_aware" == "true" ]] && daemon_is_running; then
|
|
488
|
-
# Daemon running: label ready-to-build
|
|
550
|
+
# Daemon running: add shipwright (daemon watch label) + ready-to-build (status), let daemon pick it up
|
|
489
551
|
if [[ "$NO_GITHUB" != "true" ]]; then
|
|
490
|
-
gh issue edit "$issue_num" --add-label "ready-to-build" 2>/dev/null || {
|
|
491
|
-
warn "Failed to add ready-to-build
|
|
552
|
+
gh issue edit "$issue_num" --add-label "shipwright" --add-label "ready-to-build" 2>/dev/null || {
|
|
553
|
+
warn "Failed to add shipwright/ready-to-build labels to #${issue_num}"
|
|
492
554
|
}
|
|
493
555
|
fi
|
|
494
|
-
info "Delegated issue #${issue_num} to daemon (labeled ready-to-build)"
|
|
556
|
+
info "Delegated issue #${issue_num} to daemon (labeled shipwright, ready-to-build)"
|
|
495
557
|
emit_event "autonomous.delegated_to_daemon" "issue=$issue_num" "title=$title"
|
|
496
558
|
return 0
|
|
497
559
|
fi
|
|
@@ -597,7 +659,7 @@ process_findings() {
|
|
|
597
659
|
|
|
598
660
|
# Strategic findings: issue already created by strategic agent; trigger pipeline and register, skip create
|
|
599
661
|
if [[ "$source" == "strategic" ]]; then
|
|
600
|
-
[[ -z "$issue_num" && "${NO_GITHUB:-false}" != "true" ]] && command -v gh
|
|
662
|
+
[[ -z "$issue_num" && "${NO_GITHUB:-false}" != "true" ]] && command -v gh >/dev/null 2>&1 && \
|
|
601
663
|
issue_num=$(gh issue list --state open --search "$title" --json number -q '.[0].number' 2>/dev/null || echo "")
|
|
602
664
|
if [[ -n "$issue_num" && "$issue_num" =~ ^[0-9]+$ ]]; then
|
|
603
665
|
trigger_pipeline_for_finding "$issue_num" "$title"
|
package/scripts/sw-changelog.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
9
|
|
|
10
|
-
VERSION="
|
|
10
|
+
VERSION="3.0.0"
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
12
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
13
13
|
|
|
@@ -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
|
# ─── Commit Parsing ──────────────────────────────────────────────────────────
|
|
48
38
|
|
|
49
39
|
# Extract conventional commit type (feat, fix, perf, security, etc.)
|
package/scripts/sw-checkpoint.sh
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
10
10
|
|
|
11
|
-
VERSION="
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
VERSION="3.0.0"
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
13
13
|
|
|
14
14
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
15
15
|
# shellcheck source=lib/compat.sh
|
|
@@ -35,15 +35,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
35
35
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
36
36
|
}
|
|
37
37
|
fi
|
|
38
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
39
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
40
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
41
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
42
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
43
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
44
|
-
DIM="${DIM:-\033[2m}"
|
|
45
|
-
BOLD="${BOLD:-\033[1m}"
|
|
46
|
-
RESET="${RESET:-\033[0m}"
|
|
47
38
|
|
|
48
39
|
# ─── Checkpoint Directory ───────────────────────────────────────────────────
|
|
49
40
|
CHECKPOINT_DIR=".claude/pipeline-artifacts/checkpoints"
|
|
@@ -57,6 +48,102 @@ checkpoint_file() {
|
|
|
57
48
|
echo "${CHECKPOINT_DIR}/${stage}-checkpoint.json"
|
|
58
49
|
}
|
|
59
50
|
|
|
51
|
+
# Context file for Claude prompt reconstruction on resume
|
|
52
|
+
claude_context_file() {
|
|
53
|
+
local stage="$1"
|
|
54
|
+
echo "${CHECKPOINT_DIR}/${stage}-claude-context.json"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# ─── Save/Restore Claude Context (for meaningful resume) ────────────────────────
|
|
58
|
+
# Captures goal, findings, modified files, test output so resume can reconstruct prompt.
|
|
59
|
+
checkpoint_save_context() {
|
|
60
|
+
local stage="${1:-build}"
|
|
61
|
+
local context_file
|
|
62
|
+
context_file="$(claude_context_file "$stage")"
|
|
63
|
+
ensure_checkpoint_dir
|
|
64
|
+
|
|
65
|
+
local context="{}"
|
|
66
|
+
|
|
67
|
+
# Save current goal (from env or file)
|
|
68
|
+
local goal_content=""
|
|
69
|
+
if [[ -n "${SW_LOOP_GOAL:-}" ]]; then
|
|
70
|
+
goal_content="${SW_LOOP_GOAL}"
|
|
71
|
+
elif [[ -n "${SW_LOOP_GOAL_FILE:-}" && -f "${SW_LOOP_GOAL_FILE:-}" ]]; then
|
|
72
|
+
goal_content="$(cat "${SW_LOOP_GOAL_FILE}" 2>/dev/null || true)"
|
|
73
|
+
fi
|
|
74
|
+
if [[ -n "$goal_content" ]]; then
|
|
75
|
+
context=$(printf '%s' "$context" | jq --arg g "$goal_content" '.goal = $g' 2>/dev/null || echo "$context")
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Save accumulated findings
|
|
79
|
+
local findings_content=""
|
|
80
|
+
if [[ -n "${SW_LOOP_FINDINGS:-}" ]]; then
|
|
81
|
+
findings_content="${SW_LOOP_FINDINGS}"
|
|
82
|
+
elif [[ -n "${SW_LOOP_FINDINGS_FILE:-}" && -f "${SW_LOOP_FINDINGS_FILE:-}" ]]; then
|
|
83
|
+
findings_content="$(cat "${SW_LOOP_FINDINGS_FILE}" 2>/dev/null || true)"
|
|
84
|
+
fi
|
|
85
|
+
if [[ -n "$findings_content" ]]; then
|
|
86
|
+
context=$(printf '%s' "$context" | jq --arg f "$findings_content" '.findings = $f' 2>/dev/null || echo "$context")
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Save files modified (git diff)
|
|
90
|
+
local modified
|
|
91
|
+
modified="$(git diff --name-only HEAD 2>/dev/null | head -50 || true)"
|
|
92
|
+
if [[ -z "$modified" ]]; then
|
|
93
|
+
modified="${SW_LOOP_MODIFIED:-}"
|
|
94
|
+
fi
|
|
95
|
+
context=$(printf '%s' "$context" | jq --arg m "${modified:-}" '.modified_files = $m' 2>/dev/null || echo "$context")
|
|
96
|
+
|
|
97
|
+
# Save last test output
|
|
98
|
+
local test_content=""
|
|
99
|
+
if [[ -n "${SW_LOOP_TEST_OUTPUT:-}" ]]; then
|
|
100
|
+
test_content="${SW_LOOP_TEST_OUTPUT}"
|
|
101
|
+
elif [[ -n "${SW_LOOP_TEST_OUTPUT_FILE:-}" && -f "${SW_LOOP_TEST_OUTPUT_FILE:-}" ]]; then
|
|
102
|
+
test_content="$(tail -100 "${SW_LOOP_TEST_OUTPUT_FILE}" 2>/dev/null || true)"
|
|
103
|
+
fi
|
|
104
|
+
if [[ -n "$test_content" ]]; then
|
|
105
|
+
context=$(printf '%s' "$context" | jq --arg t "$test_content" '.last_test_output = $t' 2>/dev/null || echo "$context")
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Save iteration count and status
|
|
109
|
+
local iter="${SW_LOOP_ITERATION:-0}"
|
|
110
|
+
local status="${SW_LOOP_STATUS:-running}"
|
|
111
|
+
context=$(printf '%s' "$context" | jq --argjson i "${iter}" --arg s "$status" '.iteration = ($i | tonumber) | .status = $s' 2>/dev/null || echo "$context")
|
|
112
|
+
|
|
113
|
+
printf '%s' "$context" > "$context_file" 2>/dev/null || true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
checkpoint_restore_context() {
|
|
117
|
+
local stage="${1:-build}"
|
|
118
|
+
local context_file
|
|
119
|
+
context_file="$(claude_context_file "$stage")"
|
|
120
|
+
|
|
121
|
+
[[ ! -f "$context_file" ]] && return 1
|
|
122
|
+
|
|
123
|
+
local goal findings modified test_output iter status
|
|
124
|
+
goal="$(jq -r '.goal // empty' "$context_file" 2>/dev/null)"
|
|
125
|
+
findings="$(jq -r '.findings // empty' "$context_file" 2>/dev/null)"
|
|
126
|
+
modified="$(jq -r '.modified_files // empty' "$context_file" 2>/dev/null)"
|
|
127
|
+
test_output="$(jq -r '.last_test_output // empty' "$context_file" 2>/dev/null)"
|
|
128
|
+
iter="$(jq -r '.iteration // 0' "$context_file" 2>/dev/null)"
|
|
129
|
+
status="$(jq -r '.status // empty' "$context_file" 2>/dev/null)"
|
|
130
|
+
|
|
131
|
+
# Export both RESTORED_* (for restore-context CLI) and SW_LOOP_* (for sw-loop.sh)
|
|
132
|
+
export RESTORED_GOAL="$goal"
|
|
133
|
+
export RESTORED_FINDINGS="$findings"
|
|
134
|
+
export RESTORED_MODIFIED="$modified"
|
|
135
|
+
export RESTORED_TEST_OUTPUT="$test_output"
|
|
136
|
+
export RESTORED_ITERATION="$iter"
|
|
137
|
+
export RESTORED_STATUS="$status"
|
|
138
|
+
export SW_LOOP_GOAL="$goal"
|
|
139
|
+
export SW_LOOP_FINDINGS="$findings"
|
|
140
|
+
export SW_LOOP_MODIFIED="$modified"
|
|
141
|
+
export SW_LOOP_TEST_OUTPUT="$test_output"
|
|
142
|
+
export SW_LOOP_ITERATION="$iter"
|
|
143
|
+
export SW_LOOP_STATUS="$status"
|
|
144
|
+
return 0
|
|
145
|
+
}
|
|
146
|
+
|
|
60
147
|
# ─── Save ────────────────────────────────────────────────────────────────────
|
|
61
148
|
|
|
62
149
|
cmd_save() {
|
|
@@ -314,7 +401,10 @@ cmd_clear() {
|
|
|
314
401
|
local file
|
|
315
402
|
for file in "${CHECKPOINT_DIR}"/*-checkpoint.json; do
|
|
316
403
|
[[ -f "$file" ]] || continue
|
|
404
|
+
local stage_name
|
|
405
|
+
stage_name="$(basename "$file" -checkpoint.json)"
|
|
317
406
|
rm -f "$file"
|
|
407
|
+
rm -f "$(claude_context_file "$stage_name")" 2>/dev/null || true
|
|
318
408
|
count=$((count + 1))
|
|
319
409
|
done
|
|
320
410
|
success "Cleared ${count} checkpoint(s)"
|
|
@@ -334,6 +424,7 @@ cmd_clear() {
|
|
|
334
424
|
|
|
335
425
|
if [[ -f "$target" ]]; then
|
|
336
426
|
rm -f "$target"
|
|
427
|
+
rm -f "$(claude_context_file "$stage")" 2>/dev/null || true
|
|
337
428
|
success "Cleared checkpoint for stage ${BOLD}${stage}${RESET}"
|
|
338
429
|
else
|
|
339
430
|
warn "No checkpoint found for stage ${BOLD}${stage}${RESET}"
|
|
@@ -399,7 +490,7 @@ cmd_expire() {
|
|
|
399
490
|
else
|
|
400
491
|
# Fallback: check file mtime
|
|
401
492
|
local mtime
|
|
402
|
-
mtime=$(
|
|
493
|
+
mtime=$(file_mtime "$file")
|
|
403
494
|
if [[ "$mtime" -gt 0 && $((now_e - mtime)) -gt $max_secs ]]; then
|
|
404
495
|
rm -f "$file"
|
|
405
496
|
expired=$((expired + 1))
|
|
@@ -421,8 +512,10 @@ show_help() {
|
|
|
421
512
|
echo -e " ${CYAN}shipwright checkpoint${RESET} <command> [options]"
|
|
422
513
|
echo ""
|
|
423
514
|
echo -e "${BOLD}COMMANDS${RESET}"
|
|
424
|
-
echo -e " ${CYAN}save${RESET}
|
|
425
|
-
echo -e " ${CYAN}restore${RESET}
|
|
515
|
+
echo -e " ${CYAN}save${RESET} Save a checkpoint for a stage"
|
|
516
|
+
echo -e " ${CYAN}restore${RESET} Restore a checkpoint (prints JSON to stdout)"
|
|
517
|
+
echo -e " ${CYAN}save-context${RESET} Save Claude context (goal, findings, test output) for resume"
|
|
518
|
+
echo -e " ${CYAN}restore-context${RESET} Restore Claude context (exports RESTORED_* and SW_LOOP_* vars)"
|
|
426
519
|
echo -e " ${CYAN}list${RESET} Show all available checkpoints"
|
|
427
520
|
echo -e " ${CYAN}clear${RESET} Remove checkpoint(s)"
|
|
428
521
|
echo -e " ${CYAN}expire${RESET} Remove checkpoints older than N hours"
|
|
@@ -455,6 +548,32 @@ show_help() {
|
|
|
455
548
|
echo -e " ${DIM}shipwright checkpoint expire --hours 48${RESET}"
|
|
456
549
|
}
|
|
457
550
|
|
|
551
|
+
# ─── Save Context / Restore Context (subcommands) ─────────────────────────────
|
|
552
|
+
|
|
553
|
+
cmd_save_context() {
|
|
554
|
+
local stage="build"
|
|
555
|
+
while [[ $# -gt 0 ]]; do
|
|
556
|
+
case "$1" in
|
|
557
|
+
--stage) stage="${2:-build}"; shift 2 ;;
|
|
558
|
+
--stage=*) stage="${1#--stage=}"; shift ;;
|
|
559
|
+
*) shift ;;
|
|
560
|
+
esac
|
|
561
|
+
done
|
|
562
|
+
checkpoint_save_context "$stage"
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
cmd_restore_context() {
|
|
566
|
+
local stage="build"
|
|
567
|
+
while [[ $# -gt 0 ]]; do
|
|
568
|
+
case "$1" in
|
|
569
|
+
--stage) stage="${2:-build}"; shift 2 ;;
|
|
570
|
+
--stage=*) stage="${1#--stage=}"; shift ;;
|
|
571
|
+
*) shift ;;
|
|
572
|
+
esac
|
|
573
|
+
done
|
|
574
|
+
checkpoint_restore_context "$stage"
|
|
575
|
+
}
|
|
576
|
+
|
|
458
577
|
# ─── Command Router ─────────────────────────────────────────────────────────
|
|
459
578
|
|
|
460
579
|
main() {
|
|
@@ -462,11 +581,13 @@ main() {
|
|
|
462
581
|
shift 2>/dev/null || true
|
|
463
582
|
|
|
464
583
|
case "$cmd" in
|
|
465
|
-
save)
|
|
466
|
-
restore)
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
584
|
+
save) cmd_save "$@" ;;
|
|
585
|
+
restore) cmd_restore "$@" ;;
|
|
586
|
+
save-context) cmd_save_context "$@" ;;
|
|
587
|
+
restore-context) cmd_restore_context "$@" ;;
|
|
588
|
+
list) cmd_list ;;
|
|
589
|
+
clear) cmd_clear "$@" ;;
|
|
590
|
+
expire) cmd_expire "$@" ;;
|
|
470
591
|
help|--help|-h) show_help ;;
|
|
471
592
|
*)
|
|
472
593
|
error "Unknown command: ${cmd}"
|
|
@@ -477,4 +598,7 @@ main() {
|
|
|
477
598
|
esac
|
|
478
599
|
}
|
|
479
600
|
|
|
480
|
-
main
|
|
601
|
+
# Only run main when executed directly (not when sourced)
|
|
602
|
+
if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
603
|
+
main "$@"
|
|
604
|
+
fi
|
package/scripts/sw-ci.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
|
# ─── Generate Workflow YAML from Pipeline Template ──────────────────────────
|
|
48
38
|
|
|
49
39
|
cmd_generate() {
|
|
@@ -464,7 +454,7 @@ cmd_validate() {
|
|
|
464
454
|
info "Validating workflow YAML: $workflow_file"
|
|
465
455
|
|
|
466
456
|
# Check for valid YAML structure
|
|
467
|
-
if ! jq -e 'type' <<< "$(yq eval -o=json "$workflow_file" 2>/dev/null || echo '{}')"
|
|
457
|
+
if ! jq -e 'type' <<< "$(yq eval -o=json "$workflow_file" 2>/dev/null || echo '{}')" >/dev/null 2>&1; then
|
|
468
458
|
# Fallback: basic validation
|
|
469
459
|
if grep -q "^name:" "$workflow_file" && grep -q "^on:" "$workflow_file" && grep -q "^jobs:" "$workflow_file"; then
|
|
470
460
|
success "Workflow structure looks valid"
|
package/scripts/sw-cleanup.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ║ Default: dry-run (shows what would be cleaned). ║
|
|
6
6
|
# ║ Use --force to actually kill sessions and remove files. ║
|
|
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
|
|
|
@@ -14,6 +14,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
14
14
|
# Canonical helpers (colors, output, events)
|
|
15
15
|
# shellcheck source=lib/helpers.sh
|
|
16
16
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
17
|
+
# shellcheck source=lib/compat.sh
|
|
18
|
+
[[ -f "$SCRIPT_DIR/lib/compat.sh" ]] && source "$SCRIPT_DIR/lib/compat.sh"
|
|
19
|
+
[[ -f "$SCRIPT_DIR/lib/config.sh" ]] && source "$SCRIPT_DIR/lib/config.sh"
|
|
20
|
+
|
|
21
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
22
|
+
|
|
17
23
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
18
24
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
19
25
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -31,16 +37,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
31
37
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
32
38
|
}
|
|
33
39
|
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
40
|
# ─── Parse Args ──────────────────────────────────────────────────────────────
|
|
45
41
|
|
|
46
42
|
FORCE=false
|
|
@@ -181,7 +177,7 @@ echo ""
|
|
|
181
177
|
echo -e "${BOLD}Pipeline Artifacts${RESET} ${DIM}.claude/pipeline-artifacts/${RESET}"
|
|
182
178
|
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
183
179
|
|
|
184
|
-
PIPELINE_ARTIFACTS="
|
|
180
|
+
PIPELINE_ARTIFACTS="$PROJECT_ROOT/.claude/pipeline-artifacts"
|
|
185
181
|
if [[ -d "$PIPELINE_ARTIFACTS" ]]; then
|
|
186
182
|
artifact_file_count=$(find "$PIPELINE_ARTIFACTS" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
187
183
|
if [[ "${artifact_file_count:-0}" -gt 0 ]]; then
|
|
@@ -211,7 +207,7 @@ echo ""
|
|
|
211
207
|
echo -e "${BOLD}Checkpoints${RESET} ${DIM}.claude/pipeline-artifacts/checkpoints/${RESET}"
|
|
212
208
|
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
213
209
|
|
|
214
|
-
CHECKPOINT_DIR="
|
|
210
|
+
CHECKPOINT_DIR="$PROJECT_ROOT/.claude/pipeline-artifacts/checkpoints"
|
|
215
211
|
if [[ -d "$CHECKPOINT_DIR" ]]; then
|
|
216
212
|
cp_file_count=0
|
|
217
213
|
for cp_file in "${CHECKPOINT_DIR}"/*-checkpoint.json; do
|
|
@@ -242,7 +238,7 @@ echo ""
|
|
|
242
238
|
echo -e "${BOLD}Pipeline State${RESET} ${DIM}.claude/pipeline-state.md${RESET}"
|
|
243
239
|
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
244
240
|
|
|
245
|
-
PIPELINE_STATE="
|
|
241
|
+
PIPELINE_STATE="$PROJECT_ROOT/.claude/pipeline-state.md"
|
|
246
242
|
if [[ -f "$PIPELINE_STATE" ]]; then
|
|
247
243
|
state_status=$(sed -n 's/^status: *//p' "$PIPELINE_STATE" | head -1 || true)
|
|
248
244
|
state_issue=$(sed -n 's/^issue: *//p' "$PIPELINE_STATE" | head -1 || true)
|
|
@@ -277,11 +273,11 @@ echo -e "${DIM}─────────────────────
|
|
|
277
273
|
HEARTBEAT_DIR="${HOME}/.shipwright/heartbeats"
|
|
278
274
|
if [[ -d "$HEARTBEAT_DIR" ]]; then
|
|
279
275
|
now_e=$(date +%s)
|
|
280
|
-
stale_threshold
|
|
276
|
+
stale_threshold=$(_config_get_int "cleanup.heartbeat_stale_seconds" 3600)
|
|
281
277
|
|
|
282
278
|
while IFS= read -r hb_file; do
|
|
283
279
|
[[ -f "$hb_file" ]] || continue
|
|
284
|
-
hb_mtime=$(
|
|
280
|
+
hb_mtime=$(file_mtime "$hb_file")
|
|
285
281
|
if [[ $((now_e - hb_mtime)) -gt $stale_threshold ]]; then
|
|
286
282
|
HEARTBEATS_FOUND=$((HEARTBEATS_FOUND + 1))
|
|
287
283
|
hb_name=$(basename "$hb_file" .json)
|
|
@@ -310,7 +306,7 @@ echo ""
|
|
|
310
306
|
echo -e "${BOLD}Orphaned Branches${RESET} ${DIM}pipeline/* and daemon/*${RESET}"
|
|
311
307
|
echo -e "${DIM}────────────────────────────────────────${RESET}"
|
|
312
308
|
|
|
313
|
-
if command -v git
|
|
309
|
+
if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
314
310
|
# Collect active worktree paths
|
|
315
311
|
active_worktrees=""
|
|
316
312
|
while IFS= read -r wt_line; do
|