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,21 @@
1
+ // Shared constants and configuration
2
+ export const CORS_HEADERS = {
3
+ "Access-Control-Allow-Origin": "*",
4
+ "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
5
+ "Access-Control-Allow-Headers": "Content-Type",
6
+ };
7
+
8
+ export const WS_PUSH_INTERVAL_MS = 2000;
9
+ export const MAX_WS_CLIENTS = 50;
10
+ export const WS_CONNECTION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
11
+
12
+ export const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
13
+ export const ALLOWED_PERMISSIONS = ["admin", "write"];
14
+
15
+ // ANSI color codes
16
+ export const CYAN = "\x1b[38;2;0;212;255m";
17
+ export const GREEN = "\x1b[38;2;74;222;128m";
18
+ export const BOLD = "\x1b[1m";
19
+ export const DIM = "\x1b[2m";
20
+ export const ULINE = "\x1b[4m";
21
+ export const RESET = "\x1b[0m";
@@ -4,12 +4,6 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Fleet Command — Shipwright</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500;700&family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap"
11
- rel="stylesheet"
12
- />
13
7
  <link rel="stylesheet" href="styles.css" />
14
8
  <link
15
9
  rel="icon"
@@ -113,6 +107,7 @@
113
107
  <button class="tab-btn" data-tab="fleet-map">Map</button>
114
108
  <button class="tab-btn" data-tab="pipeline-theater">Theater</button>
115
109
  <button class="tab-btn" data-tab="agent-cockpit">Cockpit</button>
110
+ <button class="tab-btn" data-tab="shipyard">Shipyard</button>
116
111
  <div class="tab-indicator" id="tab-indicator"></div>
117
112
  </div>
118
113
  </nav>
@@ -516,6 +511,12 @@
516
511
  id="cost-trend-container"
517
512
  ></div>
518
513
 
514
+ <!-- Context efficiency -->
515
+ <div
516
+ class="metric-card metric-card-wide"
517
+ id="context-efficiency-container"
518
+ ></div>
519
+
519
520
  <!-- DORA trend -->
520
521
  <div
521
522
  class="metric-card metric-card-wide"
@@ -1026,6 +1027,7 @@
1026
1027
  <div class="tab-panel" id="panel-fleet-map"></div>
1027
1028
  <div class="tab-panel" id="panel-pipeline-theater"></div>
1028
1029
  <div class="tab-panel" id="panel-agent-cockpit"></div>
1030
+ <div class="tab-panel" id="panel-shipyard"></div>
1029
1031
 
1030
1032
  <script src="dist/main.js"></script>
1031
1033
  </body>
@@ -8,39 +8,47 @@
8
8
  }
9
9
 
10
10
  :root {
11
- --abyss: #060a14;
12
- --deep: #0a1628;
13
- --ocean: #0d1f3c;
14
- --surface: #132d56;
15
- --foam: #1a3a6a;
16
-
11
+ /* Ocean Depths — quieted */
12
+ --abyss: #050508;
13
+ --deep: #0a0d14;
14
+ --ocean: #111520;
15
+ --surface: #171c28;
16
+ --foam: #1e2536;
17
+
18
+ /* Accent — cyan primary */
17
19
  --cyan: #00d4ff;
18
- --cyan-glow: rgba(0, 212, 255, 0.15);
19
- --cyan-dim: rgba(0, 212, 255, 0.4);
20
+ --cyan-subtle: rgba(0, 212, 255, 0.08);
21
+ --cyan-muted: rgba(0, 212, 255, 0.25);
20
22
  --purple: #7c3aed;
21
- --purple-glow: rgba(124, 58, 237, 0.15);
22
23
  --blue: #0066ff;
23
24
 
25
+ /* Status */
24
26
  --green: #4ade80;
25
27
  --amber: #fbbf24;
26
28
  --rose: #f43f5e;
27
29
 
28
- --text-primary: #e8ecf4;
29
- --text-secondary: #8899b8;
30
- --text-muted: #5a6d8a;
31
-
32
- --card-bg: rgba(10, 22, 40, 0.8);
33
- --card-border: rgba(0, 212, 255, 0.08);
34
- --card-hover-border: rgba(0, 212, 255, 0.2);
35
- --card-radius: 16px;
36
-
37
- --font-display: "Instrument Serif", Georgia, serif;
38
- --font-body: "Plus Jakarta Sans", system-ui, sans-serif;
39
- --font-mono: "JetBrains Mono", "SF Mono", monospace;
40
-
30
+ /* Text */
31
+ --text-primary: #e8eaed;
32
+ --text-secondary: #8b8f9a;
33
+ --text-muted: #555a66;
34
+
35
+ /* Cards */
36
+ --card-bg: rgba(10, 13, 20, 0.8);
37
+ --card-border: rgba(255, 255, 255, 0.06);
38
+ --card-hover-border: rgba(0, 212, 255, 0.15);
39
+ --card-radius: 14px;
40
+
41
+ /* Typography system fonts */
42
+ --font-body:
43
+ -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, "Helvetica Neue",
44
+ sans-serif;
45
+ --font-mono:
46
+ "SF Mono", ui-monospace, "Cascadia Code", "Geist Mono", monospace;
47
+
48
+ /* Transitions */
41
49
  --transition-fast: 0.15s ease;
42
- --transition-base: 0.3s ease;
43
- --transition-slow: 0.5s ease;
50
+ --transition-base: 0.25s ease;
51
+ --transition-slow: 0.4s ease;
44
52
 
45
53
  /* Spacing scale */
46
54
  --space-1: 4px;
@@ -55,18 +63,17 @@
55
63
  --space-16: 64px;
56
64
 
57
65
  /* Border radius */
58
- --radius-sm: 4px;
59
- --radius-md: 8px;
60
- --radius-lg: 12px;
61
- --radius-xl: 16px;
66
+ --radius-sm: 6px;
67
+ --radius-md: 10px;
68
+ --radius-lg: 14px;
69
+ --radius-xl: 20px;
62
70
  --radius-full: 9999px;
63
71
 
64
72
  /* Shadows */
65
- --shadow-glow-cyan: 0 0 20px rgba(0, 212, 255, 0.15);
66
- --shadow-glow-purple: 0 0 20px rgba(124, 58, 237, 0.15);
67
- --shadow-glow-success: 0 0 12px rgba(74, 222, 128, 0.2);
68
- --shadow-glow-error: 0 0 12px rgba(244, 63, 94, 0.2);
69
- --shadow-elevated: 0 8px 32px rgba(0, 0, 0, 0.4);
73
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.1);
74
+ --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.1);
75
+ --shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.1);
76
+ --shadow-elevated: 0 24px 48px rgba(0, 0, 0, 0.5), 0 0 1px rgba(0, 0, 0, 0.1);
70
77
 
71
78
  /* Z-index */
72
79
  --z-base: 1;
@@ -78,46 +85,42 @@
78
85
 
79
86
  /* Easing */
80
87
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
81
- --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
88
+ --ease-spring: cubic-bezier(0.34, 1.2, 0.64, 1);
82
89
  }
83
90
 
84
91
  /* ── Light Mode ───────────────────────────────── */
85
92
  :root[data-theme="light"] {
86
- --abyss: #f5f7fa;
87
- --deep: #ebeef3;
88
- --ocean: #dde2ea;
89
- --surface: #cdd4de;
90
- --foam: #bcc5d3;
91
-
92
- --cyan: #0077b6;
93
- --cyan-glow: rgba(0, 119, 182, 0.1);
94
- --cyan-dim: rgba(0, 119, 182, 0.3);
93
+ --abyss: #f8f9fa;
94
+ --deep: #ffffff;
95
+ --ocean: #f1f3f5;
96
+ --surface: #e9ecef;
97
+ --foam: #dee2e6;
98
+
99
+ --cyan: #0091b3;
100
+ --cyan-subtle: rgba(0, 145, 179, 0.08);
101
+ --cyan-muted: rgba(0, 145, 179, 0.25);
95
102
  --purple: #6d28d9;
96
- --purple-glow: rgba(109, 40, 217, 0.1);
97
103
  --blue: #0055cc;
98
104
 
99
105
  --green: #16a34a;
100
106
  --amber: #d97706;
101
107
  --rose: #dc2626;
102
108
 
103
- --text-primary: #1a1a2e;
104
- --text-secondary: #475569;
105
- --text-muted: #94a3b8;
109
+ --text-primary: #1a1d21;
110
+ --text-secondary: #495057;
111
+ --text-muted: #868e96;
106
112
 
107
113
  --card-bg: rgba(255, 255, 255, 0.9);
108
- --card-border: rgba(0, 119, 182, 0.12);
109
- --card-hover-border: rgba(0, 119, 182, 0.25);
114
+ --card-border: rgba(0, 0, 0, 0.06);
115
+ --card-hover-border: rgba(0, 145, 179, 0.15);
110
116
 
111
- --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.08);
112
- --shadow-glow-cyan: 0 0 12px rgba(0, 119, 182, 0.1);
113
- --shadow-glow-purple: 0 0 12px rgba(109, 40, 217, 0.1);
114
- --shadow-glow-success: 0 0 8px rgba(22, 163, 74, 0.15);
115
- --shadow-glow-error: 0 0 8px rgba(220, 38, 38, 0.15);
117
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.04);
118
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
119
+ --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.04);
120
+ --shadow-elevated:
121
+ 0 24px 48px rgba(0, 0, 0, 0.12), 0 8px 16px rgba(0, 0, 0, 0.06);
116
122
 
117
- --bg-abyss: #f5f7fa;
118
- --bg-deep: #ebeef3;
119
- --bg-foam: #dde2ea;
120
- --border: rgba(0, 0, 0, 0.1);
123
+ --border: rgba(0, 0, 0, 0.08);
121
124
  }
122
125
 
123
126
  ::selection {
@@ -136,7 +139,7 @@ body {
136
139
  overflow-x: hidden;
137
140
  -webkit-font-smoothing: antialiased;
138
141
  -moz-osx-font-smoothing: grayscale;
139
- scrollbar-color: var(--cyan-dim) var(--deep);
142
+ scrollbar-color: var(--cyan-muted) var(--deep);
140
143
  }
141
144
 
142
145
  body::-webkit-scrollbar {
@@ -146,7 +149,7 @@ body::-webkit-scrollbar-track {
146
149
  background: var(--deep);
147
150
  }
148
151
  body::-webkit-scrollbar-thumb {
149
- background: var(--cyan-dim);
152
+ background: var(--cyan-muted);
150
153
  border-radius: 4px;
151
154
  }
152
155
 
@@ -184,7 +187,7 @@ body::-webkit-scrollbar-thumb {
184
187
  }
185
188
 
186
189
  .header-title {
187
- font-family: var(--font-display);
190
+ font-family: var(--font-body);
188
191
  font-size: 1.25rem;
189
192
  color: var(--text-primary);
190
193
  line-height: 1;
@@ -265,7 +268,7 @@ body::-webkit-scrollbar-thumb {
265
268
 
266
269
  .user-avatar:hover {
267
270
  border-color: var(--card-hover-border);
268
- box-shadow: 0 0 12px var(--cyan-glow);
271
+ box-shadow: 0 0 12px var(--cyan-subtle);
269
272
  }
270
273
 
271
274
  .user-avatar img {
@@ -460,7 +463,7 @@ body::-webkit-scrollbar-thumb {
460
463
  }
461
464
 
462
465
  .stat-value {
463
- font-family: var(--font-display);
466
+ font-family: var(--font-body);
464
467
  font-size: 1.75rem;
465
468
  font-weight: 400;
466
469
  line-height: 1.2;
@@ -661,7 +664,7 @@ body::-webkit-scrollbar-thumb {
661
664
  @keyframes stage-glow {
662
665
  0%,
663
666
  100% {
664
- box-shadow: 0 0 6px var(--cyan-glow);
667
+ box-shadow: 0 0 6px var(--cyan-subtle);
665
668
  }
666
669
  50% {
667
670
  box-shadow: 0 0 14px rgba(0, 212, 255, 0.35);
@@ -775,7 +778,7 @@ body::-webkit-scrollbar-thumb {
775
778
  .activity-feed {
776
779
  max-height: 400px;
777
780
  overflow-y: auto;
778
- scrollbar-color: var(--cyan-dim) var(--deep);
781
+ scrollbar-color: var(--cyan-muted) var(--deep);
779
782
  }
780
783
 
781
784
  .activity-feed::-webkit-scrollbar {
@@ -785,7 +788,7 @@ body::-webkit-scrollbar-thumb {
785
788
  background: transparent;
786
789
  }
787
790
  .activity-feed::-webkit-scrollbar-thumb {
788
- background: var(--cyan-dim);
791
+ background: var(--cyan-muted);
789
792
  border-radius: 3px;
790
793
  }
791
794
 
@@ -802,7 +805,7 @@ body::-webkit-scrollbar-thumb {
802
805
  }
803
806
 
804
807
  .activity-row:hover {
805
- border-left-color: var(--cyan-glow);
808
+ border-left-color: var(--cyan-subtle);
806
809
  background: rgba(0, 212, 255, 0.02);
807
810
  }
808
811
 
@@ -1022,7 +1025,7 @@ body::-webkit-scrollbar-thumb {
1022
1025
  .issue-filter-input:focus {
1023
1026
  outline: none;
1024
1027
  border-color: var(--cyan);
1025
- box-shadow: 0 0 12px var(--cyan-glow);
1028
+ box-shadow: 0 0 12px var(--cyan-subtle);
1026
1029
  }
1027
1030
 
1028
1031
  /* ── Segmented Control ────────────────────────── */
@@ -1267,7 +1270,7 @@ body::-webkit-scrollbar-thumb {
1267
1270
  .stage-timeline-dot.active {
1268
1271
  background: var(--cyan);
1269
1272
  border-color: var(--cyan);
1270
- box-shadow: 0 0 8px var(--cyan-glow);
1273
+ box-shadow: 0 0 8px var(--cyan-subtle);
1271
1274
  }
1272
1275
  .stage-timeline-dot.failed {
1273
1276
  background: var(--rose);
@@ -1325,7 +1328,7 @@ body::-webkit-scrollbar-thumb {
1325
1328
  background: transparent;
1326
1329
  }
1327
1330
  .detail-plan-content::-webkit-scrollbar-thumb {
1328
- background: var(--cyan-dim);
1331
+ background: var(--cyan-muted);
1329
1332
  border-radius: 3px;
1330
1333
  }
1331
1334
 
@@ -1359,7 +1362,7 @@ body::-webkit-scrollbar-thumb {
1359
1362
  .activity-timeline {
1360
1363
  max-height: calc(100vh - 260px);
1361
1364
  overflow-y: auto;
1362
- scrollbar-color: var(--cyan-dim) var(--deep);
1365
+ scrollbar-color: var(--cyan-muted) var(--deep);
1363
1366
  }
1364
1367
 
1365
1368
  .activity-timeline::-webkit-scrollbar {
@@ -1369,7 +1372,7 @@ body::-webkit-scrollbar-thumb {
1369
1372
  background: transparent;
1370
1373
  }
1371
1374
  .activity-timeline::-webkit-scrollbar-thumb {
1372
- background: var(--cyan-dim);
1375
+ background: var(--cyan-muted);
1373
1376
  border-radius: 3px;
1374
1377
  }
1375
1378
 
@@ -1388,7 +1391,7 @@ body::-webkit-scrollbar-thumb {
1388
1391
 
1389
1392
  .timeline-row:hover {
1390
1393
  background: rgba(0, 212, 255, 0.03);
1391
- border-left-color: var(--cyan-glow);
1394
+ border-left-color: var(--cyan-subtle);
1392
1395
  }
1393
1396
 
1394
1397
  .timeline-ts {
@@ -1502,7 +1505,7 @@ body::-webkit-scrollbar-thumb {
1502
1505
  }
1503
1506
 
1504
1507
  .metric-big-value {
1505
- font-family: var(--font-display);
1508
+ font-family: var(--font-body);
1506
1509
  font-size: 2rem;
1507
1510
  font-weight: 400;
1508
1511
  line-height: 1.2;
@@ -1552,7 +1555,7 @@ body::-webkit-scrollbar-thumb {
1552
1555
  .donut-value {
1553
1556
  position: relative;
1554
1557
  z-index: 1;
1555
- font-family: var(--font-display);
1558
+ font-family: var(--font-body);
1556
1559
  font-size: 1.5rem;
1557
1560
  font-weight: 400;
1558
1561
  color: var(--text-primary);
@@ -1596,7 +1599,7 @@ body::-webkit-scrollbar-thumb {
1596
1599
  }
1597
1600
 
1598
1601
  .svg-donut text {
1599
- font-family: var(--font-display);
1602
+ font-family: var(--font-body);
1600
1603
  fill: var(--text-primary);
1601
1604
  text-anchor: middle;
1602
1605
  dominant-baseline: central;
@@ -2304,7 +2307,7 @@ body::-webkit-scrollbar-thumb {
2304
2307
  .modal-textarea:focus {
2305
2308
  outline: none;
2306
2309
  border-color: var(--cyan);
2307
- box-shadow: 0 0 12px var(--cyan-glow);
2310
+ box-shadow: 0 0 12px var(--cyan-subtle);
2308
2311
  }
2309
2312
 
2310
2313
  .modal-footer {
@@ -2485,7 +2488,7 @@ body::-webkit-scrollbar-thumb {
2485
2488
  @keyframes stage-node-pulse {
2486
2489
  0%,
2487
2490
  100% {
2488
- filter: drop-shadow(0 0 4px var(--cyan-glow));
2491
+ filter: drop-shadow(0 0 4px var(--cyan-subtle));
2489
2492
  }
2490
2493
  50% {
2491
2494
  filter: drop-shadow(0 0 12px rgba(0, 212, 255, 0.4));
@@ -2515,7 +2518,7 @@ body::-webkit-scrollbar-thumb {
2515
2518
  width: 48px;
2516
2519
  height: 48px;
2517
2520
  border-radius: 12px;
2518
- font-family: var(--font-display);
2521
+ font-family: var(--font-body);
2519
2522
  font-size: 1.25rem;
2520
2523
  font-weight: 400;
2521
2524
  letter-spacing: 0.02em;
@@ -2676,7 +2679,7 @@ body::-webkit-scrollbar-thumb {
2676
2679
 
2677
2680
  /* ── KPI Cards ────────────────────────────────── */
2678
2681
  .kpi-value {
2679
- font-family: var(--font-display);
2682
+ font-family: var(--font-body);
2680
2683
  font-size: 2.5rem;
2681
2684
  font-weight: 400;
2682
2685
  line-height: 1;
@@ -2762,7 +2765,7 @@ body::-webkit-scrollbar-thumb {
2762
2765
  .artifact-content {
2763
2766
  max-height: 400px;
2764
2767
  overflow-y: auto;
2765
- scrollbar-color: var(--cyan-dim) var(--deep);
2768
+ scrollbar-color: var(--cyan-muted) var(--deep);
2766
2769
  }
2767
2770
 
2768
2771
  .artifact-content::-webkit-scrollbar {
@@ -2772,7 +2775,7 @@ body::-webkit-scrollbar-thumb {
2772
2775
  background: transparent;
2773
2776
  }
2774
2777
  .artifact-content::-webkit-scrollbar-thumb {
2775
- background: var(--cyan-dim);
2778
+ background: var(--cyan-muted);
2776
2779
  border-radius: 3px;
2777
2780
  }
2778
2781
 
@@ -2790,7 +2793,7 @@ body::-webkit-scrollbar-thumb {
2790
2793
  overflow-y: auto;
2791
2794
  white-space: pre-wrap;
2792
2795
  word-break: break-all;
2793
- scrollbar-color: var(--cyan-dim) var(--abyss);
2796
+ scrollbar-color: var(--cyan-muted) var(--abyss);
2794
2797
  }
2795
2798
 
2796
2799
  .log-viewer::-webkit-scrollbar {
@@ -2800,7 +2803,7 @@ body::-webkit-scrollbar-thumb {
2800
2803
  background: transparent;
2801
2804
  }
2802
2805
  .log-viewer::-webkit-scrollbar-thumb {
2803
- background: var(--cyan-dim);
2806
+ background: var(--cyan-muted);
2804
2807
  border-radius: 3px;
2805
2808
  }
2806
2809
 
@@ -3324,7 +3327,7 @@ body::-webkit-scrollbar-thumb {
3324
3327
  }
3325
3328
 
3326
3329
  .capacity-value {
3327
- font-family: var(--font-display);
3330
+ font-family: var(--font-body);
3328
3331
  font-size: 1.5rem;
3329
3332
  color: var(--text-primary);
3330
3333
  }
@@ -3634,6 +3637,82 @@ body::-webkit-scrollbar-thumb {
3634
3637
  text-align: center;
3635
3638
  }
3636
3639
 
3640
+ /* Context efficiency widget */
3641
+ .ctx-eff-grid {
3642
+ display: grid;
3643
+ grid-template-columns: repeat(4, 1fr);
3644
+ gap: 16px;
3645
+ }
3646
+
3647
+ .ctx-eff-card {
3648
+ background: var(--glass-bg);
3649
+ border: 1px solid var(--glass-border);
3650
+ border-radius: 10px;
3651
+ padding: 12px;
3652
+ display: flex;
3653
+ flex-direction: column;
3654
+ gap: 6px;
3655
+ }
3656
+
3657
+ .ctx-eff-card-label {
3658
+ font-family: var(--font-mono);
3659
+ font-size: 0.65rem;
3660
+ font-weight: 600;
3661
+ letter-spacing: 0.08em;
3662
+ color: var(--text-muted);
3663
+ text-transform: uppercase;
3664
+ }
3665
+
3666
+ .ctx-eff-gauge {
3667
+ height: 8px;
3668
+ border-radius: 4px;
3669
+ background: var(--glass-border);
3670
+ overflow: hidden;
3671
+ }
3672
+
3673
+ .ctx-eff-gauge-fill {
3674
+ height: 100%;
3675
+ border-radius: 4px;
3676
+ transition: width 0.5s ease;
3677
+ }
3678
+
3679
+ .ctx-eff-gauge-fill.ctx-eff-high {
3680
+ background: #4ade80;
3681
+ }
3682
+
3683
+ .ctx-eff-gauge-fill.ctx-eff-mid {
3684
+ background: #00d4ff;
3685
+ }
3686
+
3687
+ .ctx-eff-gauge-fill.ctx-eff-low {
3688
+ background: #f43f5e;
3689
+ }
3690
+
3691
+ .ctx-eff-gauge-fill.ctx-eff-trim {
3692
+ background: #7c3aed;
3693
+ }
3694
+
3695
+ .ctx-eff-value {
3696
+ font-family: var(--font-mono);
3697
+ font-size: 0.85rem;
3698
+ font-weight: 700;
3699
+ color: var(--text-primary);
3700
+ }
3701
+
3702
+ .ctx-eff-big {
3703
+ font-family: var(--font-mono);
3704
+ font-size: 1.4rem;
3705
+ font-weight: 700;
3706
+ color: var(--text-primary);
3707
+ line-height: 1;
3708
+ }
3709
+
3710
+ .ctx-eff-sub {
3711
+ font-family: var(--font-mono);
3712
+ font-size: 0.65rem;
3713
+ color: var(--text-muted);
3714
+ }
3715
+
3637
3716
  .dora-trend-grid {
3638
3717
  display: grid;
3639
3718
  grid-template-columns: repeat(4, 1fr);
@@ -4934,7 +5013,7 @@ select.form-input {
4934
5013
 
4935
5014
  .theater-pipeline-item.selected {
4936
5015
  background: var(--surface);
4937
- border: 1px solid var(--cyan-dim);
5016
+ border: 1px solid var(--cyan-muted);
4938
5017
  }
4939
5018
 
4940
5019
  .theater-issue {
@@ -5080,7 +5159,7 @@ select.form-input {
5080
5159
 
5081
5160
  .cockpit-agent-btn:hover {
5082
5161
  background: var(--surface);
5083
- border-color: var(--cyan-dim);
5162
+ border-color: var(--cyan-muted);
5084
5163
  }
5085
5164
 
5086
5165
  .cockpit-agent-btn.selected {
@@ -5193,7 +5272,7 @@ select.form-input {
5193
5272
  }
5194
5273
 
5195
5274
  .sound-toggle:hover {
5196
- border-color: var(--cyan-dim);
5275
+ border-color: var(--cyan-muted);
5197
5276
  color: var(--text-secondary);
5198
5277
  }
5199
5278
 
@@ -5302,7 +5381,7 @@ select.form-input {
5302
5381
  gap: 0.5rem;
5303
5382
  }
5304
5383
  .learning-card {
5305
- background: var(--bg-foam);
5384
+ background: var(--foam);
5306
5385
  border: 1px solid var(--border);
5307
5386
  border-radius: var(--radius-md);
5308
5387
  padding: 0.75rem 1rem;
@@ -5316,7 +5395,7 @@ select.form-input {
5316
5395
  }
5317
5396
  .learning-category {
5318
5397
  background: var(--cyan);
5319
- color: var(--bg-abyss);
5398
+ color: var(--abyss);
5320
5399
  padding: 0.125rem 0.5rem;
5321
5400
  border-radius: var(--radius-sm);
5322
5401
  font-weight: 600;
@@ -5358,7 +5437,7 @@ select.form-input {
5358
5437
  display: flex;
5359
5438
  align-items: center;
5360
5439
  gap: 0.5rem;
5361
- background: var(--bg-foam);
5440
+ background: var(--foam);
5362
5441
  border: 1px solid var(--border);
5363
5442
  border-radius: var(--radius-md);
5364
5443
  padding: 0.75rem 1rem;
@@ -5369,7 +5448,7 @@ select.form-input {
5369
5448
  white-space: nowrap;
5370
5449
  }
5371
5450
  .invite-url {
5372
- background: var(--bg-deep);
5451
+ background: var(--deep);
5373
5452
  padding: 0.25rem 0.5rem;
5374
5453
  border-radius: var(--radius-sm);
5375
5454
  font-size: 0.8rem;
@@ -5396,7 +5475,7 @@ select.form-input {
5396
5475
  align-items: center;
5397
5476
  gap: 0.5rem;
5398
5477
  padding: 0.5rem 0.75rem;
5399
- background: var(--bg-foam);
5478
+ background: var(--foam);
5400
5479
  border-bottom: 1px solid var(--border);
5401
5480
  font-size: 0.85rem;
5402
5481
  font-weight: 600;
@@ -5465,7 +5544,7 @@ select.form-input {
5465
5544
  .changes-diff {
5466
5545
  margin-top: 0.25rem;
5467
5546
  padding: 0.5rem;
5468
- background: var(--bg-abyss);
5547
+ background: var(--abyss);
5469
5548
  border-radius: var(--radius-sm);
5470
5549
  font-size: 0.75rem;
5471
5550
  max-height: 300px;
@@ -5482,7 +5561,7 @@ select.form-input {
5482
5561
  }
5483
5562
  .reasoning-entry,
5484
5563
  .failure-entry {
5485
- background: var(--bg-foam);
5564
+ background: var(--foam);
5486
5565
  border: 1px solid var(--border);
5487
5566
  border-radius: var(--radius-md);
5488
5567
  padding: 0.75rem 1rem;
@@ -5608,7 +5687,7 @@ select.form-input {
5608
5687
  display: flex;
5609
5688
  align-items: center;
5610
5689
  gap: 0.5rem;
5611
- background: var(--bg-foam);
5690
+ background: var(--foam);
5612
5691
  border: 1px solid var(--border);
5613
5692
  border-radius: var(--radius-md);
5614
5693
  padding: 0.5rem 0.75rem;
@@ -5749,7 +5828,7 @@ select.form-input {
5749
5828
  overflow: auto;
5750
5829
  }
5751
5830
  .admin-debug-pre {
5752
- background: var(--bg-deep);
5831
+ background: var(--deep);
5753
5832
  border: 1px solid var(--card-border);
5754
5833
  border-radius: var(--radius-md);
5755
5834
  padding: 1rem;
@@ -0,0 +1,38 @@
1
+ import type { Session } from "../types/index.js";
2
+ import { CORS_HEADERS } from "../middleware/constants.js";
3
+ import { getAuthMode, isAuthEnabled, createSession, sessionCookie, clearSessionCookie } from "../middleware/auth.js";
4
+
5
+ export function handleAuthRoutes(req: Request, pathname: string, url: URL): Response | null {
6
+ // POST /auth/pat-login — PAT-based login (returns session)
7
+ if (pathname === "/auth/pat-login" && req.method === "POST") {
8
+ // This endpoint requires body parsing which is done in server.ts
9
+ // Returning null to signal server.ts to handle this
10
+ return null;
11
+ }
12
+
13
+ // GET /auth/logout — Clear session and redirect
14
+ if (pathname === "/auth/logout") {
15
+ return new Response("Redirecting to login...", {
16
+ status: 303,
17
+ headers: {
18
+ Location: "/login",
19
+ "Set-Cookie": clearSessionCookie(),
20
+ },
21
+ });
22
+ }
23
+
24
+ // GET /auth/github — OAuth redirect (requires server.ts to handle)
25
+ if (pathname === "/auth/github") {
26
+ if (getAuthMode() !== "oauth") {
27
+ return new Response("OAuth not configured", { status: 500 });
28
+ }
29
+ return null; // server.ts handles
30
+ }
31
+
32
+ // GET /auth/callback — OAuth callback (requires server.ts to handle)
33
+ if (pathname === "/auth/callback") {
34
+ return null; // server.ts handles with query params
35
+ }
36
+
37
+ return null;
38
+ }