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,267 @@
1
+ // Submarine Layout System — Compartment positioning, hull geometry, and navigation paths
2
+
3
+ import type { StageName } from "../design/tokens";
4
+ import { COMPARTMENTS, STAGE_TO_COMPARTMENT } from "../design/submarine-theme";
5
+ import { layout, timing } from "../design/submarine-theme";
6
+
7
+ // ── Types ────────────────────────────────────────────────────────────────────
8
+
9
+ export interface Point {
10
+ x: number;
11
+ y: number;
12
+ }
13
+
14
+ export interface CompartmentRect {
15
+ stage: StageName;
16
+ label: string;
17
+ icon: string;
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ centerX: number;
23
+ centerY: number;
24
+ stationX: number; // where agent stands to work
25
+ stationY: number;
26
+ }
27
+
28
+ export interface HullGeometry {
29
+ x: number;
30
+ y: number;
31
+ width: number;
32
+ height: number;
33
+ radius: number;
34
+ }
35
+
36
+ export interface PipeSegment {
37
+ from: Point;
38
+ to: Point;
39
+ }
40
+
41
+ // ── SubmarineLayout class ────────────────────────────────────────────────────
42
+
43
+ export class SubmarineLayout {
44
+ compartments: CompartmentRect[] = [];
45
+ hull: HullGeometry = { x: 0, y: 0, width: 0, height: 0, radius: 0 };
46
+ pipes: PipeSegment[] = [];
47
+ portholes: Point[] = [];
48
+ width = 0;
49
+ height = 0;
50
+
51
+ recalculate(width: number, height: number): void {
52
+ this.width = width;
53
+ this.height = height;
54
+
55
+ // Compute available space
56
+ const depthGaugeWidth = layout.depthGaugeWidth;
57
+ const gap = 12; // gap between depth gauge and hull
58
+ const manifestHeight = layout.manifestHeight;
59
+ const hullPadding = layout.hullPadding;
60
+ const compartmentGap = layout.compartmentGap;
61
+
62
+ const availableWidth = width - depthGaugeWidth - gap;
63
+ const availableHeight = height - manifestHeight;
64
+
65
+ // Hull takes up ~85% of height, centered vertically
66
+ const hullHeightRatio = 0.85;
67
+ const hullHeight = Math.floor(availableHeight * hullHeightRatio);
68
+ const hullWidth = availableWidth - gap;
69
+
70
+ // Center hull vertically
71
+ const topMargin = Math.floor((availableHeight - hullHeight) / 2);
72
+ const hullX = depthGaugeWidth + gap;
73
+ const hullY = topMargin;
74
+
75
+ this.hull = {
76
+ x: hullX,
77
+ y: hullY,
78
+ width: hullWidth,
79
+ height: hullHeight,
80
+ radius: layout.hullRadius,
81
+ };
82
+
83
+ // Compute compartment layout: 3 rows
84
+ this.computeCompartments();
85
+ this.computePipes();
86
+ this.computePortholes();
87
+ }
88
+
89
+ private computeCompartments(): void {
90
+ this.compartments = [];
91
+
92
+ const innerX = this.hull.x + layout.hullPadding;
93
+ const innerY = this.hull.y + layout.hullPadding;
94
+ const innerWidth = this.hull.width - layout.hullPadding * 2;
95
+ const innerHeight = this.hull.height - layout.hullPadding * 2;
96
+
97
+ // Row sizes: 4, 4, 3 compartments
98
+ const rowSizes = [4, 4, 3];
99
+ const numRows = rowSizes.length;
100
+ const rowHeight =
101
+ (innerHeight - (numRows - 1) * layout.compartmentGap) / numRows;
102
+
103
+ for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {
104
+ const colCount = rowSizes[rowIdx];
105
+ const colWidth =
106
+ (innerWidth - (colCount - 1) * layout.compartmentGap) / colCount;
107
+
108
+ const rowY = innerY + rowIdx * (rowHeight + layout.compartmentGap);
109
+
110
+ for (let colIdx = 0; colIdx < colCount; colIdx++) {
111
+ const compartment = COMPARTMENTS.find(
112
+ (c) => c.row === rowIdx && c.col === colIdx,
113
+ );
114
+
115
+ if (!compartment) continue;
116
+
117
+ const x = innerX + colIdx * (colWidth + layout.compartmentGap);
118
+
119
+ const rect: CompartmentRect = {
120
+ stage: compartment.stage,
121
+ label: compartment.label,
122
+ icon: compartment.icon,
123
+ x,
124
+ y: rowY,
125
+ width: colWidth,
126
+ height: rowHeight,
127
+ centerX: x + colWidth / 2,
128
+ centerY: rowY + rowHeight / 2,
129
+ stationX: x + colWidth / 2,
130
+ stationY: rowY + rowHeight / 2,
131
+ };
132
+
133
+ this.compartments.push(rect);
134
+ }
135
+ }
136
+ }
137
+
138
+ private computePipes(): void {
139
+ this.pipes = [];
140
+
141
+ // Connect adjacent compartments in the same row horizontally
142
+ const compartmentsByRow: { [key: number]: CompartmentRect[] } = {};
143
+ for (const comp of this.compartments) {
144
+ const compartment = COMPARTMENTS.find((c) => c.stage === comp.stage);
145
+ if (compartment) {
146
+ if (!compartmentsByRow[compartment.row]) {
147
+ compartmentsByRow[compartment.row] = [];
148
+ }
149
+ compartmentsByRow[compartment.row].push(comp);
150
+ }
151
+ }
152
+
153
+ // Connect horizontally within rows
154
+ for (const row of Object.values(compartmentsByRow)) {
155
+ row.sort((a, b) => a.x - b.x);
156
+ for (let i = 0; i < row.length - 1; i++) {
157
+ this.pipes.push({
158
+ from: { x: row[i].x + row[i].width, y: row[i].centerY },
159
+ to: { x: row[i + 1].x, y: row[i + 1].centerY },
160
+ });
161
+ }
162
+ }
163
+
164
+ // Connect rows vertically at the end of each row
165
+ for (let rowIdx = 0; rowIdx < 2; rowIdx++) {
166
+ const currentRow = compartmentsByRow[rowIdx];
167
+ const nextRow = compartmentsByRow[rowIdx + 1];
168
+ if (currentRow && nextRow) {
169
+ const lastInCurrent = currentRow[currentRow.length - 1];
170
+ const firstInNext = nextRow[0];
171
+ this.pipes.push({
172
+ from: {
173
+ x: lastInCurrent.centerX,
174
+ y: lastInCurrent.y + lastInCurrent.height,
175
+ },
176
+ to: { x: firstInNext.centerX, y: firstInNext.y },
177
+ });
178
+ }
179
+ }
180
+ }
181
+
182
+ private computePortholes(): void {
183
+ this.portholes = [];
184
+
185
+ const portholesPerEdge = 4;
186
+ const topY = this.hull.y + layout.portholeRadius;
187
+ const bottomY = this.hull.y + this.hull.height - layout.portholeRadius;
188
+
189
+ // Top portholes
190
+ for (let i = 0; i < portholesPerEdge; i++) {
191
+ const x =
192
+ this.hull.x +
193
+ layout.portholeRadius +
194
+ (i * (this.hull.width - 2 * layout.portholeRadius)) /
195
+ (portholesPerEdge - 1);
196
+ this.portholes.push({ x, y: topY });
197
+ }
198
+
199
+ // Bottom portholes
200
+ for (let i = 0; i < portholesPerEdge; i++) {
201
+ const x =
202
+ this.hull.x +
203
+ layout.portholeRadius +
204
+ (i * (this.hull.width - 2 * layout.portholeRadius)) /
205
+ (portholesPerEdge - 1);
206
+ this.portholes.push({ x, y: bottomY });
207
+ }
208
+ }
209
+
210
+ getCompartment(stage: StageName): CompartmentRect | undefined {
211
+ return this.compartments.find((c) => c.stage === stage);
212
+ }
213
+
214
+ getPathBetween(from: StageName, to: StageName): Point[] {
215
+ const fromComp = this.getCompartment(from);
216
+ const toComp = this.getCompartment(to);
217
+
218
+ if (!fromComp || !toComp) return [];
219
+ if (from === to) return [{ x: fromComp.stationX, y: fromComp.stationY }];
220
+
221
+ const fromCompartmentDef = STAGE_TO_COMPARTMENT[from];
222
+ const toCompartmentDef = STAGE_TO_COMPARTMENT[to];
223
+
224
+ const fromRow = fromCompartmentDef.row;
225
+ const toRow = toCompartmentDef.row;
226
+
227
+ const waypoints: Point[] = [{ x: fromComp.stationX, y: fromComp.stationY }];
228
+
229
+ if (fromRow === toRow) {
230
+ // Same row: straight horizontal path
231
+ waypoints.push({ x: toComp.stationX, y: toComp.stationY });
232
+ } else {
233
+ // Different rows: exit compartment, move to row connector, vertical, then horizontal to target
234
+
235
+ // Move to right edge of current compartment
236
+ waypoints.push({
237
+ x: fromComp.x + fromComp.width + layout.compartmentGap / 2,
238
+ y: fromComp.centerY,
239
+ });
240
+
241
+ // Move vertically to target row
242
+ waypoints.push({
243
+ x: fromComp.x + fromComp.width + layout.compartmentGap / 2,
244
+ y: toComp.centerY,
245
+ });
246
+
247
+ // Move horizontally to target
248
+ waypoints.push({ x: toComp.stationX, y: toComp.stationY });
249
+ }
250
+
251
+ return waypoints;
252
+ }
253
+
254
+ hitTestCompartment(x: number, y: number): CompartmentRect | undefined {
255
+ for (const comp of this.compartments) {
256
+ if (
257
+ x >= comp.x &&
258
+ x <= comp.x + comp.width &&
259
+ y >= comp.y &&
260
+ y <= comp.y + comp.height
261
+ ) {
262
+ return comp;
263
+ }
264
+ }
265
+ return undefined;
266
+ }
267
+ }
@@ -35,10 +35,10 @@ function setupSoundToggle(): void {
35
35
  btn.innerHTML = soundEnabled ? "\u{1F50A} Sound" : "\u{1F507} Mute";
36
36
  });
37
37
 
38
- // Insert before user avatar if possible
39
- const userAvatar = document.getElementById("user-avatar");
40
- if (userAvatar) {
41
- headerActions.insertBefore(btn, userAvatar);
38
+ // Insert before user menu if possible
39
+ const userMenu = document.getElementById("user-menu");
40
+ if (userMenu && userMenu.parentNode === headerActions) {
41
+ headerActions.insertBefore(btn, userMenu);
42
42
  } else {
43
43
  headerActions.appendChild(btn);
44
44
  }
@@ -71,11 +71,12 @@ function setupThemeToggle(): void {
71
71
  });
72
72
 
73
73
  const soundBtn = document.getElementById("sound-toggle");
74
- if (soundBtn) {
74
+ if (soundBtn && soundBtn.parentNode === headerActions) {
75
75
  headerActions.insertBefore(btn, soundBtn);
76
76
  } else {
77
- const userAvatar = document.getElementById("user-avatar");
78
- if (userAvatar) headerActions.insertBefore(btn, userAvatar);
77
+ const userMenu = document.getElementById("user-menu");
78
+ if (userMenu && userMenu.parentNode === headerActions)
79
+ headerActions.insertBefore(btn, userMenu);
79
80
  else headerActions.appendChild(btn);
80
81
  }
81
82
  }
@@ -7,6 +7,7 @@ import type {
7
7
  MachineInfo,
8
8
  JoinToken,
9
9
  CostBreakdown,
10
+ ContextEfficiency,
10
11
  DaemonConfig,
11
12
  AlertInfo,
12
13
  InsightsData,
@@ -113,6 +114,10 @@ export const fetchCostTrend = (period = 30) =>
113
114
  `/api/costs/trend?period=${period}`,
114
115
  );
115
116
 
117
+ // Context efficiency
118
+ export const fetchContextEfficiency = (period = 7) =>
119
+ request<ContextEfficiency>(`/api/context-efficiency?period=${period}`);
120
+
116
121
  // Daemon
117
122
  export const fetchDaemonConfig = () =>
118
123
  request<DaemonConfig>("/api/daemon/config");
@@ -19,6 +19,7 @@ const VALID_TABS: TabId[] = [
19
19
  "fleet-map",
20
20
  "pipeline-theater",
21
21
  "agent-cockpit",
22
+ "shipyard",
22
23
  ];
23
24
 
24
25
  let teamRefreshTimer: ReturnType<typeof setInterval> | null = null;
@@ -0,0 +1,253 @@
1
+ // Submarine Theme — Nautical design constants for the Shipyard tab
2
+ // Maps pipeline stages to submarine compartments, crew roles, and visual styling.
3
+
4
+ import type { StageName } from "./tokens";
5
+
6
+ // ── Compartment definitions ──────────────────────────────────────────────────
7
+
8
+ export interface Compartment {
9
+ stage: StageName;
10
+ label: string;
11
+ icon: string;
12
+ row: number; // 0=top, 1=mid, 2=bottom
13
+ col: number; // position within row
14
+ }
15
+
16
+ export const COMPARTMENTS: Compartment[] = [
17
+ // Row 0 — upper deck
18
+ { stage: "intake", label: "Airlock", icon: "\u{1F6AA}", row: 0, col: 0 },
19
+ { stage: "plan", label: "Bridge", icon: "\u{1F9ED}", row: 0, col: 1 },
20
+ { stage: "design", label: "Nav Room", icon: "\u{1F4D0}", row: 0, col: 2 },
21
+ {
22
+ stage: "build",
23
+ label: "Engine Room",
24
+ icon: "\u2699\uFE0F",
25
+ row: 0,
26
+ col: 3,
27
+ },
28
+ // Row 1 — mid deck
29
+ { stage: "test", label: "Testing Bay", icon: "\u{1F52C}", row: 1, col: 0 },
30
+ { stage: "review", label: "War Room", icon: "\u{1F4CB}", row: 1, col: 1 },
31
+ {
32
+ stage: "compound_quality",
33
+ label: "Sonar Room",
34
+ icon: "\u{1F4E1}",
35
+ row: 1,
36
+ col: 2,
37
+ },
38
+ { stage: "pr", label: "Comms", icon: "\u{1F4FB}", row: 1, col: 3 },
39
+ // Row 2 — lower deck
40
+ { stage: "merge", label: "Reactor", icon: "\u269B\uFE0F", row: 2, col: 0 },
41
+ { stage: "deploy", label: "Ballast", icon: "\u{1F680}", row: 2, col: 1 },
42
+ { stage: "monitor", label: "Periscope", icon: "\u{1F52D}", row: 2, col: 2 },
43
+ ];
44
+
45
+ export const STAGE_TO_COMPARTMENT: Record<StageName, Compartment> =
46
+ Object.fromEntries(COMPARTMENTS.map((c) => [c.stage, c])) as Record<
47
+ StageName,
48
+ Compartment
49
+ >;
50
+
51
+ // ── Crew roles ───────────────────────────────────────────────────────────────
52
+
53
+ export type CrewRole =
54
+ | "captain"
55
+ | "engineer"
56
+ | "navigator"
57
+ | "operator"
58
+ | "medic"
59
+ | "sonarTech";
60
+
61
+ export interface CrewPalette {
62
+ role: CrewRole;
63
+ /** Primary uniform hue (0-360) */
64
+ hue: number;
65
+ /** Saturation % */
66
+ sat: number;
67
+ /** Lightness for body */
68
+ bodyL: number;
69
+ /** Lightness for highlight/trim */
70
+ trimL: number;
71
+ /** Accent color hex (hat, badge, accessory) */
72
+ accent: string;
73
+ /** Skin tone base (HSL lightness) */
74
+ skinL: number;
75
+ }
76
+
77
+ export const CREW_PALETTES: Record<CrewRole, CrewPalette> = {
78
+ captain: {
79
+ role: "captain",
80
+ hue: 220,
81
+ sat: 70,
82
+ bodyL: 25,
83
+ trimL: 55,
84
+ accent: "#ffd700",
85
+ skinL: 72,
86
+ },
87
+ engineer: {
88
+ role: "engineer",
89
+ hue: 25,
90
+ sat: 85,
91
+ bodyL: 45,
92
+ trimL: 60,
93
+ accent: "#ff8c00",
94
+ skinL: 72,
95
+ },
96
+ navigator: {
97
+ role: "navigator",
98
+ hue: 140,
99
+ sat: 55,
100
+ bodyL: 30,
101
+ trimL: 55,
102
+ accent: "#2ecc71",
103
+ skinL: 72,
104
+ },
105
+ operator: {
106
+ role: "operator",
107
+ hue: 50,
108
+ sat: 80,
109
+ bodyL: 50,
110
+ trimL: 65,
111
+ accent: "#f1c40f",
112
+ skinL: 72,
113
+ },
114
+ medic: {
115
+ role: "medic",
116
+ hue: 0,
117
+ sat: 0,
118
+ bodyL: 90,
119
+ trimL: 95,
120
+ accent: "#e74c3c",
121
+ skinL: 72,
122
+ },
123
+ sonarTech: {
124
+ role: "sonarTech",
125
+ hue: 185,
126
+ sat: 70,
127
+ bodyL: 35,
128
+ trimL: 55,
129
+ accent: "#00d4ff",
130
+ skinL: 72,
131
+ },
132
+ };
133
+
134
+ /** Map stages to default crew roles */
135
+ export const STAGE_CREW_ROLE: Record<StageName, CrewRole> = {
136
+ intake: "sonarTech",
137
+ plan: "captain",
138
+ design: "navigator",
139
+ build: "engineer",
140
+ test: "engineer",
141
+ review: "medic",
142
+ compound_quality: "medic",
143
+ pr: "sonarTech",
144
+ merge: "operator",
145
+ deploy: "operator",
146
+ monitor: "navigator",
147
+ };
148
+
149
+ /** Pick a crew role for a pipeline based on its current stage */
150
+ export function crewRoleForStage(stage: StageName): CrewRole {
151
+ return STAGE_CREW_ROLE[stage];
152
+ }
153
+
154
+ // ── Nautical color palette ───────────────────────────────────────────────────
155
+
156
+ export const nautical = {
157
+ // Hull
158
+ hullOuter: "#1a2a3a",
159
+ hullInner: "#0f1d2d",
160
+ hullStroke: "#2a4a6a",
161
+ hullHighlight: "rgba(0, 212, 255, 0.08)",
162
+
163
+ // Ocean / background
164
+ oceanDeep: "#040810",
165
+ oceanMid: "#071018",
166
+ oceanLight: "#0a1828",
167
+
168
+ // Compartment
169
+ compartmentBg: "#0c1a2e",
170
+ compartmentBorder: "#1a3050",
171
+ compartmentActive: "rgba(0, 212, 255, 0.12)",
172
+
173
+ // Pipes
174
+ pipeColor: "#1a3050",
175
+ pipeFlow: "#00d4ff",
176
+ pipeFlowDim: "rgba(0, 212, 255, 0.3)",
177
+
178
+ // Porthole
179
+ portholeRing: "#2a4a6a",
180
+ portholeGlass: "rgba(0, 212, 255, 0.06)",
181
+ portholeGlow: "rgba(0, 212, 255, 0.15)",
182
+
183
+ // Effects
184
+ bubbleColor: "rgba(120, 200, 255, 0.4)",
185
+ sonarColor: "#00d4ff",
186
+ sparkleColor: "#ffffff",
187
+
188
+ // Depth gauge
189
+ depthGaugeBg: "#0a1628",
190
+ depthGaugeFill: "#00d4ff",
191
+ depthGaugeBorder: "#1a3050",
192
+
193
+ // Text
194
+ labelColor: "#8899b8",
195
+ labelActive: "#e8ecf4",
196
+ } as const;
197
+
198
+ // ── Layout constants ─────────────────────────────────────────────────────────
199
+
200
+ export const layout = {
201
+ /** Rows in the submarine */
202
+ rows: 3,
203
+ /** Max columns per row */
204
+ maxCols: 4,
205
+ /** Padding inside hull */
206
+ hullPadding: 24,
207
+ /** Gap between compartments */
208
+ compartmentGap: 12,
209
+ /** Compartment corner radius */
210
+ compartmentRadius: 6,
211
+ /** Hull corner radius */
212
+ hullRadius: 32,
213
+ /** Depth gauge width */
214
+ depthGaugeWidth: 24,
215
+ /** Crew manifest bar height */
216
+ manifestHeight: 36,
217
+ /** Min compartment size */
218
+ minCompartmentW: 100,
219
+ minCompartmentH: 70,
220
+ /** Pipe width */
221
+ pipeWidth: 2,
222
+ /** Porthole radius */
223
+ portholeRadius: 8,
224
+ } as const;
225
+
226
+ // ── Animation timing ─────────────────────────────────────────────────────────
227
+
228
+ export const timing = {
229
+ /** Agent walk speed in pixels/second */
230
+ walkSpeed: 48,
231
+ /** Idle bob amplitude in pixels */
232
+ idleBobAmplitude: 1,
233
+ /** Idle bob period in seconds */
234
+ idleBobPeriod: 2,
235
+ /** Idle wander interval range [min, max] seconds */
236
+ idleWanderRange: [2, 4] as [number, number],
237
+ /** Working frame interval in seconds */
238
+ workFrameInterval: 0.3,
239
+ /** Alert flash interval in seconds */
240
+ alertFlashInterval: 0.4,
241
+ /** Spawn cascade duration in seconds */
242
+ spawnDuration: 0.5,
243
+ /** Despawn dissolve duration in seconds */
244
+ despawnDuration: 0.5,
245
+ /** Sonar ping ring expand duration */
246
+ sonarPingDuration: 1.5,
247
+ /** Sonar ping ring count */
248
+ sonarPingRings: 3,
249
+ /** Pipe flow dash speed (px/s) */
250
+ pipeFlowSpeed: 30,
251
+ /** Bubble rise speed (px/s) */
252
+ bubbleRiseSpeed: 15,
253
+ } as const;
@@ -29,6 +29,7 @@ import { teamView } from "./views/team";
29
29
  import { fleetMapView } from "./views/fleet-map";
30
30
  import { pipelineTheaterView } from "./views/pipeline-theater";
31
31
  import { agentCockpitView } from "./views/agent-cockpit";
32
+ import { shipyardView } from "./views/shipyard";
32
33
 
33
34
  // Register all views
34
35
  registerView("overview", overviewView);
@@ -43,6 +44,7 @@ registerView("team", teamView);
43
44
  registerView("fleet-map", fleetMapView);
44
45
  registerView("pipeline-theater", pipelineTheaterView);
45
46
  registerView("agent-cockpit", agentCockpitView);
47
+ registerView("shipyard", shipyardView);
46
48
 
47
49
  // Setup header (user menu, daemon control, emergency brake)
48
50
  setupHeader();
@@ -255,6 +255,16 @@ export interface HeatmapData {
255
255
  heatmap: Record<string, Record<string, number>>;
256
256
  }
257
257
 
258
+ export interface ContextEfficiency {
259
+ avg_utilization: number;
260
+ avg_trim_ratio: number;
261
+ total_raw_chars: number;
262
+ total_trimmed_chars: number;
263
+ total_discarded_chars: number;
264
+ trim_events: number;
265
+ total_iterations: number;
266
+ }
267
+
258
268
  export interface DaemonConfig {
259
269
  paused?: boolean;
260
270
  config?: Record<string, unknown>;
@@ -328,7 +338,8 @@ export type TabId =
328
338
  | "team"
329
339
  | "fleet-map"
330
340
  | "pipeline-theater"
331
- | "agent-cockpit";
341
+ | "agent-cockpit"
342
+ | "shipyard";
332
343
 
333
344
  export interface View {
334
345
  init(): void;
@@ -76,8 +76,9 @@ function loadMoreActivity(): void {
76
76
  .then((result) => {
77
77
  const existing = store.get("activityEvents");
78
78
  const newEvents = result.events || [];
79
+ const merged = [...existing, ...newEvents];
79
80
  store.update({
80
- activityEvents: [...existing, ...newEvents],
81
+ activityEvents: merged.length > 500 ? merged.slice(-500) : merged,
81
82
  activityHasMore: result.hasMore || false,
82
83
  activityOffset: existing.length + newEvents.length,
83
84
  });