shipwright-cli 3.1.0 → 3.3.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.
Files changed (283) hide show
  1. package/.claude/agents/code-reviewer.md +2 -0
  2. package/.claude/agents/devops-engineer.md +2 -0
  3. package/.claude/agents/doc-fleet-agent.md +2 -0
  4. package/.claude/agents/pipeline-agent.md +2 -0
  5. package/.claude/agents/shell-script-specialist.md +2 -0
  6. package/.claude/agents/test-specialist.md +2 -0
  7. package/.claude/hooks/agent-crash-capture.sh +32 -0
  8. package/.claude/hooks/post-tool-use.sh +3 -2
  9. package/.claude/hooks/pre-tool-use.sh +35 -3
  10. package/README.md +22 -8
  11. package/claude-code/hooks/config-change.sh +18 -0
  12. package/claude-code/hooks/instructions-reloaded.sh +7 -0
  13. package/claude-code/hooks/worktree-create.sh +25 -0
  14. package/claude-code/hooks/worktree-remove.sh +20 -0
  15. package/config/code-constitution.json +130 -0
  16. package/config/defaults.json +25 -2
  17. package/config/policy.json +1 -1
  18. package/dashboard/middleware/auth.ts +134 -0
  19. package/dashboard/middleware/constants.ts +21 -0
  20. package/dashboard/public/index.html +8 -6
  21. package/dashboard/public/styles.css +176 -97
  22. package/dashboard/routes/auth.ts +38 -0
  23. package/dashboard/server.ts +117 -25
  24. package/dashboard/services/config.ts +26 -0
  25. package/dashboard/services/db.ts +118 -0
  26. package/dashboard/src/canvas/pixel-agent.ts +298 -0
  27. package/dashboard/src/canvas/pixel-sprites.ts +440 -0
  28. package/dashboard/src/canvas/shipyard-effects.ts +367 -0
  29. package/dashboard/src/canvas/shipyard-scene.ts +616 -0
  30. package/dashboard/src/canvas/submarine-layout.ts +267 -0
  31. package/dashboard/src/components/header.ts +8 -7
  32. package/dashboard/src/core/api.ts +5 -0
  33. package/dashboard/src/core/router.ts +1 -0
  34. package/dashboard/src/design/submarine-theme.ts +253 -0
  35. package/dashboard/src/main.ts +2 -0
  36. package/dashboard/src/types/api.ts +12 -1
  37. package/dashboard/src/views/activity.ts +2 -1
  38. package/dashboard/src/views/metrics.ts +69 -1
  39. package/dashboard/src/views/shipyard.ts +39 -0
  40. package/dashboard/types/index.ts +166 -0
  41. package/docs/plans/2026-02-28-compound-audit-and-shipyard-design.md +186 -0
  42. package/docs/plans/2026-02-28-skipper-shipwright-implementation-plan.md +1182 -0
  43. package/docs/plans/2026-02-28-skipper-shipwright-integration-design.md +531 -0
  44. package/docs/plans/2026-03-01-ai-powered-skill-injection-design.md +298 -0
  45. package/docs/plans/2026-03-01-ai-powered-skill-injection-plan.md +1109 -0
  46. package/docs/plans/2026-03-01-capabilities-cleanup-plan.md +658 -0
  47. package/docs/plans/2026-03-01-clean-architecture-plan.md +924 -0
  48. package/docs/plans/2026-03-01-compound-audit-cascade-design.md +191 -0
  49. package/docs/plans/2026-03-01-compound-audit-cascade-plan.md +921 -0
  50. package/docs/plans/2026-03-01-deep-integration-plan.md +851 -0
  51. package/docs/plans/2026-03-01-pipeline-audit-trail-design.md +145 -0
  52. package/docs/plans/2026-03-01-pipeline-audit-trail-plan.md +770 -0
  53. package/docs/plans/2026-03-01-refined-depths-brand-design.md +382 -0
  54. package/docs/plans/2026-03-01-refined-depths-implementation.md +599 -0
  55. package/docs/plans/2026-03-01-skipper-kernel-integration-design.md +203 -0
  56. package/docs/plans/2026-03-01-unified-platform-design.md +272 -0
  57. package/docs/plans/2026-03-07-claude-code-feature-integration-design.md +189 -0
  58. package/docs/plans/2026-03-07-claude-code-feature-integration-plan.md +1165 -0
  59. package/docs/research/BACKLOG_QUICK_REFERENCE.md +352 -0
  60. package/docs/research/CUTTING_EDGE_RESEARCH_2026.md +546 -0
  61. package/docs/research/RESEARCH_INDEX.md +439 -0
  62. package/docs/research/RESEARCH_SOURCES.md +440 -0
  63. package/docs/research/RESEARCH_SUMMARY.txt +275 -0
  64. package/docs/superpowers/specs/2026-03-10-pipeline-quality-revolution-design.md +341 -0
  65. package/package.json +2 -2
  66. package/scripts/lib/adaptive-model.sh +427 -0
  67. package/scripts/lib/adaptive-timeout.sh +316 -0
  68. package/scripts/lib/audit-trail.sh +309 -0
  69. package/scripts/lib/auto-recovery.sh +471 -0
  70. package/scripts/lib/bandit-selector.sh +431 -0
  71. package/scripts/lib/bootstrap.sh +104 -2
  72. package/scripts/lib/causal-graph.sh +455 -0
  73. package/scripts/lib/compat.sh +126 -0
  74. package/scripts/lib/compound-audit.sh +337 -0
  75. package/scripts/lib/constitutional.sh +454 -0
  76. package/scripts/lib/context-budget.sh +359 -0
  77. package/scripts/lib/convergence.sh +594 -0
  78. package/scripts/lib/cost-optimizer.sh +634 -0
  79. package/scripts/lib/daemon-adaptive.sh +14 -2
  80. package/scripts/lib/daemon-dispatch.sh +106 -17
  81. package/scripts/lib/daemon-failure.sh +34 -4
  82. package/scripts/lib/daemon-patrol.sh +25 -4
  83. package/scripts/lib/daemon-poll-github.sh +361 -0
  84. package/scripts/lib/daemon-poll-health.sh +299 -0
  85. package/scripts/lib/daemon-poll.sh +27 -611
  86. package/scripts/lib/daemon-state.sh +119 -66
  87. package/scripts/lib/daemon-triage.sh +10 -0
  88. package/scripts/lib/dod-scorecard.sh +442 -0
  89. package/scripts/lib/error-actionability.sh +300 -0
  90. package/scripts/lib/formal-spec.sh +461 -0
  91. package/scripts/lib/helpers.sh +180 -5
  92. package/scripts/lib/intent-analysis.sh +409 -0
  93. package/scripts/lib/loop-convergence.sh +350 -0
  94. package/scripts/lib/loop-iteration.sh +682 -0
  95. package/scripts/lib/loop-progress.sh +48 -0
  96. package/scripts/lib/loop-restart.sh +185 -0
  97. package/scripts/lib/memory-effectiveness.sh +506 -0
  98. package/scripts/lib/mutation-executor.sh +352 -0
  99. package/scripts/lib/outcome-feedback.sh +521 -0
  100. package/scripts/lib/pipeline-cli.sh +336 -0
  101. package/scripts/lib/pipeline-commands.sh +1216 -0
  102. package/scripts/lib/pipeline-detection.sh +101 -3
  103. package/scripts/lib/pipeline-execution.sh +897 -0
  104. package/scripts/lib/pipeline-github.sh +28 -3
  105. package/scripts/lib/pipeline-intelligence-compound.sh +431 -0
  106. package/scripts/lib/pipeline-intelligence-scoring.sh +407 -0
  107. package/scripts/lib/pipeline-intelligence-skip.sh +181 -0
  108. package/scripts/lib/pipeline-intelligence.sh +104 -1138
  109. package/scripts/lib/pipeline-quality-bash-compat.sh +182 -0
  110. package/scripts/lib/pipeline-quality-checks.sh +17 -711
  111. package/scripts/lib/pipeline-quality-gates.sh +563 -0
  112. package/scripts/lib/pipeline-stages-build.sh +730 -0
  113. package/scripts/lib/pipeline-stages-delivery.sh +965 -0
  114. package/scripts/lib/pipeline-stages-intake.sh +1133 -0
  115. package/scripts/lib/pipeline-stages-monitor.sh +407 -0
  116. package/scripts/lib/pipeline-stages-review.sh +1022 -0
  117. package/scripts/lib/pipeline-stages.sh +161 -2901
  118. package/scripts/lib/pipeline-state.sh +36 -5
  119. package/scripts/lib/pipeline-util.sh +487 -0
  120. package/scripts/lib/policy-learner.sh +438 -0
  121. package/scripts/lib/process-reward.sh +493 -0
  122. package/scripts/lib/project-detect.sh +649 -0
  123. package/scripts/lib/quality-profile.sh +334 -0
  124. package/scripts/lib/recruit-commands.sh +885 -0
  125. package/scripts/lib/recruit-learning.sh +739 -0
  126. package/scripts/lib/recruit-roles.sh +648 -0
  127. package/scripts/lib/reward-aggregator.sh +458 -0
  128. package/scripts/lib/rl-optimizer.sh +362 -0
  129. package/scripts/lib/root-cause.sh +427 -0
  130. package/scripts/lib/scope-enforcement.sh +445 -0
  131. package/scripts/lib/session-restart.sh +493 -0
  132. package/scripts/lib/skill-memory.sh +300 -0
  133. package/scripts/lib/skill-registry.sh +775 -0
  134. package/scripts/lib/spec-driven.sh +476 -0
  135. package/scripts/lib/test-helpers.sh +18 -7
  136. package/scripts/lib/test-holdout.sh +429 -0
  137. package/scripts/lib/test-optimizer.sh +511 -0
  138. package/scripts/shipwright-file-suggest.sh +45 -0
  139. package/scripts/skills/adversarial-quality.md +61 -0
  140. package/scripts/skills/api-design.md +44 -0
  141. package/scripts/skills/architecture-design.md +50 -0
  142. package/scripts/skills/brainstorming.md +43 -0
  143. package/scripts/skills/data-pipeline.md +44 -0
  144. package/scripts/skills/deploy-safety.md +64 -0
  145. package/scripts/skills/documentation.md +38 -0
  146. package/scripts/skills/frontend-design.md +45 -0
  147. package/scripts/skills/generated/.gitkeep +0 -0
  148. package/scripts/skills/generated/_refinements/.gitkeep +0 -0
  149. package/scripts/skills/generated/_refinements/adversarial-quality.patch.md +3 -0
  150. package/scripts/skills/generated/_refinements/architecture-design.patch.md +3 -0
  151. package/scripts/skills/generated/_refinements/brainstorming.patch.md +3 -0
  152. package/scripts/skills/generated/cli-version-management.md +29 -0
  153. package/scripts/skills/generated/collection-system-validation.md +99 -0
  154. package/scripts/skills/generated/large-scale-c-refactoring-coordination.md +97 -0
  155. package/scripts/skills/generated/pattern-matching-similarity-scoring.md +195 -0
  156. package/scripts/skills/generated/test-parallelization-detection.md +65 -0
  157. package/scripts/skills/observability.md +79 -0
  158. package/scripts/skills/performance.md +48 -0
  159. package/scripts/skills/pr-quality.md +49 -0
  160. package/scripts/skills/product-thinking.md +43 -0
  161. package/scripts/skills/security-audit.md +49 -0
  162. package/scripts/skills/systematic-debugging.md +40 -0
  163. package/scripts/skills/testing-strategy.md +47 -0
  164. package/scripts/skills/two-stage-review.md +52 -0
  165. package/scripts/skills/validation-thoroughness.md +55 -0
  166. package/scripts/sw +9 -3
  167. package/scripts/sw-activity.sh +9 -8
  168. package/scripts/sw-adaptive.sh +8 -7
  169. package/scripts/sw-adversarial.sh +2 -1
  170. package/scripts/sw-architecture-enforcer.sh +3 -1
  171. package/scripts/sw-auth.sh +12 -2
  172. package/scripts/sw-autonomous.sh +5 -1
  173. package/scripts/sw-changelog.sh +4 -1
  174. package/scripts/sw-checkpoint.sh +2 -1
  175. package/scripts/sw-ci.sh +15 -6
  176. package/scripts/sw-cleanup.sh +4 -26
  177. package/scripts/sw-code-review.sh +45 -20
  178. package/scripts/sw-connect.sh +2 -1
  179. package/scripts/sw-context.sh +2 -1
  180. package/scripts/sw-cost.sh +107 -5
  181. package/scripts/sw-daemon.sh +71 -11
  182. package/scripts/sw-dashboard.sh +3 -1
  183. package/scripts/sw-db.sh +71 -20
  184. package/scripts/sw-decide.sh +8 -2
  185. package/scripts/sw-decompose.sh +360 -17
  186. package/scripts/sw-deps.sh +4 -1
  187. package/scripts/sw-developer-simulation.sh +4 -1
  188. package/scripts/sw-discovery.sh +378 -5
  189. package/scripts/sw-doc-fleet.sh +4 -1
  190. package/scripts/sw-docs-agent.sh +3 -1
  191. package/scripts/sw-docs.sh +2 -1
  192. package/scripts/sw-doctor.sh +453 -2
  193. package/scripts/sw-dora.sh +4 -1
  194. package/scripts/sw-durable.sh +12 -7
  195. package/scripts/sw-e2e-orchestrator.sh +17 -16
  196. package/scripts/sw-eventbus.sh +13 -4
  197. package/scripts/sw-evidence.sh +364 -12
  198. package/scripts/sw-feedback.sh +550 -9
  199. package/scripts/sw-fix.sh +20 -1
  200. package/scripts/sw-fleet-discover.sh +6 -2
  201. package/scripts/sw-fleet-viz.sh +9 -4
  202. package/scripts/sw-fleet.sh +5 -1
  203. package/scripts/sw-github-app.sh +18 -4
  204. package/scripts/sw-github-checks.sh +3 -2
  205. package/scripts/sw-github-deploy.sh +3 -2
  206. package/scripts/sw-github-graphql.sh +18 -7
  207. package/scripts/sw-guild.sh +5 -1
  208. package/scripts/sw-heartbeat.sh +5 -30
  209. package/scripts/sw-hello.sh +67 -0
  210. package/scripts/sw-hygiene.sh +10 -3
  211. package/scripts/sw-incident.sh +273 -5
  212. package/scripts/sw-init.sh +18 -2
  213. package/scripts/sw-instrument.sh +10 -2
  214. package/scripts/sw-intelligence.sh +44 -7
  215. package/scripts/sw-jira.sh +5 -1
  216. package/scripts/sw-launchd.sh +2 -1
  217. package/scripts/sw-linear.sh +4 -1
  218. package/scripts/sw-logs.sh +4 -1
  219. package/scripts/sw-loop.sh +436 -1076
  220. package/scripts/sw-memory.sh +357 -3
  221. package/scripts/sw-mission-control.sh +6 -1
  222. package/scripts/sw-model-router.sh +483 -27
  223. package/scripts/sw-otel.sh +15 -4
  224. package/scripts/sw-oversight.sh +14 -5
  225. package/scripts/sw-patrol-meta.sh +334 -0
  226. package/scripts/sw-pipeline-composer.sh +7 -1
  227. package/scripts/sw-pipeline-vitals.sh +12 -6
  228. package/scripts/sw-pipeline.sh +54 -2653
  229. package/scripts/sw-pm.sh +16 -8
  230. package/scripts/sw-pr-lifecycle.sh +2 -1
  231. package/scripts/sw-predictive.sh +17 -5
  232. package/scripts/sw-prep.sh +185 -2
  233. package/scripts/sw-ps.sh +5 -25
  234. package/scripts/sw-public-dashboard.sh +17 -4
  235. package/scripts/sw-quality.sh +14 -6
  236. package/scripts/sw-reaper.sh +8 -25
  237. package/scripts/sw-recruit.sh +156 -2303
  238. package/scripts/sw-regression.sh +19 -12
  239. package/scripts/sw-release-manager.sh +3 -1
  240. package/scripts/sw-release.sh +4 -1
  241. package/scripts/sw-remote.sh +3 -1
  242. package/scripts/sw-replay.sh +7 -1
  243. package/scripts/sw-retro.sh +158 -1
  244. package/scripts/sw-review-rerun.sh +3 -1
  245. package/scripts/sw-scale.sh +14 -5
  246. package/scripts/sw-security-audit.sh +6 -1
  247. package/scripts/sw-self-optimize.sh +173 -6
  248. package/scripts/sw-session.sh +9 -3
  249. package/scripts/sw-setup.sh +3 -1
  250. package/scripts/sw-stall-detector.sh +406 -0
  251. package/scripts/sw-standup.sh +15 -7
  252. package/scripts/sw-status.sh +3 -1
  253. package/scripts/sw-strategic.sh +14 -6
  254. package/scripts/sw-stream.sh +13 -4
  255. package/scripts/sw-swarm.sh +20 -7
  256. package/scripts/sw-team-stages.sh +13 -6
  257. package/scripts/sw-templates.sh +7 -31
  258. package/scripts/sw-testgen.sh +17 -6
  259. package/scripts/sw-tmux-pipeline.sh +4 -1
  260. package/scripts/sw-tmux-role-color.sh +2 -0
  261. package/scripts/sw-tmux-status.sh +1 -1
  262. package/scripts/sw-tmux.sh +37 -1
  263. package/scripts/sw-trace.sh +3 -1
  264. package/scripts/sw-tracker-github.sh +3 -0
  265. package/scripts/sw-tracker-jira.sh +3 -0
  266. package/scripts/sw-tracker-linear.sh +3 -0
  267. package/scripts/sw-tracker.sh +3 -1
  268. package/scripts/sw-triage.sh +3 -2
  269. package/scripts/sw-upgrade.sh +3 -1
  270. package/scripts/sw-ux.sh +5 -2
  271. package/scripts/sw-webhook.sh +5 -2
  272. package/scripts/sw-widgets.sh +9 -4
  273. package/scripts/sw-worktree.sh +15 -3
  274. package/scripts/test-skill-injection.sh +1233 -0
  275. package/templates/pipelines/autonomous.json +27 -3
  276. package/templates/pipelines/cost-aware.json +34 -8
  277. package/templates/pipelines/deployed.json +12 -0
  278. package/templates/pipelines/enterprise.json +12 -0
  279. package/templates/pipelines/fast.json +6 -0
  280. package/templates/pipelines/full.json +27 -3
  281. package/templates/pipelines/hotfix.json +6 -0
  282. package/templates/pipelines/standard.json +12 -0
  283. package/templates/pipelines/tdd.json +12 -0
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env bash
2
+ # Module guard - prevent double-sourcing
3
+ [[ -n "${_BANDIT_SELECTOR_LOADED:-}" ]] && return 0
4
+ _BANDIT_SELECTOR_LOADED=1
5
+
6
+ # ╔═══════════════════════════════════════════════════════════════════════════╗
7
+ # ║ shipwright bandit-selector — Thompson Sampling for Model/Template ║
8
+ # ║ Multi-armed bandit: Beta(α,β) priors, Thompson sampling selection ║
9
+ # ║ Replaces static model routing with learned optimal selection ║
10
+ # ╚═══════════════════════════════════════════════════════════════════════════╝
11
+
12
+ # shellcheck disable=SC2034
13
+ VERSION="3.3.0"
14
+
15
+ # ─── Output Helpers ──────────────────────────────────────────────────────────
16
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
17
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
18
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
19
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
20
+
21
+ # ─── Configuration ───────────────────────────────────────────────────────────
22
+
23
+ BANDIT_STATE_FILE="${BANDIT_STATE_FILE:-$HOME/.shipwright/bandits.json}"
24
+
25
+ # Default models and stages for arm initialization
26
+ BANDIT_MODELS="${BANDIT_MODELS:-haiku,sonnet,opus}"
27
+ BANDIT_STAGES="${BANDIT_STAGES:-build,test,review,design,plan,intake,pr}"
28
+ BANDIT_TEMPLATES="${BANDIT_TEMPLATES:-fast,standard,full,hotfix,autonomous}"
29
+ BANDIT_ISSUE_TYPES="${BANDIT_ISSUE_TYPES:-bug,feature,refactor,docs}"
30
+
31
+ # ─── Beta Sampling ───────────────────────────────────────────────────────────
32
+ # Sample from Beta(alpha, beta) using Gamma decomposition.
33
+ # If X~Gamma(a,1) and Y~Gamma(b,1), then X/(X+Y)~Beta(a,b).
34
+ # Gamma(k,1) for integer k = sum of k Exponential(1) samples.
35
+
36
+ _beta_sample() {
37
+ local alpha="$1" beta="$2"
38
+ awk -v a="$alpha" -v b="$beta" -v seed="$RANDOM" 'BEGIN {
39
+ srand(seed)
40
+ x = 0; y = 0
41
+ for (i = 0; i < a; i++) { r = rand(); if (r < 1e-10) r = 1e-10; x += -log(r) }
42
+ for (i = 0; i < b; i++) { r = rand(); if (r < 1e-10) r = 1e-10; y += -log(r) }
43
+ if (x + y > 0) printf "%.6f", x / (x + y)
44
+ else printf "0.500000"
45
+ }'
46
+ }
47
+
48
+ # ─── JSON Helpers ────────────────────────────────────────────────────────────
49
+
50
+ _bandit_ensure_dir() {
51
+ local dir
52
+ dir="$(dirname "$BANDIT_STATE_FILE")"
53
+ [[ -d "$dir" ]] || mkdir -p "$dir"
54
+ }
55
+
56
+ _bandit_read_state() {
57
+ if [[ -f "$BANDIT_STATE_FILE" ]]; then
58
+ cat "$BANDIT_STATE_FILE"
59
+ else
60
+ echo '{}'
61
+ fi
62
+ }
63
+
64
+ _bandit_write_state() {
65
+ local json="$1"
66
+ _bandit_ensure_dir
67
+ local tmp_file="${BANDIT_STATE_FILE}.tmp.$$"
68
+ echo "$json" > "$tmp_file"
69
+ mv "$tmp_file" "$BANDIT_STATE_FILE"
70
+ }
71
+
72
+ # ─── Arm Key Helpers ─────────────────────────────────────────────────────────
73
+
74
+ _arm_key() {
75
+ # Create arm key from context:value pair, e.g. "build:haiku"
76
+ echo "${1}:${2}"
77
+ }
78
+
79
+ # ─── Core Functions ──────────────────────────────────────────────────────────
80
+
81
+ # Initialize Beta(1,1) distributions for all model and template arms.
82
+ # Idempotent: does not overwrite existing arms.
83
+ bandit_init() {
84
+ _bandit_ensure_dir
85
+ local state
86
+ state="$(_bandit_read_state)"
87
+
88
+ # Ensure model_arms and template_arms keys exist
89
+ local has_model_arms has_template_arms
90
+ has_model_arms=$(echo "$state" | jq -r 'has("model_arms")' 2>/dev/null || echo "false")
91
+ has_template_arms=$(echo "$state" | jq -r 'has("template_arms")' 2>/dev/null || echo "false")
92
+
93
+ if [[ "$has_model_arms" != "true" ]]; then
94
+ state=$(echo "$state" | jq '. + {"model_arms": {}}')
95
+ fi
96
+ if [[ "$has_template_arms" != "true" ]]; then
97
+ state=$(echo "$state" | jq '. + {"template_arms": {}}')
98
+ fi
99
+
100
+ # Initialize model arms: stage × model
101
+ local IFS_OLD="$IFS"
102
+ IFS=','
103
+ local stages_arr=($BANDIT_STAGES)
104
+ local models_arr=($BANDIT_MODELS)
105
+ local templates_arr=($BANDIT_TEMPLATES)
106
+ local types_arr=($BANDIT_ISSUE_TYPES)
107
+ IFS="$IFS_OLD"
108
+
109
+ local stage model key existing
110
+ for stage in "${stages_arr[@]}"; do
111
+ for model in "${models_arr[@]}"; do
112
+ key="$(_arm_key "$stage" "$model")"
113
+ existing=$(echo "$state" | jq -r --arg k "$key" '.model_arms | has($k)' 2>/dev/null || echo "false")
114
+ if [[ "$existing" != "true" ]]; then
115
+ state=$(echo "$state" | jq --arg k "$key" \
116
+ '.model_arms[$k] = {"alpha": 1, "beta": 1, "pulls": 0, "successes": 0}')
117
+ fi
118
+ done
119
+ done
120
+
121
+ # Initialize template arms: issue_type × template
122
+ local itype tmpl
123
+ for itype in "${types_arr[@]}"; do
124
+ for tmpl in "${templates_arr[@]}"; do
125
+ key="$(_arm_key "$itype" "$tmpl")"
126
+ existing=$(echo "$state" | jq -r --arg k "$key" '.template_arms | has($k)' 2>/dev/null || echo "false")
127
+ if [[ "$existing" != "true" ]]; then
128
+ state=$(echo "$state" | jq --arg k "$key" \
129
+ '.template_arms[$k] = {"alpha": 1, "beta": 1, "pulls": 0, "successes": 0}')
130
+ fi
131
+ done
132
+ done
133
+
134
+ # Add metadata
135
+ state=$(echo "$state" | jq --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
136
+ '. + {"initialized_at": $ts, "version": "3.2.4"}')
137
+
138
+ _bandit_write_state "$state"
139
+ info "Bandit arms initialized ($(echo "$state" | jq '.model_arms | length') model, $(echo "$state" | jq '.template_arms | length') template)"
140
+ }
141
+
142
+ # Select model for a given stage via Thompson sampling.
143
+ # Usage: bandit_select_model <stage> [models_csv]
144
+ # Output: model_name (to stdout)
145
+ # Returns 0 on success, 1 on error.
146
+ bandit_select_model() {
147
+ local stage="${1:-build}"
148
+ local models_csv="${2:-$BANDIT_MODELS}"
149
+
150
+ local state
151
+ state="$(_bandit_read_state)"
152
+
153
+ # Check if state has model_arms
154
+ local has_arms
155
+ has_arms=$(echo "$state" | jq -r 'has("model_arms")' 2>/dev/null || echo "false")
156
+ if [[ "$has_arms" != "true" ]]; then
157
+ # Auto-init if missing
158
+ bandit_init >/dev/null 2>&1
159
+ state="$(_bandit_read_state)"
160
+ fi
161
+
162
+ local IFS_OLD="$IFS"
163
+ IFS=','
164
+ local models_arr=($models_csv)
165
+ IFS="$IFS_OLD"
166
+
167
+ local best_model="" best_sample="-1"
168
+ local model key alpha beta sample
169
+
170
+ for model in "${models_arr[@]}"; do
171
+ key="$(_arm_key "$stage" "$model")"
172
+ alpha=$(echo "$state" | jq -r --arg k "$key" '.model_arms[$k].alpha // 1' 2>/dev/null)
173
+ beta=$(echo "$state" | jq -r --arg k "$key" '.model_arms[$k].beta // 1' 2>/dev/null)
174
+ sample=$(_beta_sample "$alpha" "$beta")
175
+
176
+ # Compare floats in awk
177
+ local is_better
178
+ is_better=$(awk -v s="$sample" -v b="$best_sample" 'BEGIN { print (s > b) ? "1" : "0" }')
179
+ if [[ "$is_better" == "1" ]]; then
180
+ best_sample="$sample"
181
+ best_model="$model"
182
+ fi
183
+ done
184
+
185
+ if [[ -z "$best_model" ]]; then
186
+ # Fallback to first model
187
+ best_model="${models_arr[0]}"
188
+ fi
189
+
190
+ echo "$best_model"
191
+ }
192
+
193
+ # Select template for a given issue type via Thompson sampling.
194
+ # Usage: bandit_select_template <issue_type> [templates_csv]
195
+ # Output: template_name (to stdout)
196
+ bandit_select_template() {
197
+ local issue_type="${1:-bug}"
198
+ local templates_csv="${2:-$BANDIT_TEMPLATES}"
199
+
200
+ local state
201
+ state="$(_bandit_read_state)"
202
+
203
+ local has_arms
204
+ has_arms=$(echo "$state" | jq -r 'has("template_arms")' 2>/dev/null || echo "false")
205
+ if [[ "$has_arms" != "true" ]]; then
206
+ bandit_init >/dev/null 2>&1
207
+ state="$(_bandit_read_state)"
208
+ fi
209
+
210
+ local IFS_OLD="$IFS"
211
+ IFS=','
212
+ local templates_arr=($templates_csv)
213
+ IFS="$IFS_OLD"
214
+
215
+ local best_template="" best_sample="-1"
216
+ local tmpl key alpha beta sample
217
+
218
+ for tmpl in "${templates_arr[@]}"; do
219
+ key="$(_arm_key "$issue_type" "$tmpl")"
220
+ alpha=$(echo "$state" | jq -r --arg k "$key" '.template_arms[$k].alpha // 1' 2>/dev/null)
221
+ beta=$(echo "$state" | jq -r --arg k "$key" '.template_arms[$k].beta // 1' 2>/dev/null)
222
+ sample=$(_beta_sample "$alpha" "$beta")
223
+
224
+ local is_better
225
+ is_better=$(awk -v s="$sample" -v b="$best_sample" 'BEGIN { print (s > b) ? "1" : "0" }')
226
+ if [[ "$is_better" == "1" ]]; then
227
+ best_sample="$sample"
228
+ best_template="$tmpl"
229
+ fi
230
+ done
231
+
232
+ if [[ -z "$best_template" ]]; then
233
+ best_template="${templates_arr[0]}"
234
+ fi
235
+
236
+ echo "$best_template"
237
+ }
238
+
239
+ # Update arm after pipeline completion.
240
+ # Usage: bandit_update <arm_type> <key> <outcome>
241
+ # arm_type: "model" or "template"
242
+ # key: e.g. "build:opus" or "bug:fast"
243
+ # outcome: "success" or "failure"
244
+ bandit_update() {
245
+ local arm_type="${1:-model}"
246
+ local key="${2:-}"
247
+ local outcome="${3:-failure}"
248
+
249
+ if [[ -z "$key" ]]; then
250
+ error "bandit_update: arm key required"
251
+ return 1
252
+ fi
253
+
254
+ local state
255
+ state="$(_bandit_read_state)"
256
+
257
+ local section
258
+ if [[ "$arm_type" == "model" ]]; then
259
+ section="model_arms"
260
+ else
261
+ section="template_arms"
262
+ fi
263
+
264
+ # Check arm exists
265
+ local arm_exists
266
+ arm_exists=$(echo "$state" | jq -r --arg s "$section" --arg k "$key" '.[$s] | has($k)' 2>/dev/null || echo "false")
267
+ if [[ "$arm_exists" != "true" ]]; then
268
+ # Create arm on the fly with Beta(1,1)
269
+ state=$(echo "$state" | jq --arg s "$section" --arg k "$key" \
270
+ '.[$s][$k] = {"alpha": 1, "beta": 1, "pulls": 0, "successes": 0}')
271
+ fi
272
+
273
+ # Update based on outcome
274
+ if [[ "$outcome" == "success" ]]; then
275
+ state=$(echo "$state" | jq --arg s "$section" --arg k "$key" \
276
+ '.[$s][$k].alpha += 1 | .[$s][$k].pulls += 1 | .[$s][$k].successes += 1')
277
+ else
278
+ state=$(echo "$state" | jq --arg s "$section" --arg k "$key" \
279
+ '.[$s][$k].beta += 1 | .[$s][$k].pulls += 1')
280
+ fi
281
+
282
+ # Add last_updated timestamp
283
+ state=$(echo "$state" | jq --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
284
+ '. + {"last_updated": $ts}')
285
+
286
+ _bandit_write_state "$state"
287
+ }
288
+
289
+ # Return exploration rate for a given arm set.
290
+ # Exploration rate = proportion of arms where pulls < threshold (default 10).
291
+ # As arms accumulate data, exploration naturally decreases.
292
+ # Usage: bandit_explore_rate [arm_type] [threshold]
293
+ # Output: float between 0 and 1
294
+ bandit_explore_rate() {
295
+ local arm_type="${1:-model}"
296
+ local threshold="${2:-10}"
297
+
298
+ local state
299
+ state="$(_bandit_read_state)"
300
+
301
+ local section
302
+ if [[ "$arm_type" == "model" ]]; then
303
+ section="model_arms"
304
+ else
305
+ section="template_arms"
306
+ fi
307
+
308
+ echo "$state" | jq -r --arg s "$section" --arg t "$threshold" '
309
+ .[$s] // {} | to_entries |
310
+ if length == 0 then "1.0000"
311
+ else
312
+ (map(select(.value.pulls < ($t | tonumber))) | length) as $under |
313
+ (length) as $total |
314
+ ($under / $total) | tostring | .[0:6]
315
+ end
316
+ ' 2>/dev/null || echo "1.0000"
317
+ }
318
+
319
+ # Show arm statistics with success rates.
320
+ # Usage: bandit_report [arm_type] [filter_prefix]
321
+ bandit_report() {
322
+ local arm_type="${1:-model}"
323
+ local filter_prefix="${2:-}"
324
+
325
+ local state
326
+ state="$(_bandit_read_state)"
327
+
328
+ local section
329
+ if [[ "$arm_type" == "model" ]]; then
330
+ section="model_arms"
331
+ echo "╔═══════════════════════════════════════════════════════════════╗"
332
+ echo "║ Model Arm Statistics ║"
333
+ echo "╚═══════════════════════════════════════════════════════════════╝"
334
+ else
335
+ section="template_arms"
336
+ echo "╔═══════════════════════════════════════════════════════════════╗"
337
+ echo "║ Template Arm Statistics ║"
338
+ echo "╚═══════════════════════════════════════════════════════════════╝"
339
+ fi
340
+
341
+ local jq_filter
342
+ if [[ -n "$filter_prefix" ]]; then
343
+ jq_filter='.[$s] // {} | to_entries | map(select(.key | startswith($p)))'
344
+ else
345
+ jq_filter='.[$s] // {} | to_entries'
346
+ fi
347
+
348
+ # Get sorted arms (by success rate descending)
349
+ local arms_json
350
+ arms_json=$(echo "$state" | jq -r --arg s "$section" --arg p "$filter_prefix" "
351
+ $jq_filter |
352
+ sort_by(-(.value.successes / ([.value.pulls, 1] | max))) |
353
+ .[] |
354
+ .key + \"|\" +
355
+ (.value.alpha | tostring) + \"|\" +
356
+ (.value.beta | tostring) + \"|\" +
357
+ (.value.pulls | tostring) + \"|\" +
358
+ (.value.successes | tostring)
359
+ " 2>/dev/null || true)
360
+
361
+ if [[ -z "$arms_json" ]]; then
362
+ echo " No arms found."
363
+ return 0
364
+ fi
365
+
366
+ local first=1
367
+ echo ""
368
+ printf " %-25s %8s %8s %8s %10s\n" "Arm" "α" "β" "Pulls" "Success%"
369
+ printf " %-25s %8s %8s %8s %10s\n" "-------------------------" "--------" "--------" "--------" "----------"
370
+
371
+ echo "$arms_json" | while IFS='|' read -r key alpha beta pulls successes; do
372
+ local rate
373
+ if [[ "$pulls" -gt 0 ]]; then
374
+ rate=$(awk -v s="$successes" -v p="$pulls" 'BEGIN { printf "%.1f%%", (s/p)*100 }')
375
+ else
376
+ rate="N/A"
377
+ fi
378
+ local marker=""
379
+ if [[ "$first" -eq 1 ]] && [[ "$pulls" -gt 0 ]]; then
380
+ marker=" ★"
381
+ first=0
382
+ fi
383
+ printf " %-25s %8s %8s %8s %10s%s\n" "$key" "$alpha" "$beta" "$pulls" "$rate" "$marker"
384
+ done
385
+
386
+ echo ""
387
+ local explore_rate
388
+ explore_rate=$(bandit_explore_rate "$arm_type")
389
+ echo " Exploration rate: $explore_rate"
390
+ }
391
+
392
+ # Get arm statistics as JSON for a specific arm.
393
+ # Usage: bandit_get_arm <arm_type> <key>
394
+ bandit_get_arm() {
395
+ local arm_type="${1:-model}"
396
+ local key="${2:-}"
397
+
398
+ local state
399
+ state="$(_bandit_read_state)"
400
+
401
+ local section
402
+ if [[ "$arm_type" == "model" ]]; then
403
+ section="model_arms"
404
+ else
405
+ section="template_arms"
406
+ fi
407
+
408
+ echo "$state" | jq -r --arg s "$section" --arg k "$key" '.[$s][$k] // empty' 2>/dev/null
409
+ }
410
+
411
+ # Reset a specific arm back to Beta(1,1).
412
+ # Usage: bandit_reset_arm <arm_type> <key>
413
+ bandit_reset_arm() {
414
+ local arm_type="${1:-model}"
415
+ local key="${2:-}"
416
+
417
+ local state
418
+ state="$(_bandit_read_state)"
419
+
420
+ local section
421
+ if [[ "$arm_type" == "model" ]]; then
422
+ section="model_arms"
423
+ else
424
+ section="template_arms"
425
+ fi
426
+
427
+ state=$(echo "$state" | jq --arg s "$section" --arg k "$key" \
428
+ '.[$s][$k] = {"alpha": 1, "beta": 1, "pulls": 0, "successes": 0}')
429
+
430
+ _bandit_write_state "$state"
431
+ }
@@ -1,9 +1,111 @@
1
1
  #!/usr/bin/env bash
2
2
  # ╔═══════════════════════════════════════════════════════════════════════════╗
3
- # ║ shipwright bootstrap — Cold-start initialization for optimization data
4
- # ║ Creates sensible defaults when no historical data exists (new installs)
3
+ # ║ shipwright bootstrap — Boilerplate consolidation + initialization
4
+ # ║
5
+ # ║ Part 1: Boilerplate elimination (SCRIPT_DIR, ERR trap, library loading) ║
6
+ # ║ Part 2: Cold-start initialization (optimization, memory defaults) ║
7
+ # ║ Part 3: Helper functions (show_help_header for standard boxed headers) ║
5
8
  # ╚═══════════════════════════════════════════════════════════════════════════╝
6
9
 
10
+ # ─── Module Guard (prevent double-sourcing) ─────────────────────────────
11
+ [[ -n "${_SW_BOOTSTRAP_LOADED:-}" ]] && return 0
12
+ _SW_BOOTSTRAP_LOADED=1
13
+
14
+ # ─── Part 1: Boilerplate Elimination ───────────────────────────────────
15
+ #
16
+ # SCRIPT_DIR Resolution: Caller's BASH_SOURCE[0] is expected to be in scripts/
17
+ # We derive SCRIPT_DIR from the call stack, skipping lib/ sources.
18
+ if [[ -z "${SCRIPT_DIR:-}" ]]; then
19
+ # Find the first non-lib caller (skip lib/bootstrap.sh itself)
20
+ for _src in "${BASH_SOURCE[@]}"; do
21
+ if [[ "$_src" != *"/lib/"* && "$_src" != "$BASH_SOURCE" ]]; then
22
+ SCRIPT_DIR="$(cd "$(dirname "$_src")" && pwd)"
23
+ break
24
+ fi
25
+ done
26
+ # Fallback if all sources are in lib/
27
+ if [[ -z "${SCRIPT_DIR:-}" ]]; then
28
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ SCRIPT_DIR="${SCRIPT_DIR%/lib}" # strip /lib suffix if present
30
+ fi
31
+ fi
32
+ export SCRIPT_DIR
33
+
34
+ # Set up ERR trap (if not already set)
35
+ if [[ "$(trap -p ERR)" == "" ]]; then
36
+ trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
37
+ fi
38
+
39
+ # Source lib/compat.sh (cross-platform compatibility)
40
+ # shellcheck source=./compat.sh
41
+ if [[ -f "$SCRIPT_DIR/lib/compat.sh" ]]; then
42
+ source "$SCRIPT_DIR/lib/compat.sh"
43
+ fi
44
+
45
+ # Source lib/helpers.sh (colors, output, timestamps)
46
+ # shellcheck source=./helpers.sh
47
+ if [[ -f "$SCRIPT_DIR/lib/helpers.sh" ]]; then
48
+ source "$SCRIPT_DIR/lib/helpers.sh"
49
+ fi
50
+
51
+ # Fallbacks (when helpers not loaded, e.g. test env)
52
+ [[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
53
+ [[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
54
+ [[ "$(type -t warn 2>/dev/null)" == "function" ]] || warn() { echo -e "\033[38;2;250;204;21m\033[1m⚠\033[0m $*"; }
55
+ [[ "$(type -t error 2>/dev/null)" == "function" ]] || error() { echo -e "\033[38;2;248;113;113m\033[1m✗\033[0m $*" >&2; }
56
+
57
+ # Color fallbacks (when NO_COLOR is set or lib/helpers.sh not sourced)
58
+ if [[ -z "${CYAN:-}" ]]; then
59
+ CYAN='\033[38;2;0;212;255m'
60
+ PURPLE='\033[38;2;124;58;237m'
61
+ BLUE='\033[38;2;0;102;255m'
62
+ GREEN='\033[38;2;74;222;128m'
63
+ YELLOW='\033[38;2;250;204;21m'
64
+ RED='\033[38;2;248;113;113m'
65
+ DIM='\033[2m'
66
+ BOLD='\033[1m'
67
+ RESET='\033[0m'
68
+ fi
69
+
70
+ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
71
+ now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
72
+ now_epoch() { date +%s; }
73
+ fi
74
+
75
+ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
76
+ emit_event() {
77
+ local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
78
+ # shellcheck disable=SC2155
79
+ local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
80
+ while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
81
+ echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
82
+ }
83
+ fi
84
+
85
+ # ─── Part 3: Standard Header Helper ────────────────────────────────────
86
+ #
87
+ # show_help_header — Prints a standard boxed header with script name, description, and version.
88
+ # Usage:
89
+ # show_help_header "Script Name" "Optional description line"
90
+ show_help_header() {
91
+ local name="$1"
92
+ local desc="${2:-}"
93
+
94
+ echo ""
95
+ if [[ -n "$desc" ]]; then
96
+ echo -e "${CYAN}${BOLD} ${name}${RESET} ${DIM}v${VERSION:-unknown}${RESET}"
97
+ echo -e "${DIM} $(printf '%0.s═' $(seq 1 42))${RESET}"
98
+ echo ""
99
+ echo -e " ${desc}"
100
+ else
101
+ echo -e "${CYAN}${BOLD} ${name}${RESET} ${DIM}v${VERSION:-unknown}${RESET}"
102
+ echo -e "${DIM} $(printf '%0.s═' $(seq 1 42))${RESET}"
103
+ fi
104
+ echo ""
105
+ }
106
+
107
+ # ─── Part 2: Cold-Start Initialization ────────────────────────────────
108
+ #
7
109
  # bootstrap_optimization — create default iteration model, template weights, model routing
8
110
  bootstrap_optimization() {
9
111
  local opt_dir="$HOME/.shipwright/optimization"