shipwright-cli 3.0.0 → 3.1.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 +3 -3
- package/completions/_shipwright +247 -93
- package/completions/shipwright.bash +69 -15
- package/completions/shipwright.fish +309 -41
- package/config/decision-tiers.json +55 -0
- package/config/event-schema.json +142 -5
- package/config/policy.json +8 -0
- package/package.json +3 -3
- package/scripts/lib/architecture.sh +2 -1
- package/scripts/lib/bootstrap.sh +0 -0
- package/scripts/lib/config.sh +0 -0
- package/scripts/lib/daemon-adaptive.sh +0 -0
- package/scripts/lib/daemon-dispatch.sh +24 -1
- package/scripts/lib/daemon-failure.sh +0 -0
- package/scripts/lib/daemon-health.sh +0 -0
- package/scripts/lib/daemon-patrol.sh +40 -5
- package/scripts/lib/daemon-poll.sh +17 -0
- package/scripts/lib/daemon-state.sh +10 -0
- package/scripts/lib/daemon-triage.sh +1 -1
- package/scripts/lib/decide-autonomy.sh +295 -0
- package/scripts/lib/decide-scoring.sh +228 -0
- package/scripts/lib/decide-signals.sh +462 -0
- package/scripts/lib/fleet-failover.sh +0 -0
- package/scripts/lib/helpers.sh +16 -17
- package/scripts/lib/pipeline-detection.sh +0 -0
- package/scripts/lib/pipeline-github.sh +0 -0
- package/scripts/lib/pipeline-intelligence.sh +20 -3
- package/scripts/lib/pipeline-quality-checks.sh +3 -2
- package/scripts/lib/pipeline-quality.sh +0 -0
- package/scripts/lib/pipeline-stages.sh +199 -32
- package/scripts/lib/pipeline-state.sh +14 -0
- package/scripts/lib/policy.sh +0 -0
- package/scripts/lib/test-helpers.sh +0 -0
- package/scripts/postinstall.mjs +75 -1
- package/scripts/signals/example-collector.sh +36 -0
- package/scripts/sw +8 -4
- package/scripts/sw-activity.sh +1 -1
- package/scripts/sw-adaptive.sh +1 -1
- package/scripts/sw-adversarial.sh +1 -1
- package/scripts/sw-architecture-enforcer.sh +1 -1
- package/scripts/sw-auth.sh +1 -1
- package/scripts/sw-autonomous.sh +1 -1
- package/scripts/sw-changelog.sh +1 -1
- package/scripts/sw-checkpoint.sh +1 -1
- package/scripts/sw-ci.sh +1 -1
- package/scripts/sw-cleanup.sh +1 -1
- package/scripts/sw-code-review.sh +1 -1
- package/scripts/sw-connect.sh +1 -1
- package/scripts/sw-context.sh +1 -1
- package/scripts/sw-cost.sh +12 -3
- package/scripts/sw-daemon.sh +2 -2
- package/scripts/sw-dashboard.sh +1 -1
- package/scripts/sw-db.sh +41 -34
- package/scripts/sw-decide.sh +685 -0
- package/scripts/sw-decompose.sh +1 -1
- package/scripts/sw-deps.sh +1 -1
- package/scripts/sw-developer-simulation.sh +1 -1
- package/scripts/sw-discovery.sh +27 -1
- package/scripts/sw-doc-fleet.sh +1 -1
- package/scripts/sw-docs-agent.sh +1 -1
- package/scripts/sw-docs.sh +1 -1
- package/scripts/sw-doctor.sh +1 -1
- package/scripts/sw-dora.sh +1 -1
- package/scripts/sw-durable.sh +1 -1
- package/scripts/sw-e2e-orchestrator.sh +1 -1
- package/scripts/sw-eventbus.sh +1 -1
- package/scripts/sw-evidence.sh +1 -1
- package/scripts/sw-feedback.sh +1 -1
- package/scripts/sw-fix.sh +1 -1
- package/scripts/sw-fleet-discover.sh +1 -1
- package/scripts/sw-fleet-viz.sh +1 -1
- package/scripts/sw-fleet.sh +1 -1
- package/scripts/sw-github-app.sh +1 -1
- package/scripts/sw-github-checks.sh +1 -1
- package/scripts/sw-github-deploy.sh +1 -1
- package/scripts/sw-github-graphql.sh +1 -1
- package/scripts/sw-guild.sh +1 -1
- package/scripts/sw-heartbeat.sh +1 -1
- package/scripts/sw-hygiene.sh +1 -1
- package/scripts/sw-incident.sh +1 -1
- package/scripts/sw-init.sh +1 -1
- package/scripts/sw-instrument.sh +1 -1
- package/scripts/sw-intelligence.sh +9 -5
- package/scripts/sw-jira.sh +1 -1
- package/scripts/sw-launchd.sh +1 -1
- package/scripts/sw-linear.sh +1 -1
- package/scripts/sw-logs.sh +1 -1
- package/scripts/sw-loop.sh +267 -17
- package/scripts/sw-memory.sh +22 -5
- package/scripts/sw-mission-control.sh +1 -1
- package/scripts/sw-model-router.sh +1 -1
- package/scripts/sw-otel.sh +5 -3
- package/scripts/sw-oversight.sh +1 -1
- package/scripts/sw-pipeline-composer.sh +1 -1
- package/scripts/sw-pipeline-vitals.sh +1 -1
- package/scripts/sw-pipeline.sh +73 -1
- package/scripts/sw-pm.sh +1 -1
- package/scripts/sw-pr-lifecycle.sh +7 -4
- package/scripts/sw-predictive.sh +1 -1
- package/scripts/sw-prep.sh +1 -1
- package/scripts/sw-ps.sh +1 -1
- package/scripts/sw-public-dashboard.sh +1 -1
- package/scripts/sw-quality.sh +9 -5
- package/scripts/sw-reaper.sh +1 -1
- package/scripts/sw-regression.sh +1 -1
- package/scripts/sw-release-manager.sh +1 -1
- package/scripts/sw-release.sh +1 -1
- package/scripts/sw-remote.sh +1 -1
- package/scripts/sw-replay.sh +1 -1
- package/scripts/sw-retro.sh +1 -1
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +66 -10
- package/scripts/sw-security-audit.sh +1 -1
- package/scripts/sw-self-optimize.sh +1 -1
- package/scripts/sw-session.sh +3 -3
- package/scripts/sw-setup.sh +1 -1
- package/scripts/sw-standup.sh +1 -1
- package/scripts/sw-status.sh +1 -1
- package/scripts/sw-strategic.sh +1 -1
- package/scripts/sw-stream.sh +1 -1
- package/scripts/sw-swarm.sh +1 -1
- package/scripts/sw-team-stages.sh +1 -1
- package/scripts/sw-templates.sh +1 -1
- package/scripts/sw-testgen.sh +1 -1
- package/scripts/sw-tmux-pipeline.sh +1 -1
- package/scripts/sw-tmux.sh +1 -1
- package/scripts/sw-trace.sh +1 -1
- package/scripts/sw-tracker.sh +1 -1
- package/scripts/sw-triage.sh +6 -6
- package/scripts/sw-upgrade.sh +1 -1
- package/scripts/sw-ux.sh +1 -1
- package/scripts/sw-webhook.sh +1 -1
- package/scripts/sw-widgets.sh +1 -1
- package/scripts/sw-worktree.sh +1 -1
- package/scripts/update-homebrew-sha.sh +21 -15
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
# pipeline-stages.sh — Stage implementations (intake, plan, build, test, review, pr, merge, deploy, validate, monitor) for sw-pipeline.sh
|
|
1
|
+
# pipeline-stages.sh — Stage implementations (intake, plan, build, test, review, compound_quality, pr, merge, deploy, validate, monitor) for sw-pipeline.sh
|
|
2
2
|
# Source from sw-pipeline.sh. Requires all pipeline globals and state/github/detection/quality modules.
|
|
3
3
|
[[ -n "${_PIPELINE_STAGES_LOADED:-}" ]] && return 0
|
|
4
4
|
_PIPELINE_STAGES_LOADED=1
|
|
5
5
|
|
|
6
|
+
# ─── Safe git helpers ────────────────────────────────────────────────────────
|
|
7
|
+
# BASE_BRANCH may not exist locally (e.g. --local mode with no remote).
|
|
8
|
+
# These helpers return empty output instead of crashing under set -euo pipefail.
|
|
9
|
+
_safe_base_log() {
|
|
10
|
+
local branch="${BASE_BRANCH:-main}"
|
|
11
|
+
git rev-parse --verify "$branch" >/dev/null 2>&1 || { echo ""; return 0; }
|
|
12
|
+
git log "$@" "${branch}..HEAD" 2>/dev/null || true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
_safe_base_diff() {
|
|
16
|
+
local branch="${BASE_BRANCH:-main}"
|
|
17
|
+
git rev-parse --verify "$branch" >/dev/null 2>&1 || { git diff HEAD~5 "$@" 2>/dev/null || true; return 0; }
|
|
18
|
+
git diff "${branch}...HEAD" "$@" 2>/dev/null || true
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
show_stage_preview() {
|
|
7
22
|
local stage_id="$1"
|
|
8
23
|
echo ""
|
|
@@ -15,6 +30,7 @@ show_stage_preview() {
|
|
|
15
30
|
test_first) echo -e " Generate tests from requirements (TDD mode) before implementation" ;;
|
|
16
31
|
test) echo -e " Run test suite and check coverage" ;;
|
|
17
32
|
review) echo -e " AI code review on the diff, post findings" ;;
|
|
33
|
+
compound_quality) echo -e " Adversarial review, negative tests, e2e, DoD audit" ;;
|
|
18
34
|
pr) echo -e " Create GitHub PR with labels, reviewers, milestone" ;;
|
|
19
35
|
merge) echo -e " Wait for CI checks, merge PR, optionally delete branch" ;;
|
|
20
36
|
deploy) echo -e " Deploy to staging/production with rollback" ;;
|
|
@@ -300,10 +316,22 @@ Checklist of completion criteria.
|
|
|
300
316
|
fi
|
|
301
317
|
|
|
302
318
|
local _token_log="${ARTIFACTS_DIR}/.claude-tokens-plan.log"
|
|
303
|
-
claude --print --model "$plan_model" --max-turns 25 \
|
|
319
|
+
claude --print --model "$plan_model" --max-turns 25 --dangerously-skip-permissions \
|
|
304
320
|
"$plan_prompt" < /dev/null > "$plan_file" 2>"$_token_log" || true
|
|
305
321
|
parse_claude_tokens "$_token_log"
|
|
306
322
|
|
|
323
|
+
# Claude may write to disk via tools instead of stdout — rescue those files
|
|
324
|
+
local _plan_rescue
|
|
325
|
+
for _plan_rescue in "${PROJECT_ROOT}/PLAN.md" "${PROJECT_ROOT}/plan.md" \
|
|
326
|
+
"${PROJECT_ROOT}/implementation-plan.md"; do
|
|
327
|
+
if [[ -s "$_plan_rescue" ]] && [[ $(wc -l < "$plan_file" 2>/dev/null | xargs) -lt 10 ]]; then
|
|
328
|
+
info "Plan written to ${_plan_rescue} via tools — adopting as plan artifact"
|
|
329
|
+
cat "$_plan_rescue" >> "$plan_file"
|
|
330
|
+
rm -f "$_plan_rescue"
|
|
331
|
+
break
|
|
332
|
+
fi
|
|
333
|
+
done
|
|
334
|
+
|
|
307
335
|
if [[ ! -s "$plan_file" ]]; then
|
|
308
336
|
error "Plan generation failed — empty output"
|
|
309
337
|
return 1
|
|
@@ -708,10 +736,22 @@ Be concrete and specific. Reference actual file paths in the codebase. Consider
|
|
|
708
736
|
fi
|
|
709
737
|
|
|
710
738
|
local _token_log="${ARTIFACTS_DIR}/.claude-tokens-design.log"
|
|
711
|
-
claude --print --model "$design_model" --max-turns 25 \
|
|
739
|
+
claude --print --model "$design_model" --max-turns 25 --dangerously-skip-permissions \
|
|
712
740
|
"$design_prompt" < /dev/null > "$design_file" 2>"$_token_log" || true
|
|
713
741
|
parse_claude_tokens "$_token_log"
|
|
714
742
|
|
|
743
|
+
# Claude may write to disk via tools instead of stdout — rescue those files
|
|
744
|
+
local _design_rescue
|
|
745
|
+
for _design_rescue in "${PROJECT_ROOT}/design-adr.md" "${PROJECT_ROOT}/design.md" \
|
|
746
|
+
"${PROJECT_ROOT}/ADR.md" "${PROJECT_ROOT}/DESIGN.md"; do
|
|
747
|
+
if [[ -s "$_design_rescue" ]] && [[ $(wc -l < "$design_file" 2>/dev/null | xargs) -lt 10 ]]; then
|
|
748
|
+
info "Design written to ${_design_rescue} via tools — adopting as design artifact"
|
|
749
|
+
cat "$_design_rescue" >> "$design_file"
|
|
750
|
+
rm -f "$_design_rescue"
|
|
751
|
+
break
|
|
752
|
+
fi
|
|
753
|
+
done
|
|
754
|
+
|
|
715
755
|
if [[ ! -s "$design_file" ]]; then
|
|
716
756
|
error "Design generation failed — empty output"
|
|
717
757
|
return 1
|
|
@@ -739,7 +779,7 @@ Be concrete and specific. Reference actual file paths in the codebase. Consider
|
|
|
739
779
|
files_to_modify=$(sed -n '/Files to modify/,/^-\|^#\|^$/p' "$design_file" 2>/dev/null | grep -E '^\s*-' | head -20 || true)
|
|
740
780
|
|
|
741
781
|
if [[ -n "$files_to_create" || -n "$files_to_modify" ]]; then
|
|
742
|
-
info "Design scope: ${DIM}$(echo "$files_to_create $files_to_modify" | grep -c '^\s*-' ||
|
|
782
|
+
info "Design scope: ${DIM}$(echo "$files_to_create $files_to_modify" | grep -c '^\s*-' || true) file(s)${RESET}"
|
|
743
783
|
fi
|
|
744
784
|
|
|
745
785
|
# Post design to GitHub issue
|
|
@@ -1077,8 +1117,9 @@ ${prevention_text}"
|
|
|
1077
1117
|
loop_args+=(--resume)
|
|
1078
1118
|
fi
|
|
1079
1119
|
|
|
1080
|
-
# Skip permissions
|
|
1081
|
-
|
|
1120
|
+
# Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
|
|
1121
|
+
# for interactive permission prompts. Without this flag, agents can't write files.
|
|
1122
|
+
loop_args+=(--skip-permissions)
|
|
1082
1123
|
|
|
1083
1124
|
info "Starting build loop: ${DIM}shipwright loop${RESET} (max ${max_iter} iterations, ${agents} agent(s))"
|
|
1084
1125
|
|
|
@@ -1131,13 +1172,13 @@ ${prevention_text}"
|
|
|
1131
1172
|
|
|
1132
1173
|
# Count commits made during build
|
|
1133
1174
|
local commit_count
|
|
1134
|
-
commit_count=$(
|
|
1175
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1135
1176
|
info "Build produced ${BOLD}$commit_count${RESET} commit(s)"
|
|
1136
1177
|
|
|
1137
1178
|
# Commit quality evaluation when intelligence is enabled
|
|
1138
1179
|
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1 && [[ "${commit_count:-0}" -gt 0 ]]; then
|
|
1139
1180
|
local commit_msgs
|
|
1140
|
-
commit_msgs=$(
|
|
1181
|
+
commit_msgs=$(_safe_base_log --format="%s" | head -20)
|
|
1141
1182
|
local quality_score
|
|
1142
1183
|
quality_score=$(claude --print --output-format text -p "Rate the quality of these git commit messages on a scale of 0-100. Consider: focus (one thing per commit), clarity (describes the why), atomicity (small logical units). Reply with ONLY a number 0-100.
|
|
1143
1184
|
|
|
@@ -1276,8 +1317,7 @@ stage_review() {
|
|
|
1276
1317
|
local diff_file="$ARTIFACTS_DIR/review-diff.patch"
|
|
1277
1318
|
local review_file="$ARTIFACTS_DIR/review.md"
|
|
1278
1319
|
|
|
1279
|
-
|
|
1280
|
-
git diff HEAD~5 > "$diff_file" 2>/dev/null || true
|
|
1320
|
+
_safe_base_diff > "$diff_file" 2>/dev/null || true
|
|
1281
1321
|
|
|
1282
1322
|
if [[ ! -s "$diff_file" ]]; then
|
|
1283
1323
|
warn "No diff found — skipping review"
|
|
@@ -1290,13 +1330,13 @@ stage_review() {
|
|
|
1290
1330
|
fi
|
|
1291
1331
|
|
|
1292
1332
|
local diff_stats
|
|
1293
|
-
diff_stats=$(
|
|
1333
|
+
diff_stats=$(_safe_base_diff --stat | tail -1 || echo "")
|
|
1294
1334
|
info "Running AI code review... ${DIM}($diff_stats)${RESET}"
|
|
1295
1335
|
|
|
1296
1336
|
# Semantic risk scoring when intelligence is enabled
|
|
1297
1337
|
if type intelligence_search_memory >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
1298
1338
|
local diff_files
|
|
1299
|
-
diff_files=$(
|
|
1339
|
+
diff_files=$(_safe_base_diff --name-only || true)
|
|
1300
1340
|
local risk_score="low"
|
|
1301
1341
|
# Fast heuristic: flag high-risk file patterns
|
|
1302
1342
|
if echo "$diff_files" | grep -qiE 'migration|schema|auth|crypto|security|password|token|secret|\.env'; then
|
|
@@ -1390,11 +1430,9 @@ $(cat "$dod_file")
|
|
|
1390
1430
|
## Diff to Review
|
|
1391
1431
|
$(cat "$diff_file")"
|
|
1392
1432
|
|
|
1393
|
-
#
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
review_args+=(--dangerously-skip-permissions)
|
|
1397
|
-
fi
|
|
1433
|
+
# Skip permissions — pipeline runs headlessly (claude -p) and has no terminal
|
|
1434
|
+
# for interactive permission prompts. Same rationale as build stage (line ~1083).
|
|
1435
|
+
local review_args=(--print --model "$review_model" --max-turns 25 --dangerously-skip-permissions)
|
|
1398
1436
|
|
|
1399
1437
|
claude "${review_args[@]}" "$review_prompt" < /dev/null > "$review_file" 2>"${ARTIFACTS_DIR}/.claude-tokens-review.log" || true
|
|
1400
1438
|
parse_claude_tokens "${ARTIFACTS_DIR}/.claude-tokens-review.log"
|
|
@@ -1539,15 +1577,143 @@ ${review_summary}
|
|
|
1539
1577
|
log_stage "review" "AI review complete ($total_issues issues: $critical_count critical, $bug_count bugs, $warning_count suggestions)"
|
|
1540
1578
|
}
|
|
1541
1579
|
|
|
1580
|
+
# ─── Compound Quality (fallback) ────────────────────────────────────────────
|
|
1581
|
+
# Basic implementation: adversarial review, negative testing, e2e checks, DoD audit.
|
|
1582
|
+
# If pipeline-intelligence.sh was sourced first, its enhanced version takes priority.
|
|
1583
|
+
if ! type stage_compound_quality >/dev/null 2>&1; then
|
|
1584
|
+
stage_compound_quality() {
|
|
1585
|
+
CURRENT_STAGE_ID="compound_quality"
|
|
1586
|
+
|
|
1587
|
+
# Read stage config from pipeline template
|
|
1588
|
+
local cfg
|
|
1589
|
+
cfg=$(jq -r '.stages[] | select(.id == "compound_quality") | .config // {}' "$PIPELINE_CONFIG" 2>/dev/null) || cfg="{}"
|
|
1590
|
+
|
|
1591
|
+
local do_adversarial do_negative do_e2e do_dod max_cycles blocking
|
|
1592
|
+
do_adversarial=$(echo "$cfg" | jq -r '.adversarial // false')
|
|
1593
|
+
do_negative=$(echo "$cfg" | jq -r '.negative // false')
|
|
1594
|
+
do_e2e=$(echo "$cfg" | jq -r '.e2e // false')
|
|
1595
|
+
do_dod=$(echo "$cfg" | jq -r '.dod_audit // false')
|
|
1596
|
+
max_cycles=$(echo "$cfg" | jq -r '.max_cycles // 1')
|
|
1597
|
+
blocking=$(echo "$cfg" | jq -r '.compound_quality_blocking // false')
|
|
1598
|
+
|
|
1599
|
+
local pass_count=0 fail_count=0 total=0
|
|
1600
|
+
local compound_log="$ARTIFACTS_DIR/compound-quality.log"
|
|
1601
|
+
: > "$compound_log"
|
|
1602
|
+
|
|
1603
|
+
# ── Adversarial review ──
|
|
1604
|
+
if [[ "$do_adversarial" == "true" ]]; then
|
|
1605
|
+
total=$((total + 1))
|
|
1606
|
+
info "Running adversarial review..."
|
|
1607
|
+
if [[ -x "$SCRIPT_DIR/sw-adversarial.sh" ]]; then
|
|
1608
|
+
if bash "$SCRIPT_DIR/sw-adversarial.sh" --repo "${REPO_DIR:-.}" >> "$compound_log" 2>&1; then
|
|
1609
|
+
pass_count=$((pass_count + 1))
|
|
1610
|
+
success "Adversarial review passed"
|
|
1611
|
+
else
|
|
1612
|
+
fail_count=$((fail_count + 1))
|
|
1613
|
+
warn "Adversarial review found issues"
|
|
1614
|
+
fi
|
|
1615
|
+
else
|
|
1616
|
+
warn "sw-adversarial.sh not found, skipping"
|
|
1617
|
+
fi
|
|
1618
|
+
fi
|
|
1619
|
+
|
|
1620
|
+
# ── Negative / edge-case testing ──
|
|
1621
|
+
if [[ "$do_negative" == "true" ]]; then
|
|
1622
|
+
total=$((total + 1))
|
|
1623
|
+
info "Running negative test pass..."
|
|
1624
|
+
if [[ -n "${TEST_CMD:-}" ]]; then
|
|
1625
|
+
if eval "$TEST_CMD" >> "$compound_log" 2>&1; then
|
|
1626
|
+
pass_count=$((pass_count + 1))
|
|
1627
|
+
success "Negative test pass passed"
|
|
1628
|
+
else
|
|
1629
|
+
fail_count=$((fail_count + 1))
|
|
1630
|
+
warn "Negative test pass found failures"
|
|
1631
|
+
fi
|
|
1632
|
+
else
|
|
1633
|
+
pass_count=$((pass_count + 1))
|
|
1634
|
+
info "No test command configured, skipping negative tests"
|
|
1635
|
+
fi
|
|
1636
|
+
fi
|
|
1637
|
+
|
|
1638
|
+
# ── E2E checks ──
|
|
1639
|
+
if [[ "$do_e2e" == "true" ]]; then
|
|
1640
|
+
total=$((total + 1))
|
|
1641
|
+
info "Running e2e checks..."
|
|
1642
|
+
if [[ -x "$SCRIPT_DIR/sw-e2e-orchestrator.sh" ]]; then
|
|
1643
|
+
if bash "$SCRIPT_DIR/sw-e2e-orchestrator.sh" run >> "$compound_log" 2>&1; then
|
|
1644
|
+
pass_count=$((pass_count + 1))
|
|
1645
|
+
success "E2E checks passed"
|
|
1646
|
+
else
|
|
1647
|
+
fail_count=$((fail_count + 1))
|
|
1648
|
+
warn "E2E checks found issues"
|
|
1649
|
+
fi
|
|
1650
|
+
else
|
|
1651
|
+
pass_count=$((pass_count + 1))
|
|
1652
|
+
info "sw-e2e-orchestrator.sh not found, skipping e2e"
|
|
1653
|
+
fi
|
|
1654
|
+
fi
|
|
1655
|
+
|
|
1656
|
+
# ── Definition of Done audit ──
|
|
1657
|
+
if [[ "$do_dod" == "true" ]]; then
|
|
1658
|
+
total=$((total + 1))
|
|
1659
|
+
info "Running definition-of-done audit..."
|
|
1660
|
+
if [[ -x "$SCRIPT_DIR/sw-quality.sh" ]]; then
|
|
1661
|
+
if bash "$SCRIPT_DIR/sw-quality.sh" validate >> "$compound_log" 2>&1; then
|
|
1662
|
+
pass_count=$((pass_count + 1))
|
|
1663
|
+
success "DoD audit passed"
|
|
1664
|
+
else
|
|
1665
|
+
fail_count=$((fail_count + 1))
|
|
1666
|
+
warn "DoD audit found gaps"
|
|
1667
|
+
fi
|
|
1668
|
+
else
|
|
1669
|
+
pass_count=$((pass_count + 1))
|
|
1670
|
+
info "sw-quality.sh not found, skipping DoD audit"
|
|
1671
|
+
fi
|
|
1672
|
+
fi
|
|
1673
|
+
|
|
1674
|
+
# ── Summary ──
|
|
1675
|
+
log_stage "compound_quality" "Compound quality: $pass_count/$total checks passed, $fail_count failed"
|
|
1676
|
+
|
|
1677
|
+
if [[ "$fail_count" -gt 0 && "$blocking" == "true" ]]; then
|
|
1678
|
+
error "Compound quality gate failed: $fail_count of $total checks failed"
|
|
1679
|
+
return 1
|
|
1680
|
+
fi
|
|
1681
|
+
|
|
1682
|
+
return 0
|
|
1683
|
+
}
|
|
1684
|
+
fi # end fallback stage_compound_quality
|
|
1685
|
+
|
|
1542
1686
|
stage_pr() {
|
|
1543
1687
|
CURRENT_STAGE_ID="pr"
|
|
1544
1688
|
local plan_file="$ARTIFACTS_DIR/plan.md"
|
|
1545
1689
|
local test_log="$ARTIFACTS_DIR/test-results.log"
|
|
1546
1690
|
local review_file="$ARTIFACTS_DIR/review.md"
|
|
1547
1691
|
|
|
1692
|
+
# ── Skip PR in local/no-github mode ──
|
|
1693
|
+
if [[ "${NO_GITHUB:-false}" == "true" || "${SHIPWRIGHT_LOCAL:-}" == "1" || "${LOCAL_MODE:-false}" == "true" ]]; then
|
|
1694
|
+
info "Skipping PR stage — running in local/no-github mode"
|
|
1695
|
+
# Save a PR draft locally for reference
|
|
1696
|
+
local branch_name
|
|
1697
|
+
branch_name=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
1698
|
+
local commit_count
|
|
1699
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1700
|
+
{
|
|
1701
|
+
echo "# PR Draft (local mode)"
|
|
1702
|
+
echo ""
|
|
1703
|
+
echo "**Branch:** ${branch_name}"
|
|
1704
|
+
echo "**Commits:** ${commit_count:-0}"
|
|
1705
|
+
echo "**Goal:** ${GOAL:-N/A}"
|
|
1706
|
+
echo ""
|
|
1707
|
+
echo "## Changes"
|
|
1708
|
+
_safe_base_diff --stat || true
|
|
1709
|
+
} > ".claude/pr-draft.md" 2>/dev/null || true
|
|
1710
|
+
emit_event "pr.skipped" "issue=${ISSUE_NUMBER:-0}" "reason=local_mode"
|
|
1711
|
+
return 0
|
|
1712
|
+
fi
|
|
1713
|
+
|
|
1548
1714
|
# ── PR Hygiene Checks (informational) ──
|
|
1549
1715
|
local hygiene_commit_count
|
|
1550
|
-
hygiene_commit_count=$(
|
|
1716
|
+
hygiene_commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1551
1717
|
hygiene_commit_count="${hygiene_commit_count:-0}"
|
|
1552
1718
|
|
|
1553
1719
|
if [[ "$hygiene_commit_count" -gt 20 ]]; then
|
|
@@ -1556,7 +1722,7 @@ stage_pr() {
|
|
|
1556
1722
|
|
|
1557
1723
|
# Check for WIP/fixup/squash commits (expanded patterns)
|
|
1558
1724
|
local wip_commits
|
|
1559
|
-
wip_commits=$(
|
|
1725
|
+
wip_commits=$(_safe_base_log --oneline | grep -ciE '^[0-9a-f]+ (WIP|fixup!|squash!|TODO|HACK|TEMP|BROKEN|wip[:-]|temp[:-]|broken[:-]|do not merge)' || true)
|
|
1560
1726
|
wip_commits="${wip_commits:-0}"
|
|
1561
1727
|
if [[ "$wip_commits" -gt 0 ]]; then
|
|
1562
1728
|
warn "Branch has ${wip_commits} WIP/fixup/squash/temp commit(s) — consider cleaning up"
|
|
@@ -1564,7 +1730,7 @@ stage_pr() {
|
|
|
1564
1730
|
|
|
1565
1731
|
# ── PR Quality Gate: reject PRs with no real code changes ──
|
|
1566
1732
|
local real_files
|
|
1567
|
-
real_files=$(
|
|
1733
|
+
real_files=$(_safe_base_diff --name-only | grep -v '^\.claude/' | grep -v '^\.github/' || true)
|
|
1568
1734
|
if [[ -z "$real_files" ]]; then
|
|
1569
1735
|
error "No real code changes detected — only pipeline artifacts (.claude/ logs)."
|
|
1570
1736
|
error "The build agent did not produce meaningful changes. Skipping PR creation."
|
|
@@ -1614,7 +1780,7 @@ stage_pr() {
|
|
|
1614
1780
|
if [[ "$sim_enabled" == "true" ]]; then
|
|
1615
1781
|
info "Running developer simulation review..."
|
|
1616
1782
|
local diff_for_sim
|
|
1617
|
-
diff_for_sim=$(
|
|
1783
|
+
diff_for_sim=$(_safe_base_diff || true)
|
|
1618
1784
|
if [[ -n "$diff_for_sim" ]]; then
|
|
1619
1785
|
local sim_result
|
|
1620
1786
|
sim_result=$(simulation_review "$diff_for_sim" "${GOAL:-}" 2>/dev/null || echo "")
|
|
@@ -1644,7 +1810,7 @@ stage_pr() {
|
|
|
1644
1810
|
if [[ "$arch_enabled" == "true" ]]; then
|
|
1645
1811
|
info "Validating architecture..."
|
|
1646
1812
|
local diff_for_arch
|
|
1647
|
-
diff_for_arch=$(
|
|
1813
|
+
diff_for_arch=$(_safe_base_diff || true)
|
|
1648
1814
|
if [[ -n "$diff_for_arch" ]]; then
|
|
1649
1815
|
local arch_result
|
|
1650
1816
|
arch_result=$(architecture_validate_changes "$diff_for_arch" "" 2>/dev/null || echo "")
|
|
@@ -1668,10 +1834,10 @@ stage_pr() {
|
|
|
1668
1834
|
|
|
1669
1835
|
# Pre-PR diff gate — verify meaningful code changes exist (not just bookkeeping)
|
|
1670
1836
|
local real_changes
|
|
1671
|
-
real_changes=$(
|
|
1837
|
+
real_changes=$(_safe_base_diff --name-only \
|
|
1672
1838
|
-- . ':!.claude/loop-state.md' ':!.claude/pipeline-state.md' \
|
|
1673
1839
|
':!.claude/pipeline-artifacts/*' ':!**/progress.md' \
|
|
1674
|
-
':!**/error-summary.json'
|
|
1840
|
+
':!**/error-summary.json' | wc -l | xargs || echo "0")
|
|
1675
1841
|
if [[ "${real_changes:-0}" -eq 0 ]]; then
|
|
1676
1842
|
error "No meaningful code changes detected — only bookkeeping files modified"
|
|
1677
1843
|
error "Refusing to create PR with zero real changes"
|
|
@@ -1726,10 +1892,10 @@ stage_pr() {
|
|
|
1726
1892
|
[[ -n "${GITHUB_ISSUE:-}" ]] && closes_line="Closes ${GITHUB_ISSUE}"
|
|
1727
1893
|
|
|
1728
1894
|
local diff_stats
|
|
1729
|
-
diff_stats=$(
|
|
1895
|
+
diff_stats=$(_safe_base_diff --stat | tail -1 || echo "")
|
|
1730
1896
|
|
|
1731
1897
|
local commit_count
|
|
1732
|
-
commit_count=$(
|
|
1898
|
+
commit_count=$(_safe_base_log --oneline | wc -l | xargs)
|
|
1733
1899
|
|
|
1734
1900
|
local total_dur=""
|
|
1735
1901
|
if [[ -n "$PIPELINE_START_EPOCH" ]]; then
|
|
@@ -1774,7 +1940,7 @@ EOF
|
|
|
1774
1940
|
risk_tier="low"
|
|
1775
1941
|
if [[ -f "$REPO_DIR/config/policy.json" ]]; then
|
|
1776
1942
|
local changed_files
|
|
1777
|
-
changed_files=$(
|
|
1943
|
+
changed_files=$(_safe_base_diff --name-only || true)
|
|
1778
1944
|
if [[ -n "$changed_files" ]]; then
|
|
1779
1945
|
local policy_file="$REPO_DIR/config/policy.json"
|
|
1780
1946
|
check_tier_match() {
|
|
@@ -1906,7 +2072,7 @@ EOF
|
|
|
1906
2072
|
codeowners_json=$(gh_codeowners "$REPO_OWNER" "$REPO_NAME" 2>/dev/null || echo "[]")
|
|
1907
2073
|
if [[ "$codeowners_json" != "[]" && -n "$codeowners_json" ]]; then
|
|
1908
2074
|
local changed_files
|
|
1909
|
-
changed_files=$(
|
|
2075
|
+
changed_files=$(_safe_base_diff --name-only || true)
|
|
1910
2076
|
if [[ -n "$changed_files" ]]; then
|
|
1911
2077
|
local co_reviewers
|
|
1912
2078
|
co_reviewers=$(echo "$codeowners_json" | jq -r '.[].owners[]' 2>/dev/null | sort -u | head -3 || true)
|
|
@@ -1980,13 +2146,14 @@ stage_merge() {
|
|
|
1980
2146
|
local merge_diff_file="${ARTIFACTS_DIR}/review-diff.patch"
|
|
1981
2147
|
local merge_review_file="${ARTIFACTS_DIR}/review.md"
|
|
1982
2148
|
if [[ ! -s "$merge_diff_file" ]]; then
|
|
1983
|
-
|
|
1984
|
-
git diff HEAD~5 > "$merge_diff_file" 2>/dev/null || true
|
|
2149
|
+
_safe_base_diff > "$merge_diff_file" 2>/dev/null || true
|
|
1985
2150
|
fi
|
|
1986
2151
|
if [[ -s "$merge_diff_file" ]]; then
|
|
1987
2152
|
local _merge_critical _merge_sec _merge_blocking _merge_reject
|
|
1988
|
-
_merge_critical=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$merge_review_file" 2>/dev/null ||
|
|
1989
|
-
|
|
2153
|
+
_merge_critical=$(grep -ciE '\*\*\[?Critical\]?\*\*' "$merge_review_file" 2>/dev/null || true)
|
|
2154
|
+
_merge_critical="${_merge_critical:-0}"
|
|
2155
|
+
_merge_sec=$(grep -ciE '\*\*\[?Security\]?\*\*' "$merge_review_file" 2>/dev/null || true)
|
|
2156
|
+
_merge_sec="${_merge_sec:-0}"
|
|
1990
2157
|
_merge_blocking=$((${_merge_critical:-0} + ${_merge_sec:-0}))
|
|
1991
2158
|
[[ "$_merge_blocking" -gt 0 ]] && _merge_reject="Review found ${_merge_blocking} critical/security issue(s)"
|
|
1992
2159
|
if ! bash "$SCRIPT_DIR/sw-oversight.sh" gate --diff "$merge_diff_file" --description "${GOAL:-Pipeline merge}" --reject-if "${_merge_reject:-}" >/dev/null 2>&1; then
|
|
@@ -176,6 +176,13 @@ mark_stage_complete() {
|
|
|
176
176
|
write_state
|
|
177
177
|
|
|
178
178
|
record_stage_effectiveness "$stage_id" "complete"
|
|
179
|
+
|
|
180
|
+
# Record stage completion in SQLite pipeline_stages table
|
|
181
|
+
if type record_stage >/dev/null 2>&1; then
|
|
182
|
+
local _stage_secs
|
|
183
|
+
_stage_secs=$(get_stage_timing_seconds "$stage_id")
|
|
184
|
+
record_stage "${SHIPWRIGHT_PIPELINE_ID:-}" "$stage_id" "complete" "${_stage_secs:-0}" "" 2>/dev/null || true
|
|
185
|
+
fi
|
|
179
186
|
# Update memory baselines and predictive baselines for stage durations
|
|
180
187
|
if [[ "$stage_id" == "test" || "$stage_id" == "build" ]]; then
|
|
181
188
|
local secs
|
|
@@ -354,6 +361,13 @@ mark_stage_failed() {
|
|
|
354
361
|
log_stage "$stage_id" "failed (${timing})"
|
|
355
362
|
write_state
|
|
356
363
|
|
|
364
|
+
# Record stage failure in SQLite pipeline_stages table
|
|
365
|
+
if type record_stage >/dev/null 2>&1; then
|
|
366
|
+
local _stage_secs
|
|
367
|
+
_stage_secs=$(get_stage_timing_seconds "$stage_id")
|
|
368
|
+
record_stage "${SHIPWRIGHT_PIPELINE_ID:-}" "$stage_id" "failed" "${_stage_secs:-0}" "" 2>/dev/null || true
|
|
369
|
+
fi
|
|
370
|
+
|
|
357
371
|
# Update GitHub progress + comment failure
|
|
358
372
|
if [[ -n "$ISSUE_NUMBER" ]]; then
|
|
359
373
|
local body
|
package/scripts/lib/policy.sh
CHANGED
|
File without changes
|
|
File without changes
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -11,8 +11,11 @@ import {
|
|
|
11
11
|
readFileSync,
|
|
12
12
|
writeFileSync,
|
|
13
13
|
appendFileSync,
|
|
14
|
+
chmodSync,
|
|
15
|
+
readdirSync,
|
|
14
16
|
} from "fs";
|
|
15
|
-
import { join } from "path";
|
|
17
|
+
import { join, basename } from "path";
|
|
18
|
+
import { execSync } from "child_process";
|
|
16
19
|
|
|
17
20
|
const HOME = process.env.HOME || process.env.USERPROFILE;
|
|
18
21
|
const PKG_DIR = join(import.meta.dirname, "..");
|
|
@@ -130,6 +133,77 @@ try {
|
|
|
130
133
|
success("Migrated legacy config (originals preserved)");
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
// Set executable bits on all scripts (npm strips them on some platforms)
|
|
137
|
+
const scriptsDir = join(PKG_DIR, "scripts");
|
|
138
|
+
if (existsSync(scriptsDir)) {
|
|
139
|
+
let madeExecutable = 0;
|
|
140
|
+
for (const file of readdirSync(scriptsDir)) {
|
|
141
|
+
const fp = join(scriptsDir, file);
|
|
142
|
+
try {
|
|
143
|
+
chmodSync(fp, 0o755);
|
|
144
|
+
madeExecutable++;
|
|
145
|
+
} catch (_) {
|
|
146
|
+
// skip non-files
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const libDir = join(scriptsDir, "lib");
|
|
150
|
+
if (existsSync(libDir)) {
|
|
151
|
+
for (const file of readdirSync(libDir)) {
|
|
152
|
+
try {
|
|
153
|
+
chmodSync(join(libDir, file), 0o755);
|
|
154
|
+
madeExecutable++;
|
|
155
|
+
} catch (_) {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
success(`Set executable bits on ${madeExecutable} scripts`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Install shell completions for the user's current shell
|
|
162
|
+
const completionsDir = join(PKG_DIR, "completions");
|
|
163
|
+
if (existsSync(completionsDir)) {
|
|
164
|
+
const shell = basename(process.env.SHELL || "/bin/bash");
|
|
165
|
+
try {
|
|
166
|
+
if (shell === "bash") {
|
|
167
|
+
const dest =
|
|
168
|
+
process.env.BASH_COMPLETION_USER_DIR ||
|
|
169
|
+
join(
|
|
170
|
+
process.env.XDG_DATA_HOME || join(HOME, ".local", "share"),
|
|
171
|
+
"bash-completion",
|
|
172
|
+
"completions",
|
|
173
|
+
);
|
|
174
|
+
ensureDir(dest);
|
|
175
|
+
cpSync(
|
|
176
|
+
join(completionsDir, "shipwright.bash"),
|
|
177
|
+
join(dest, "shipwright"),
|
|
178
|
+
);
|
|
179
|
+
cpSync(join(completionsDir, "shipwright.bash"), join(dest, "sw"));
|
|
180
|
+
success(`Installed bash completions to ${dest}`);
|
|
181
|
+
} else if (shell === "zsh") {
|
|
182
|
+
const dest = join(HOME, ".zfunc");
|
|
183
|
+
ensureDir(dest);
|
|
184
|
+
cpSync(join(completionsDir, "_shipwright"), join(dest, "_shipwright"));
|
|
185
|
+
cpSync(join(completionsDir, "_shipwright"), join(dest, "_sw"));
|
|
186
|
+
success(`Installed zsh completions to ${dest}`);
|
|
187
|
+
} else if (shell === "fish") {
|
|
188
|
+
const dest = join(
|
|
189
|
+
process.env.XDG_CONFIG_HOME || join(HOME, ".config"),
|
|
190
|
+
"fish",
|
|
191
|
+
"completions",
|
|
192
|
+
);
|
|
193
|
+
ensureDir(dest);
|
|
194
|
+
cpSync(
|
|
195
|
+
join(completionsDir, "shipwright.fish"),
|
|
196
|
+
join(dest, "shipwright.fish"),
|
|
197
|
+
);
|
|
198
|
+
cpSync(join(completionsDir, "shipwright.fish"), join(dest, "sw.fish"));
|
|
199
|
+
success(`Installed fish completions to ${dest}`);
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {
|
|
202
|
+
warn(`Could not auto-install completions: ${e.message}`);
|
|
203
|
+
info(`Run: shipwright init (or: bash scripts/install-completions.sh)`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
133
207
|
// Print success banner
|
|
134
208
|
console.log();
|
|
135
209
|
console.log(`${GREEN}${BOLD}Shipwright CLI installed!${RESET} Next steps:`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ╔═══════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
# ║ Example External Signal Collector for Shipwright Decision Engine ║
|
|
4
|
+
# ║ Place custom collectors in scripts/signals/ — they're auto-discovered ║
|
|
5
|
+
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
|
+
#
|
|
7
|
+
# Output: one JSON candidate per line (JSONL).
|
|
8
|
+
# Required fields: id, signal, category, title, description, risk_score,
|
|
9
|
+
# confidence, dedup_key
|
|
10
|
+
# Optional fields: evidence (object)
|
|
11
|
+
#
|
|
12
|
+
# The decision engine collects output from all scripts/signals/*.sh files,
|
|
13
|
+
# validates each line as JSON, and includes valid candidates in the scoring
|
|
14
|
+
# pipeline.
|
|
15
|
+
#
|
|
16
|
+
# Categories (determines autonomy tier):
|
|
17
|
+
# auto: deps_patch, deps_minor, security_patch, test_coverage,
|
|
18
|
+
# doc_sync, dead_code
|
|
19
|
+
# propose: refactor_hotspot, architecture_drift, performance_regression,
|
|
20
|
+
# deps_major, security_critical, recurring_failure, dora_regression
|
|
21
|
+
# draft: new_feature, breaking_change, business_logic, api_change,
|
|
22
|
+
# data_model_change
|
|
23
|
+
#
|
|
24
|
+
# Example: detect a custom condition and emit a candidate
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
# Example: check if a TODO count exceeds a threshold
|
|
30
|
+
TODO_COUNT=$(grep -r "TODO" --include="*.ts" --include="*.js" --include="*.sh" . 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
31
|
+
|
|
32
|
+
if [[ "${TODO_COUNT:-0}" -gt 50 ]]; then
|
|
33
|
+
cat <<EOF
|
|
34
|
+
{"id":"custom-todo-cleanup","signal":"custom","category":"dead_code","title":"Clean up ${TODO_COUNT} TODOs","description":"Codebase has ${TODO_COUNT} TODO comments — consider a cleanup sprint","evidence":{"todo_count":${TODO_COUNT}},"risk_score":15,"confidence":"0.70","dedup_key":"custom:todo:cleanup"}
|
|
35
|
+
EOF
|
|
36
|
+
fi
|
package/scripts/sw
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
-
VERSION="3.
|
|
8
|
+
VERSION="3.1.0"
|
|
9
9
|
|
|
10
10
|
# Resolve symlinks (required for npm global install where bin/ symlinks to node_modules/)
|
|
11
11
|
SOURCE="${BASH_SOURCE[0]}"
|
|
@@ -193,7 +193,8 @@ route_quality() {
|
|
|
193
193
|
security-audit|audit) exec "$SCRIPT_DIR/sw-security-audit.sh" "$@" ;;
|
|
194
194
|
testgen) exec "$SCRIPT_DIR/sw-testgen.sh" "$@" ;;
|
|
195
195
|
hygiene) exec "$SCRIPT_DIR/sw-hygiene.sh" "$@" ;;
|
|
196
|
-
|
|
196
|
+
validate|gate) exec "$SCRIPT_DIR/sw-quality.sh" "$@" ;;
|
|
197
|
+
help|*) echo "Usage: shipwright quality {code-review|security-audit|testgen|hygiene|validate}"; exit 1 ;;
|
|
197
198
|
esac
|
|
198
199
|
}
|
|
199
200
|
|
|
@@ -530,14 +531,17 @@ main() {
|
|
|
530
531
|
evidence|ev)
|
|
531
532
|
exec "$SCRIPT_DIR/sw-evidence.sh" "$@"
|
|
532
533
|
;;
|
|
534
|
+
decide)
|
|
535
|
+
exec "$SCRIPT_DIR/sw-decide.sh" "$@"
|
|
536
|
+
;;
|
|
533
537
|
otel)
|
|
534
538
|
exec "$SCRIPT_DIR/sw-otel.sh" "$@"
|
|
535
539
|
;;
|
|
536
540
|
triage)
|
|
537
541
|
exec "$SCRIPT_DIR/sw-triage.sh" "$@"
|
|
538
542
|
;;
|
|
539
|
-
|
|
540
|
-
exec "$SCRIPT_DIR/sw-
|
|
543
|
+
pipeline-composer|composer)
|
|
544
|
+
exec "$SCRIPT_DIR/sw-pipeline-composer.sh" "$@"
|
|
541
545
|
;;
|
|
542
546
|
oversight)
|
|
543
547
|
exec "$SCRIPT_DIR/sw-oversight.sh" "$@"
|
package/scripts/sw-activity.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="3.
|
|
9
|
+
VERSION="3.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
package/scripts/sw-adaptive.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="3.
|
|
9
|
+
VERSION="3.1.0"
|
|
10
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
11
|
|
|
12
12
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
package/scripts/sw-auth.sh
CHANGED