shipwright-cli 2.3.1 → 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 +95 -28
- 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 +155 -2
- package/config/policy.schema.json +162 -1
- 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 +15 -5
- 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 +748 -0
- 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 +284 -67
- 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 +203 -29
- 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 +220 -0
- 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
|
@@ -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,19 +34,12 @@ 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
|
# ─── Structured Event Log ────────────────────────────────────────────────────
|
|
48
38
|
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
49
39
|
|
|
40
|
+
# ─── DB for outcome-based learning ─────────────────────────────────────────────
|
|
41
|
+
[[ -f "$SCRIPT_DIR/sw-db.sh" ]] && source "$SCRIPT_DIR/sw-db.sh"
|
|
42
|
+
|
|
50
43
|
# ─── Storage Paths ───────────────────────────────────────────────────────────
|
|
51
44
|
OPTIMIZATION_DIR="${HOME}/.shipwright/optimization"
|
|
52
45
|
OUTCOMES_FILE="${OPTIMIZATION_DIR}/outcomes.jsonl"
|
|
@@ -64,13 +57,13 @@ ensure_optimization_dir() {
|
|
|
64
57
|
# ─── GitHub Metrics ──────────────────────────────────────────────────────
|
|
65
58
|
|
|
66
59
|
_optimize_github_metrics() {
|
|
67
|
-
type _gh_detect_repo
|
|
60
|
+
type _gh_detect_repo >/dev/null 2>&1 || { echo "{}"; return 0; }
|
|
68
61
|
_gh_detect_repo 2>/dev/null || { echo "{}"; return 0; }
|
|
69
62
|
|
|
70
63
|
local owner="${GH_OWNER:-}" repo="${GH_REPO:-}"
|
|
71
64
|
[[ -z "$owner" || -z "$repo" ]] && { echo "{}"; return 0; }
|
|
72
65
|
|
|
73
|
-
if type gh_actions_runs
|
|
66
|
+
if type gh_actions_runs >/dev/null 2>&1; then
|
|
74
67
|
local runs
|
|
75
68
|
runs=$(gh_actions_runs "$owner" "$repo" "" 50 2>/dev/null || echo "[]")
|
|
76
69
|
local success_rate avg_duration
|
|
@@ -171,7 +164,7 @@ optimize_analyze_outcome() {
|
|
|
171
164
|
echo "$outcome_line" >> "$OUTCOMES_FILE"
|
|
172
165
|
|
|
173
166
|
# Rotate outcomes file to prevent unbounded growth
|
|
174
|
-
type rotate_jsonl
|
|
167
|
+
type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$OUTCOMES_FILE" 10000
|
|
175
168
|
|
|
176
169
|
# Record GitHub CI metrics alongside outcome
|
|
177
170
|
local gh_ci_metrics
|
|
@@ -223,7 +216,7 @@ optimize_ingest_retro() {
|
|
|
223
216
|
latest_retro=$(ls -t "$retros_dir"/retro-*.json 2>/dev/null | head -1)
|
|
224
217
|
[[ -z "$latest_retro" || ! -f "$latest_retro" ]] && return 0
|
|
225
218
|
|
|
226
|
-
if ! command -v jq
|
|
219
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
227
220
|
warn "jq required for retro ingest — skipping"
|
|
228
221
|
return 0
|
|
229
222
|
fi
|
|
@@ -290,7 +283,7 @@ optimize_ingest_retro() {
|
|
|
290
283
|
fi
|
|
291
284
|
fi
|
|
292
285
|
|
|
293
|
-
type rotate_jsonl
|
|
286
|
+
type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$OUTCOMES_FILE" 10000
|
|
294
287
|
|
|
295
288
|
emit_event "optimize.retro_ingested" \
|
|
296
289
|
"success_rate=${success_rate:-0}" \
|
|
@@ -680,7 +673,7 @@ _optimize_apply_prediction_bias() {
|
|
|
680
673
|
fi
|
|
681
674
|
|
|
682
675
|
# Rotate validation file
|
|
683
|
-
type rotate_jsonl
|
|
676
|
+
type rotate_jsonl >/dev/null 2>&1 && rotate_jsonl "$validation_file" 5000
|
|
684
677
|
}
|
|
685
678
|
|
|
686
679
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -739,10 +732,13 @@ optimize_route_models() {
|
|
|
739
732
|
done
|
|
740
733
|
done < "$outcomes_file"
|
|
741
734
|
|
|
742
|
-
# Build routing recommendations
|
|
735
|
+
# Build routing recommendations; extract .routes from existing file when present
|
|
743
736
|
local routing='{}'
|
|
744
|
-
if [[ -f "$MODEL_ROUTING_FILE" ]]; then
|
|
745
|
-
|
|
737
|
+
if [[ -f "$MODEL_ROUTING_FILE" ]] && command -v jq >/dev/null 2>&1; then
|
|
738
|
+
local existing
|
|
739
|
+
existing=$(cat "$MODEL_ROUTING_FILE")
|
|
740
|
+
# Use .routes when present (self-optimize format), else flatten for merge
|
|
741
|
+
routing=$(echo "$existing" | jq -r 'if .routes then .routes else . end | if type == "object" then . else {} end' 2>/dev/null || echo '{}')
|
|
746
742
|
fi
|
|
747
743
|
|
|
748
744
|
if [[ -f "$tmp_stage_stats" && -s "$tmp_stage_stats" ]]; then
|
|
@@ -760,7 +756,7 @@ optimize_route_models() {
|
|
|
760
756
|
sonnet_success="${sonnet_success:-0}"
|
|
761
757
|
|
|
762
758
|
if [[ "$sonnet_total" -gt 0 ]]; then
|
|
763
|
-
sonnet_rate=$(awk "BEGIN{printf \"%.1f\", ($sonnet_success/$sonnet_total)*100}")
|
|
759
|
+
sonnet_rate=$(awk "BEGIN{printf \"%.1f\", ($sonnet_success/$sonnet_total)*100}" | tr -d '\n')
|
|
764
760
|
else
|
|
765
761
|
sonnet_rate="0"
|
|
766
762
|
fi
|
|
@@ -773,7 +769,7 @@ optimize_route_models() {
|
|
|
773
769
|
opus_success="${opus_success:-0}"
|
|
774
770
|
|
|
775
771
|
if [[ "$opus_total" -gt 0 ]]; then
|
|
776
|
-
opus_rate=$(awk "BEGIN{printf \"%.1f\", ($opus_success/$opus_total)*100}")
|
|
772
|
+
opus_rate=$(awk "BEGIN{printf \"%.1f\", ($opus_success/$opus_total)*100}" | tr -d '\n')
|
|
777
773
|
else
|
|
778
774
|
opus_rate="0"
|
|
779
775
|
fi
|
|
@@ -812,7 +808,7 @@ optimize_route_models() {
|
|
|
812
808
|
routes: (. | to_entries | map({
|
|
813
809
|
key: .key,
|
|
814
810
|
value: {
|
|
815
|
-
model: .value.recommended,
|
|
811
|
+
model: (.value.recommended // .value.model),
|
|
816
812
|
confidence: (if .value.sonnet_samples + .value.opus_samples >= 10 then 0.9
|
|
817
813
|
elif .value.sonnet_samples + .value.opus_samples >= 5 then 0.7
|
|
818
814
|
else 0.5 end),
|
|
@@ -836,6 +832,129 @@ optimize_route_models() {
|
|
|
836
832
|
success "Model routing updated"
|
|
837
833
|
}
|
|
838
834
|
|
|
835
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
836
|
+
# OUTCOME-BASED LEARNING: Thompson Sampling & UCB1
|
|
837
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
838
|
+
|
|
839
|
+
# Thompson sampling: select template based on historical success rates
|
|
840
|
+
# Uses Beta distribution approximation: sample from Beta(successes+1, failures+1)
|
|
841
|
+
thompson_select_template() {
|
|
842
|
+
local complexity="${1:-medium}"
|
|
843
|
+
|
|
844
|
+
if ! db_available 2>/dev/null; then
|
|
845
|
+
_legacy_template_select "$complexity"
|
|
846
|
+
return
|
|
847
|
+
fi
|
|
848
|
+
|
|
849
|
+
local templates
|
|
850
|
+
templates=$(_db_query "SELECT template,
|
|
851
|
+
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as wins,
|
|
852
|
+
SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as losses
|
|
853
|
+
FROM pipeline_outcomes
|
|
854
|
+
WHERE complexity = '$complexity' AND template IS NOT NULL AND template != ''
|
|
855
|
+
GROUP BY template;" 2>/dev/null || echo "")
|
|
856
|
+
|
|
857
|
+
if [[ -z "$templates" ]]; then
|
|
858
|
+
echo "standard"
|
|
859
|
+
return
|
|
860
|
+
fi
|
|
861
|
+
|
|
862
|
+
local best_template="standard"
|
|
863
|
+
local best_score=0
|
|
864
|
+
|
|
865
|
+
while IFS='|' read -r template wins losses; do
|
|
866
|
+
[[ -z "$template" ]] && continue
|
|
867
|
+
template=$(echo "$template" | xargs)
|
|
868
|
+
local alpha=$((wins + 1))
|
|
869
|
+
local beta_param=$((losses + 1))
|
|
870
|
+
local total=$((alpha + beta_param))
|
|
871
|
+
local mean_x1000=$(( (alpha * 1000) / total ))
|
|
872
|
+
local noise=$(( (RANDOM % 200) - 100 ))
|
|
873
|
+
local variance_factor=$(( 1000 / (total + 1) ))
|
|
874
|
+
local score=$(( mean_x1000 + (noise * variance_factor / 100) ))
|
|
875
|
+
|
|
876
|
+
if [[ $score -gt $best_score ]]; then
|
|
877
|
+
best_score=$score
|
|
878
|
+
best_template="$template"
|
|
879
|
+
fi
|
|
880
|
+
done <<< "$templates"
|
|
881
|
+
|
|
882
|
+
echo "$best_template"
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
# Fallback when DB unavailable: map complexity to template
|
|
886
|
+
_legacy_template_select() {
|
|
887
|
+
local complexity="${1:-medium}"
|
|
888
|
+
case "$complexity" in
|
|
889
|
+
low|fast) echo "fast" ;;
|
|
890
|
+
high|full) echo "full" ;;
|
|
891
|
+
*) echo "standard" ;;
|
|
892
|
+
esac
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
# UCB1: select best model for a given stage
|
|
896
|
+
# UCB1 = mean_reward + sqrt(2 * ln(total_trials) / trials_for_this_arm)
|
|
897
|
+
ucb1_select_model() {
|
|
898
|
+
local stage="${1:-build}"
|
|
899
|
+
|
|
900
|
+
if ! db_available 2>/dev/null; then
|
|
901
|
+
echo "sonnet"
|
|
902
|
+
return
|
|
903
|
+
fi
|
|
904
|
+
|
|
905
|
+
local total_trials
|
|
906
|
+
total_trials=$(_db_query "SELECT COUNT(*) FROM model_outcomes WHERE stage = '$stage';" 2>/dev/null || echo "0")
|
|
907
|
+
|
|
908
|
+
if [[ "${total_trials:-0}" -lt 5 ]]; then
|
|
909
|
+
echo ""
|
|
910
|
+
return
|
|
911
|
+
fi
|
|
912
|
+
|
|
913
|
+
local models
|
|
914
|
+
models=$(_db_query "SELECT model,
|
|
915
|
+
AVG(success) as mean_reward,
|
|
916
|
+
COUNT(*) as trials,
|
|
917
|
+
AVG(cost_usd) as avg_cost
|
|
918
|
+
FROM model_outcomes
|
|
919
|
+
WHERE stage = '$stage'
|
|
920
|
+
GROUP BY model;" 2>/dev/null || echo "")
|
|
921
|
+
|
|
922
|
+
if [[ -z "$models" ]]; then
|
|
923
|
+
echo "sonnet"
|
|
924
|
+
return
|
|
925
|
+
fi
|
|
926
|
+
|
|
927
|
+
local best_model="sonnet"
|
|
928
|
+
local best_ucb=0
|
|
929
|
+
|
|
930
|
+
while IFS='|' read -r model mean_reward trials avg_cost; do
|
|
931
|
+
[[ -z "$model" ]] && continue
|
|
932
|
+
model=$(echo "$model" | xargs)
|
|
933
|
+
local mean_x1000 exploration ucb
|
|
934
|
+
mean_x1000=$(echo "$mean_reward" | awk '{printf "%d", $1 * 1000}')
|
|
935
|
+
exploration=$(awk "BEGIN { printf \"%d\", 1000 * sqrt(2 * log($total_trials) / $trials) }" 2>/dev/null || echo "0")
|
|
936
|
+
ucb=$((mean_x1000 + exploration))
|
|
937
|
+
|
|
938
|
+
if [[ $ucb -gt $best_ucb ]]; then
|
|
939
|
+
best_ucb=$ucb
|
|
940
|
+
best_model="$model"
|
|
941
|
+
fi
|
|
942
|
+
done <<< "$models"
|
|
943
|
+
|
|
944
|
+
echo "$best_model"
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
# Record model outcome for UCB1 learning
|
|
948
|
+
record_model_outcome() {
|
|
949
|
+
local model="$1" stage="$2" success="${3:-1}" duration="${4:-0}" cost="${5:-0}"
|
|
950
|
+
if db_available 2>/dev/null; then
|
|
951
|
+
model="${model//\'/\'\'}"
|
|
952
|
+
stage="${stage//\'/\'\'}"
|
|
953
|
+
_db_exec "INSERT INTO model_outcomes (model, stage, success, duration_secs, cost_usd, created_at)
|
|
954
|
+
VALUES ('$model', '$stage', $success, $duration, $cost, '$(now_iso)');" 2>/dev/null || true
|
|
955
|
+
fi
|
|
956
|
+
}
|
|
957
|
+
|
|
839
958
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
840
959
|
# RISK KEYWORD LEARNING
|
|
841
960
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -1062,6 +1181,100 @@ optimize_evolve_memory() {
|
|
|
1062
1181
|
success "Memory evolved: pruned=$pruned, strengthened=$strengthened, promoted=$promoted"
|
|
1063
1182
|
}
|
|
1064
1183
|
|
|
1184
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
1185
|
+
# QUALITY INDEX (LONGITUDINAL TRACKING)
|
|
1186
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
1187
|
+
|
|
1188
|
+
# optimize_track_quality_index
|
|
1189
|
+
# Compute composite quality metrics from last N pipeline outcomes and append to quality-index.jsonl
|
|
1190
|
+
optimize_track_quality_index() {
|
|
1191
|
+
local quality_file="${HOME}/.shipwright/optimization/quality-index.jsonl"
|
|
1192
|
+
mkdir -p "$(dirname "$quality_file")"
|
|
1193
|
+
|
|
1194
|
+
local outcomes_file="${HOME}/.shipwright/optimization/outcomes.jsonl"
|
|
1195
|
+
[[ ! -f "$outcomes_file" ]] && return 0
|
|
1196
|
+
|
|
1197
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
1198
|
+
return 0
|
|
1199
|
+
fi
|
|
1200
|
+
|
|
1201
|
+
# Get pipeline outcomes only (exclude retro_summary, ci_metrics)
|
|
1202
|
+
local window=20
|
|
1203
|
+
local recent
|
|
1204
|
+
recent=$(jq -c 'select((.type // "") != "retro_summary" and (.type // "") != "ci_metrics")' "$outcomes_file" 2>/dev/null | tail -"$window" || true)
|
|
1205
|
+
|
|
1206
|
+
[[ -z "$recent" ]] && return 0
|
|
1207
|
+
|
|
1208
|
+
local total success_count
|
|
1209
|
+
total=$(echo "$recent" | wc -l | tr -d ' ')
|
|
1210
|
+
[[ "$total" -lt 3 ]] && return 0
|
|
1211
|
+
|
|
1212
|
+
success_count=$(echo "$recent" | jq -c 'select(.result == "success" or .result == "completed")' 2>/dev/null | wc -l | tr -d ' ')
|
|
1213
|
+
success_count="${success_count:-0}"
|
|
1214
|
+
|
|
1215
|
+
local avg_iterations avg_quality
|
|
1216
|
+
avg_iterations=$(echo "$recent" | jq -s '[.[] | .iterations // 0 | tonumber] | if length > 0 then add / length else 0 end' 2>/dev/null || echo "0")
|
|
1217
|
+
# quality_score: use from record if present, else 100 for success/completed, 0 for failed
|
|
1218
|
+
avg_quality=$(echo "$recent" | jq -s '[.[] | .quality_score // (if (.result == "success" or .result == "completed") then 100 else 0 end) | tonumber] | if length > 0 then add / length else 0 end' 2>/dev/null || echo "0")
|
|
1219
|
+
|
|
1220
|
+
local success_rate=0
|
|
1221
|
+
[[ "$total" -gt 0 ]] && success_rate=$((success_count * 100 / total))
|
|
1222
|
+
[[ "$success_rate" -gt 100 ]] && success_rate=100
|
|
1223
|
+
|
|
1224
|
+
# Efficiency (lower iterations = more efficient)
|
|
1225
|
+
local efficiency
|
|
1226
|
+
efficiency=$(awk "BEGIN{if($avg_iterations > 0) printf \"%.1f\", 100 / $avg_iterations; else print 0}" 2>/dev/null || echo "0")
|
|
1227
|
+
|
|
1228
|
+
# Composite quality index (0-100): success_rate 40%, efficiency 30%, quality 30%
|
|
1229
|
+
local quality_index
|
|
1230
|
+
quality_index=$(awk "BEGIN{printf \"%.0f\", ($success_rate * 0.4) + ($efficiency * 0.3) + ($avg_quality * 0.3)}" 2>/dev/null || echo "0")
|
|
1231
|
+
|
|
1232
|
+
local entry="{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"window\":$window,\"total\":$total,\"success_rate\":$success_rate,\"avg_iterations\":$avg_iterations,\"avg_quality\":$avg_quality,\"efficiency\":$efficiency,\"quality_index\":$quality_index}"
|
|
1233
|
+
echo "$entry" >> "$quality_file"
|
|
1234
|
+
|
|
1235
|
+
# Detect trend
|
|
1236
|
+
if [[ -f "$quality_file" ]]; then
|
|
1237
|
+
local line_count
|
|
1238
|
+
line_count=$(wc -l < "$quality_file" 2>/dev/null | tr -d ' ' || echo "0")
|
|
1239
|
+
if [[ "$line_count" -ge 2 ]]; then
|
|
1240
|
+
local prev_index
|
|
1241
|
+
prev_index=$(tail -2 "$quality_file" | head -1 | jq -r '.quality_index // 0' 2>/dev/null || echo "0")
|
|
1242
|
+
local delta
|
|
1243
|
+
delta=$(awk "BEGIN{printf \"%.0f\", $quality_index - $prev_index}" 2>/dev/null || echo "0")
|
|
1244
|
+
|
|
1245
|
+
if [[ "$delta" -gt 5 ]]; then
|
|
1246
|
+
info "Quality index: $quality_index (+${delta}) - IMPROVING"
|
|
1247
|
+
elif [[ "$delta" -lt -5 ]]; then
|
|
1248
|
+
warn "Quality index: $quality_index (${delta}) - DECLINING"
|
|
1249
|
+
else
|
|
1250
|
+
info "Quality index: $quality_index (stable)"
|
|
1251
|
+
fi
|
|
1252
|
+
fi
|
|
1253
|
+
fi
|
|
1254
|
+
|
|
1255
|
+
emit_event "quality.index_updated" "quality_index=$quality_index" "success_rate=$success_rate" 2>/dev/null || true
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
# cmd_quality_index — Show quality trend (last 10 snapshots)
|
|
1259
|
+
cmd_quality_index() {
|
|
1260
|
+
local quality_file="${HOME}/.shipwright/optimization/quality-index.jsonl"
|
|
1261
|
+
if [[ ! -f "$quality_file" ]]; then
|
|
1262
|
+
echo "No quality data yet. Run some pipelines first."
|
|
1263
|
+
return
|
|
1264
|
+
fi
|
|
1265
|
+
|
|
1266
|
+
echo "Quality Index Trend (last 10 snapshots):"
|
|
1267
|
+
echo "========================================="
|
|
1268
|
+
tail -10 "$quality_file" | while IFS= read -r line; do
|
|
1269
|
+
local ts qi sr ai
|
|
1270
|
+
ts=$(echo "$line" | jq -r '.timestamp' 2>/dev/null)
|
|
1271
|
+
qi=$(echo "$line" | jq -r '.quality_index' 2>/dev/null)
|
|
1272
|
+
sr=$(echo "$line" | jq -r '.success_rate' 2>/dev/null)
|
|
1273
|
+
ai=$(echo "$line" | jq -r '.avg_iterations' 2>/dev/null)
|
|
1274
|
+
printf " %s QI: %s Success: %s%% Avg Iters: %s\n" "$ts" "$qi" "$sr" "$ai"
|
|
1275
|
+
done
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1065
1278
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
1066
1279
|
# FULL ANALYSIS (DAILY)
|
|
1067
1280
|
# ═════════════════════════════════════════════════════════════════════════════
|
|
@@ -1082,6 +1295,7 @@ optimize_full_analysis() {
|
|
|
1082
1295
|
optimize_route_models
|
|
1083
1296
|
optimize_learn_risk_keywords
|
|
1084
1297
|
optimize_evolve_memory
|
|
1298
|
+
optimize_track_quality_index 2>/dev/null || true
|
|
1085
1299
|
optimize_report >> "${OPTIMIZATION_DIR}/last-report.txt" 2>/dev/null || true
|
|
1086
1300
|
optimize_adjust_audit_intensity 2>/dev/null || true
|
|
1087
1301
|
|
|
@@ -1272,6 +1486,7 @@ show_help() {
|
|
|
1272
1486
|
echo " analyze-outcome <state-file> Analyze a completed pipeline outcome"
|
|
1273
1487
|
echo " tune Run full optimization analysis"
|
|
1274
1488
|
echo " report Show optimization report (last 7 days)"
|
|
1489
|
+
echo " quality-index Show quality trend (last 10 snapshots)"
|
|
1275
1490
|
echo " ingest-retro Ingest most recent retro into optimization loop"
|
|
1276
1491
|
echo " evolve-memory Prune/strengthen/promote memory patterns"
|
|
1277
1492
|
echo " help Show this help"
|
|
@@ -1296,6 +1511,7 @@ main() {
|
|
|
1296
1511
|
tune) optimize_full_analysis ;;
|
|
1297
1512
|
ingest-retro) optimize_ingest_retro ;;
|
|
1298
1513
|
report) optimize_report ;;
|
|
1514
|
+
quality-index) cmd_quality_index ;;
|
|
1299
1515
|
evolve-memory) optimize_evolve_memory ;;
|
|
1300
1516
|
help|--help|-h) show_help ;;
|
|
1301
1517
|
*) error "Unknown command: $cmd"; exit 1 ;;
|
package/scripts/sw-session.sh
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# ║ Supports --template to scaffold from a team template and --terminal ║
|
|
9
9
|
# ║ to select a terminal adapter (tmux, iterm2, wezterm). ║
|
|
10
10
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
11
|
-
VERSION="
|
|
11
|
+
VERSION="3.0.0"
|
|
12
12
|
set -euo pipefail
|
|
13
13
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
14
14
|
|
|
@@ -38,16 +38,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
38
38
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
39
39
|
}
|
|
40
40
|
fi
|
|
41
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
42
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
43
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
44
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
45
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
46
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
47
|
-
DIM="${DIM:-\033[2m}"
|
|
48
|
-
BOLD="${BOLD:-\033[1m}"
|
|
49
|
-
RESET="${RESET:-\033[0m}"
|
|
50
|
-
|
|
51
41
|
# ─── Parse Arguments ────────────────────────────────────────────────────────
|
|
52
42
|
|
|
53
43
|
TEAM_NAME=""
|
|
@@ -207,7 +197,7 @@ if [[ -n "$TEMPLATE_NAME" ]]; then
|
|
|
207
197
|
info "Loading template: ${PURPLE}${BOLD}${TEMPLATE_NAME}${RESET}"
|
|
208
198
|
|
|
209
199
|
# Parse template — single jq call extracts all fields + agents in one pass
|
|
210
|
-
if command -v jq
|
|
200
|
+
if command -v jq >/dev/null 2>&1; then
|
|
211
201
|
# Single jq call: outputs metadata lines then agent lines
|
|
212
202
|
# Format: META<tab>field<tab>value for metadata, AGENT<tab>name|role|focus for agents
|
|
213
203
|
while IFS=$'\t' read -r tag key value; do
|
|
@@ -508,7 +498,7 @@ LAUNCHER_STATIC
|
|
|
508
498
|
tmux select-pane -t "$WINDOW_NAME" -P 'bg=#1a1a2e,fg=#e4e4e7' 2>/dev/null || true
|
|
509
499
|
} &
|
|
510
500
|
|
|
511
|
-
elif [[ -f "$ADAPTER_FILE" ]] && type -t spawn_agent
|
|
501
|
+
elif [[ -f "$ADAPTER_FILE" ]] && type -t spawn_agent >/dev/null 2>&1; then
|
|
512
502
|
# ─── Non-tmux adapter session (iterm2, wezterm, etc.) ──────────────────
|
|
513
503
|
info "Creating team session: ${CYAN}${BOLD}${TEAM_NAME}${RESET} ${DIM}(${TERMINAL_ADAPTER})${RESET}"
|
|
514
504
|
|
package/scripts/sw-setup.sh
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
12
12
|
|
|
13
|
-
VERSION="
|
|
13
|
+
VERSION="3.0.0"
|
|
14
14
|
|
|
15
15
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
16
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
@@ -39,16 +39,6 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
39
39
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
40
40
|
}
|
|
41
41
|
fi
|
|
42
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
43
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
44
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
45
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
46
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
47
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
48
|
-
DIM="${DIM:-\033[2m}"
|
|
49
|
-
BOLD="${BOLD:-\033[1m}"
|
|
50
|
-
RESET="${RESET:-\033[0m}"
|
|
51
|
-
|
|
52
42
|
PASS=0
|
|
53
43
|
WARN=0
|
|
54
44
|
FAIL=0
|
|
@@ -128,7 +118,7 @@ OPTIONAL_TOOLS=("bun")
|
|
|
128
118
|
for tool in "${REQUIRED_TOOLS[@]}"; do
|
|
129
119
|
case "$tool" in
|
|
130
120
|
tmux)
|
|
131
|
-
if command -v tmux
|
|
121
|
+
if command -v tmux >/dev/null 2>&1; then
|
|
132
122
|
TMUX_VERSION="$(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"
|
|
133
123
|
TMUX_MAJOR="$(echo "$TMUX_VERSION" | cut -d. -f1)"
|
|
134
124
|
TMUX_MINOR="$(echo "$TMUX_VERSION" | cut -d. -f2 | tr -dc '0-9')"
|
|
@@ -153,7 +143,7 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
|
|
|
153
143
|
fi
|
|
154
144
|
;;
|
|
155
145
|
git)
|
|
156
|
-
if command -v git
|
|
146
|
+
if command -v git >/dev/null 2>&1; then
|
|
157
147
|
check_pass "git $(git --version | awk '{print $3}')"
|
|
158
148
|
else
|
|
159
149
|
check_fail "git not installed"
|
|
@@ -161,7 +151,7 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
|
|
|
161
151
|
fi
|
|
162
152
|
;;
|
|
163
153
|
jq)
|
|
164
|
-
if command -v jq
|
|
154
|
+
if command -v jq >/dev/null 2>&1; then
|
|
165
155
|
check_pass "jq $(jq --version 2>&1 | tr -d 'jq-')"
|
|
166
156
|
else
|
|
167
157
|
check_fail "jq not installed"
|
|
@@ -169,8 +159,8 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
|
|
|
169
159
|
fi
|
|
170
160
|
;;
|
|
171
161
|
gh)
|
|
172
|
-
if command -v gh
|
|
173
|
-
if gh auth status
|
|
162
|
+
if command -v gh >/dev/null 2>&1; then
|
|
163
|
+
if gh auth status >/dev/null 2>&1; then
|
|
174
164
|
GH_USER="$(gh api user -q .login 2>/dev/null || echo "authenticated")"
|
|
175
165
|
check_pass "GitHub CLI: ${GH_USER}"
|
|
176
166
|
else
|
|
@@ -183,7 +173,7 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
|
|
|
183
173
|
fi
|
|
184
174
|
;;
|
|
185
175
|
claude)
|
|
186
|
-
if command -v claude
|
|
176
|
+
if command -v claude >/dev/null 2>&1; then
|
|
187
177
|
check_pass "Claude Code CLI"
|
|
188
178
|
else
|
|
189
179
|
check_fail "Claude Code CLI not found"
|
|
@@ -199,7 +189,7 @@ echo ""
|
|
|
199
189
|
for tool in "${OPTIONAL_TOOLS[@]}"; do
|
|
200
190
|
case "$tool" in
|
|
201
191
|
bun)
|
|
202
|
-
if command -v bun
|
|
192
|
+
if command -v bun >/dev/null 2>&1; then
|
|
203
193
|
check_pass "Bun (dashboard server)"
|
|
204
194
|
else
|
|
205
195
|
check_warn "Bun not installed (optional — for dashboard)"
|
package/scripts/sw-standup.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
|
# ─── Constants ──────────────────────────────────────────────────────────────
|
|
48
38
|
STANDUP_DIR="${HOME}/.shipwright/standups"
|
|
49
39
|
EVENTS_FILE="${HOME}/.shipwright/events.jsonl"
|
|
@@ -62,7 +52,7 @@ ensure_dirs() {
|
|
|
62
52
|
# Convert ISO 8601 timestamp to epoch seconds (works on macOS and Linux)
|
|
63
53
|
iso_to_epoch() {
|
|
64
54
|
local iso="$1"
|
|
65
|
-
if TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$iso" +%s
|
|
55
|
+
if TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$iso" +%s >/dev/null 2>&1; then
|
|
66
56
|
TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$iso" +%s 2>/dev/null || echo 0
|
|
67
57
|
else
|
|
68
58
|
date -d "$iso" +%s 2>/dev/null || echo 0
|
|
@@ -533,9 +523,9 @@ cmd_notify() {
|
|
|
533
523
|
]
|
|
534
524
|
}')
|
|
535
525
|
|
|
536
|
-
if command -v curl
|
|
537
|
-
if curl -s -X POST -H 'Content-type: application/json' \
|
|
538
|
-
--data "$payload" "$webhook_url"
|
|
526
|
+
if command -v curl >/dev/null 2>&1; then
|
|
527
|
+
if curl -s --connect-timeout 10 --max-time 30 -X POST -H 'Content-type: application/json' \
|
|
528
|
+
--data "$payload" "$webhook_url" >/dev/null 2>&1; then
|
|
539
529
|
success "Standup delivered to webhook"
|
|
540
530
|
else
|
|
541
531
|
error "Failed to deliver standup to webhook"
|