0xray 3.5.4 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/.mcp.json +51 -7
  2. package/.opencode/skills/api-design/SKILL.md +1 -1
  3. package/.opencode/skills/architect-tools/SKILL.md +1 -1
  4. package/.opencode/skills/architecture-patterns/SKILL.md +1 -1
  5. package/.opencode/skills/auto-format/SKILL.md +1 -1
  6. package/.opencode/skills/boot-orchestrator/SKILL.md +2 -6
  7. package/.opencode/skills/bug-triage/SKILL.md +1 -1
  8. package/.opencode/skills/code-analyzer/SKILL.md +1 -1
  9. package/.opencode/skills/code-review/SKILL.md +1 -1
  10. package/.opencode/skills/enforcer/SKILL.md +1 -1
  11. package/.opencode/skills/framework-compliance-audit/SKILL.md +1 -1
  12. package/.opencode/skills/git-workflow/SKILL.md +1 -1
  13. package/.opencode/skills/hermes-agent/SKILL.md +36 -36
  14. package/.opencode/skills/inference-improve/SKILL.md +1 -1
  15. package/.opencode/skills/lint/SKILL.md +1 -1
  16. package/.opencode/skills/log-monitor/SKILL.md +1 -1
  17. package/.opencode/skills/model-health-check/SKILL.md +1 -1
  18. package/.opencode/skills/multimodal-looker/SKILL.md +1 -1
  19. package/.opencode/skills/orchestrator/SKILL.md +38 -14
  20. package/.opencode/skills/performance-analysis/SKILL.md +1 -1
  21. package/.opencode/skills/performance-optimization/SKILL.md +1 -1
  22. package/.opencode/skills/processor-pipeline/SKILL.md +1 -1
  23. package/.opencode/skills/project-analysis/SKILL.md +1 -1
  24. package/.opencode/skills/refactoring-strategies/SKILL.md +1 -1
  25. package/.opencode/skills/registry.json +65 -0
  26. package/.opencode/skills/researcher/SKILL.md +18 -5
  27. package/.opencode/skills/security-audit/SKILL.md +1 -1
  28. package/.opencode/skills/security-scan/SKILL.md +1 -1
  29. package/.opencode/skills/session-management/SKILL.md +1 -1
  30. package/.opencode/skills/state-manager/SKILL.md +1 -1
  31. package/.opencode/skills/storyteller/SKILL.md +1 -1
  32. package/.opencode/skills/strategist/SKILL.md +12 -0
  33. package/.opencode/skills/testing-best-practices/SKILL.md +37 -0
  34. package/.opencode/skills/testing-strategy/SKILL.md +14 -3
  35. package/.opencode/skills/ui-ux-design/SKILL.md +1 -1
  36. package/.opencode/skills/xray-orchestrator/index.ts +267 -0
  37. package/.xray/codex.json +12 -3
  38. package/.xray/features.json +32 -6
  39. package/.xray/features.schema.json +87 -0
  40. package/AGENTS-consumer.md +16 -6
  41. package/AGENTS.md +21 -3
  42. package/README.md +13 -10
  43. package/SKILLS.md +1 -1
  44. package/dist/AGENTS.md +21 -3
  45. package/dist/CHANGELOG.md +98 -0
  46. package/dist/README.md +13 -10
  47. package/dist/SKILLS.md +1 -1
  48. package/dist/cli/commands/hermes-install.js +6 -1
  49. package/dist/cli/commands/openclaw-install.js +14 -0
  50. package/dist/cli/index.js +0 -0
  51. package/dist/core/features-config.d.ts +30 -2
  52. package/dist/governance/governance-service.d.ts +1 -0
  53. package/dist/governance/governance-service.js +81 -6
  54. package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
  55. package/dist/integrations/enforcement-gate.js +15 -5
  56. package/dist/integrations/grok/grok-cli.d.ts +3 -0
  57. package/dist/integrations/grok/grok-cli.js +92 -13
  58. package/dist/integrations/grok/hooks/grok-hook-utils.js +99 -9
  59. package/dist/integrations/grok/hooks/post-tool-use.js +68 -25
  60. package/dist/integrations/grok/hooks/pre-tool-use.js +17 -14
  61. package/dist/integrations/grok/hooks/session-start.js +114 -4
  62. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  63. package/dist/integrations/hermes-agent/__init__.py +11 -1
  64. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  65. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  66. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  67. package/dist/integrations/hermes-agent/bridge.mjs +92 -11
  68. package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
  69. package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
  70. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
  71. package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
  72. package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
  73. package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
  74. package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
  75. package/dist/integrations/hooks/station-memory-match.mjs +47 -0
  76. package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
  77. package/dist/integrations/openclaw/client.d.ts +5 -1
  78. package/dist/integrations/openclaw/client.js +17 -9
  79. package/dist/integrations/openclaw/config.js +0 -1
  80. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
  81. package/dist/integrations/openclaw/index.js +17 -0
  82. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  83. package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  84. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  85. package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
  86. package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
  87. package/dist/mcps/architect-tools.server.js +6 -1
  88. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
  89. package/dist/mcps/orchestrator/handlers/complexity-handler.js +107 -8
  90. package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
  91. package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
  92. package/dist/mcps/orchestrator/server.js +54 -5
  93. package/dist/memory-routing/index.d.ts +1 -1
  94. package/dist/memory-routing/index.js +1 -1
  95. package/dist/memory-routing/provider-loader.d.ts +4 -0
  96. package/dist/memory-routing/provider-loader.js +74 -4
  97. package/dist/memory-routing/provider-registry.js +3 -2
  98. package/dist/nucleus/aside-worktree.d.ts +41 -0
  99. package/dist/nucleus/aside-worktree.js +164 -0
  100. package/dist/nucleus/autonomy-kernel.d.ts +2 -2
  101. package/dist/nucleus/autonomy-kernel.js +17 -6
  102. package/dist/nucleus/confer.d.ts +57 -0
  103. package/dist/nucleus/confer.js +322 -0
  104. package/dist/nucleus/delegation-gate.d.ts +18 -8
  105. package/dist/nucleus/delegation-gate.js +234 -20
  106. package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
  107. package/dist/nucleus/lead-dev-plan-persistence.js +201 -19
  108. package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
  109. package/dist/nucleus/spawn-plan-resolution.js +42 -0
  110. package/dist/nucleus/suit-temperament.d.ts +40 -0
  111. package/dist/nucleus/suit-temperament.js +157 -0
  112. package/dist/nucleus/synthesis-consult-receipt.js +18 -2
  113. package/dist/nucleus/synthesis-context.js +1 -1
  114. package/dist/nucleus/user-aside.d.ts +70 -0
  115. package/dist/nucleus/user-aside.js +393 -0
  116. package/dist/plugin/xray-codex-injection.js +14 -3
  117. package/dist/postprocessor/PostProcessor.d.ts +1 -0
  118. package/dist/postprocessor/PostProcessor.js +11 -0
  119. package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
  120. package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
  121. package/dist/processors/implementations/session-summary-processor.js +9 -6
  122. package/opencode.json +88 -0
  123. package/package.json +25 -6
  124. package/scripts/hooks/install-hooks.cjs +21 -6
  125. package/scripts/hooks/run-hook.js +237 -88
  126. package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
  127. package/scripts/mjs/verify-confer-core.mjs +103 -0
  128. package/scripts/mjs/verify-delegation-gate-core.mjs +76 -3
  129. package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
  130. package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
  131. package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
  132. package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
  133. package/scripts/mjs/verify-temperament-live.mjs +261 -0
  134. package/scripts/mjs/verify-user-aside-core.mjs +137 -0
  135. package/scripts/node/bridge-mcp-wiring.cjs +287 -29
  136. package/scripts/node/install-bridges.cjs +198 -44
  137. package/scripts/node/load-reflection-config.mjs +2 -2
  138. package/scripts/node/postinstall.cjs +50 -27
  139. package/scripts/node/release-gate.mjs +8 -4
  140. package/scripts/node/release.mjs +2 -2
  141. package/scripts/node/validate-release-docs.mjs +20 -33
  142. package/scripts/node/version-manager.mjs +25 -10
  143. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  144. package/src/integrations/openclaw/README.md +134 -0
  145. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  146. package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  147. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  148. package/src/mcps/architect-tools.server.ts +6 -1
  149. package/src/mcps/orchestrator/handlers/complexity-handler.ts +180 -21
  150. package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
  151. package/src/mcps/orchestrator/server.ts +56 -5
  152. package/vendor/@0xray/repertoire/AGENTS.md +163 -0
  153. package/vendor/@0xray/repertoire/LICENSE +21 -0
  154. package/vendor/@0xray/repertoire/README.md +120 -0
  155. package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
  156. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
  157. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
  158. package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
  159. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
  160. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
  161. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
  162. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
  163. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
  164. package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
  165. package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
  166. package/vendor/@0xray/repertoire/dist/index.js +14 -0
  167. package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
  168. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
  169. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
  170. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
  171. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
  172. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
  173. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
  174. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
  175. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
  176. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
  177. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
  178. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
  179. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
  180. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
  181. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
  182. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
  183. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
  184. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
  185. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
  186. package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
  187. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
  188. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
  189. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
  190. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
  191. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
  192. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
  193. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
  194. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
  195. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
  196. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
  197. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
  198. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
  199. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
  200. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
  201. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
  202. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
  203. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
  204. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
  205. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
  206. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
  207. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
  208. package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
  209. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
  210. package/vendor/@0xray/repertoire/dist/paths.js +74 -0
  211. package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
  212. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
  213. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
  214. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
  215. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
  216. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
  217. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
  218. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
  219. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
  220. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
  221. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
  222. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
  223. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
  224. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
  225. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
  226. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
  227. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
  228. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
  229. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
  230. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
  231. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
  232. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
  233. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
  234. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
  235. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
  236. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
  237. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
  238. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
  239. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
  240. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
  241. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
  242. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
  243. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
  244. package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
  245. package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
  246. package/vendor/@0xray/repertoire/dist/types.js +2 -0
  247. package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
  248. package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
  249. package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
  250. package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
  251. package/vendor/@0xray/repertoire/package.json +124 -0
  252. package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
  253. package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
  254. package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
  255. package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
  256. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
  257. package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
  258. package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
  259. package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
  260. package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
  261. package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
  262. package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
  263. package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
  264. package/xray/codex.json +12 -3
  265. package/xray/features.json +32 -6
  266. package/xray/features.schema.json +87 -0
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Pipeline facet hooks — reflection, routing outcomes, reporting, inference-improvement.
4
+ * Loaded by Grok session-start / post-tool-use (rewire only, no new MCP surface).
5
+ */
6
+ import { createRequire } from 'node:module';
7
+ import { execSync, spawnSync } from 'node:child_process';
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs';
9
+ import { dirname, join, resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+
14
+ function resolvePackageRoot() {
15
+ let dir = __dirname;
16
+ for (let i = 0; i < 10; i++) {
17
+ const pkg = join(dir, 'package.json');
18
+ if (existsSync(pkg)) {
19
+ try {
20
+ const require = createRequire(join(dir, 'package.json'));
21
+ if (require('./package.json').name === '0xray') return dir;
22
+ } catch {
23
+ /* continue */
24
+ }
25
+ }
26
+ const nm = join(dir, 'node_modules', '0xray', 'package.json');
27
+ if (existsSync(nm)) return dirname(nm);
28
+ const parent = dirname(dir);
29
+ if (parent === dir) break;
30
+ dir = parent;
31
+ }
32
+ return resolve(__dirname, '../../..');
33
+ }
34
+
35
+ function loadFeatures(projectRoot) {
36
+ const path = join(projectRoot, '.xray', 'features.json');
37
+ if (!existsSync(path)) return {};
38
+ try {
39
+ return JSON.parse(readFileSync(path, 'utf8'));
40
+ } catch {
41
+ return {};
42
+ }
43
+ }
44
+
45
+ function appendActivity(projectRoot, action, level, data = {}) {
46
+ const logDir = join(projectRoot, 'logs', 'framework');
47
+ mkdirSync(logDir, { recursive: true });
48
+ const line = JSON.stringify({
49
+ timestamp: new Date().toISOString(),
50
+ component: 'pipeline-hook',
51
+ action,
52
+ level,
53
+ data,
54
+ });
55
+ appendFileSync(join(logDir, 'activity.log'), `${line}\n`);
56
+ }
57
+
58
+ function gitCommitCount(projectRoot) {
59
+ try {
60
+ const out = execSync('git rev-list --count HEAD', { cwd: projectRoot, encoding: 'utf8' }).trim();
61
+ const n = parseInt(out, 10);
62
+ return Number.isFinite(n) ? n : 0;
63
+ } catch {
64
+ return 0;
65
+ }
66
+ }
67
+
68
+ /** P1.1 — reflection stub when synthesis.reflection triggers fire */
69
+ export function maybeRunReflectionStub(projectRoot) {
70
+ const features = loadFeatures(projectRoot);
71
+ const reflection = features.synthesis?.reflection;
72
+ if (!reflection || reflection.mode === 'off') return null;
73
+
74
+ const threshold = reflection.thresholds?.[reflection.mode ?? 'minimal'];
75
+ const commitTrigger = reflection.triggers?.commit_threshold;
76
+ const commits = gitCommitCount(projectRoot);
77
+ const commitLimit = commitTrigger?.threshold ?? threshold?.commit_threshold ?? 25;
78
+
79
+ if (!commitTrigger?.enabled && !threshold?.auto_generate) return null;
80
+ if (commits > 0 && commits % commitLimit !== 0) return null;
81
+
82
+ const pkgRoot = resolvePackageRoot();
83
+ const generator = join(pkgRoot, 'scripts', 'node', 'auto-reflection-generator.mjs');
84
+ if (!existsSync(generator)) return null;
85
+
86
+ const result = spawnSync(
87
+ process.execPath,
88
+ [generator, '--trigger', 'commit-threshold', '--title', `Pipeline checkpoint (${commits} commits)`],
89
+ { cwd: projectRoot, encoding: 'utf8', timeout: 15000 },
90
+ );
91
+
92
+ appendActivity(projectRoot, 'reflection-stub', result.status === 0 ? 'success' : 'warning', {
93
+ commits,
94
+ exitCode: result.status,
95
+ stdout: (result.stdout || '').slice(0, 200),
96
+ });
97
+
98
+ return { triggered: true, commits, exitCode: result.status };
99
+ }
100
+
101
+ /** P1.2 — record routing outcome after orchestrate / Task spawn */
102
+ export function recordRoutingOutcome(projectRoot, entry) {
103
+ const logDir = join(projectRoot, 'logs', 'framework');
104
+ mkdirSync(logDir, { recursive: true });
105
+ const path = join(logDir, 'routing-outcomes.json');
106
+ let outcomes = [];
107
+ if (existsSync(path)) {
108
+ try {
109
+ outcomes = JSON.parse(readFileSync(path, 'utf8'));
110
+ if (!Array.isArray(outcomes)) outcomes = [];
111
+ } catch {
112
+ outcomes = [];
113
+ }
114
+ }
115
+ outcomes.push({
116
+ timestamp: new Date().toISOString(),
117
+ tool: entry.tool ?? 'unknown',
118
+ agent: entry.agent ?? entry.subagent ?? null,
119
+ planTodoId: entry.planTodoId ?? null,
120
+ success: entry.success ?? true,
121
+ sessionId: entry.sessionId ?? null,
122
+ });
123
+ if (outcomes.length > 500) outcomes = outcomes.slice(-500);
124
+ writeFileSync(path, `${JSON.stringify(outcomes, null, 2)}\n`);
125
+ appendActivity(projectRoot, 'routing-outcome', 'info', {
126
+ tool: entry.tool,
127
+ agent: entry.agent ?? entry.subagent,
128
+ count: outcomes.length,
129
+ });
130
+ return outcomes.length;
131
+ }
132
+
133
+ /** P1.3 — mark autonomous reporting scheduled (framework reporter wired at boot in TS path) */
134
+ export function scheduleAutonomousReportingMarker(projectRoot) {
135
+ const features = loadFeatures(projectRoot);
136
+ const reporting = features.autonomous_reporting;
137
+ if (!reporting?.enabled || !reporting?.auto_schedule) return null;
138
+
139
+ const reportsDir = join(projectRoot, 'reports');
140
+ mkdirSync(reportsDir, { recursive: true });
141
+ const marker = join(reportsDir, '.autonomous-reporting-scheduled.json');
142
+ writeFileSync(
143
+ marker,
144
+ `${JSON.stringify({
145
+ scheduledAt: new Date().toISOString(),
146
+ intervalMinutes: reporting.interval_minutes ?? 60,
147
+ }, null, 2)}\n`,
148
+ );
149
+ appendActivity(projectRoot, 'autonomous-report-scheduled', 'info', {
150
+ intervalMinutes: reporting.interval_minutes ?? 60,
151
+ });
152
+ return marker;
153
+ }
154
+
155
+ /** P1.7 — lightweight inference-improvement workflow stub */
156
+ export function runInferenceImprovementLight(projectRoot) {
157
+ const features = loadFeatures(projectRoot);
158
+ if (features.inference_governance?.enabled === false) return null;
159
+
160
+ const infDir = join(projectRoot, '.xray', 'inference');
161
+ mkdirSync(infDir, { recursive: true });
162
+ const workflowPath = join(infDir, `workflow-${Date.now()}.json`);
163
+ const payload = {
164
+ timestamp: new Date().toISOString(),
165
+ phase: 'pending',
166
+ triggered: true,
167
+ mode: 'light',
168
+ dataLocations: {
169
+ reflections: 'docs/reflections',
170
+ logs: 'logs/framework',
171
+ reports: 'reports',
172
+ },
173
+ };
174
+ writeFileSync(workflowPath, `${JSON.stringify(payload, null, 2)}\n`);
175
+ appendActivity(projectRoot, 'inference-improvement-light', 'info', { workflowPath });
176
+ return workflowPath;
177
+ }
178
+
179
+ /** P2.2 — opt-in lightweight postprocessor on write tools (features.grok_postprocessor_light) */
180
+ export function runGrokPostprocessorLight(projectRoot, entry = {}) {
181
+ const features = loadFeatures(projectRoot);
182
+ if (features.grok_postprocessor_light !== true) return null;
183
+
184
+ const infDir = join(projectRoot, '.xray', 'inference');
185
+ mkdirSync(infDir, { recursive: true });
186
+ const markerPath = join(infDir, 'postprocessor-light-latest.json');
187
+ const payload = {
188
+ timestamp: new Date().toISOString(),
189
+ phase: 'post-process-light',
190
+ tool: entry.tool ?? 'unknown',
191
+ paths: entry.paths ?? [],
192
+ mode: 'grok-post-tool-light',
193
+ };
194
+ writeFileSync(markerPath, `${JSON.stringify(payload, null, 2)}\n`);
195
+ appendActivity(projectRoot, 'postprocessor-light', 'info', {
196
+ tool: entry.tool,
197
+ pathCount: (entry.paths ?? []).length,
198
+ });
199
+ return markerPath;
200
+ }
201
+
202
+ /** P3.3 — pipeline facet snapshot for suit audit */
203
+ export function writePipelineFacetSnapshot(projectRoot, counts) {
204
+ const outPath = join(projectRoot, 'docs', 'PIPELINE-FACET-SNAPSHOT.json');
205
+ mkdirSync(dirname(outPath), { recursive: true });
206
+ writeFileSync(
207
+ outPath,
208
+ `${JSON.stringify({ generatedAt: new Date().toISOString(), ...counts }, null, 2)}\n`,
209
+ );
210
+ return outPath;
211
+ }
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Load lead-dev plan persistence from consumer or package dist.
4
+ * Used by Grok/Hermes session-start for stale plan archival.
5
+ */
6
+ import { createRequire } from 'node:module';
7
+ import { existsSync } from 'node:fs';
8
+ import { dirname, join, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ function resolvePackageRoot() {
14
+ let dir = __dirname;
15
+ for (let i = 0; i < 10; i++) {
16
+ const pkg = join(dir, 'package.json');
17
+ if (existsSync(pkg)) {
18
+ try {
19
+ const require = createRequire(join(dir, 'package.json'));
20
+ const name = require('./package.json').name;
21
+ if (name === '0xray') return dir;
22
+ } catch {
23
+ /* continue */
24
+ }
25
+ }
26
+ const nm = join(dir, 'node_modules', '0xray', 'package.json');
27
+ if (existsSync(nm)) {
28
+ return dirname(nm);
29
+ }
30
+ const parent = dirname(dir);
31
+ if (parent === dir) break;
32
+ dir = parent;
33
+ }
34
+ return resolve(__dirname, '../../..');
35
+ }
36
+
37
+ function loadPlanPersistence() {
38
+ const root = resolvePackageRoot();
39
+ const candidates = [
40
+ join(__dirname, '../../nucleus/lead-dev-plan-persistence.js'),
41
+ join(root, 'dist/nucleus/lead-dev-plan-persistence.js'),
42
+ join(process.cwd(), 'node_modules/0xray/dist/nucleus/lead-dev-plan-persistence.js'),
43
+ ];
44
+ const found = candidates.find((p) => existsSync(p));
45
+ if (!found) {
46
+ throw new Error(
47
+ 'lead-dev-plan-persistence.js missing — run npm run build in 0xray',
48
+ );
49
+ }
50
+ return createRequire(import.meta.url)(found);
51
+ }
52
+
53
+ let persistence;
54
+
55
+ function getPersistence() {
56
+ if (!persistence) {
57
+ persistence = loadPlanPersistence();
58
+ }
59
+ return persistence;
60
+ }
61
+
62
+ export function archiveStaleLeadDevPlan(projectRoot) {
63
+ return getPersistence().archiveStaleLeadDevPlan(projectRoot);
64
+ }
65
+
66
+ export function isLeadDevPlanStale(plan, projectRoot) {
67
+ return getPersistence().isLeadDevPlanStale(plan, projectRoot);
68
+ }
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Station heat — compaction / host-swap card.
3
+ * SSOT remains session-boot.json. STATION.md is the projection the model Reads.
4
+ * Grok ignores SessionStart/UserPromptSubmit stdout; disk + AGENTS.md is the contract.
5
+ */
6
+ const { execFileSync } = require("child_process");
7
+ const { existsSync, mkdirSync, readFileSync, writeFileSync } = require("fs");
8
+ const { join } = require("path");
9
+
10
+ const HOOKS_DIR = __dirname;
11
+
12
+ const INTENT_MAX = 240;
13
+
14
+ function stationMarkdownPath(root) {
15
+ return join(root, ".xray", "state", "STATION.md");
16
+ }
17
+
18
+ function sessionBootPath(root) {
19
+ return join(root, ".xray", "state", "session-boot.json");
20
+ }
21
+
22
+ function clipIntent(raw) {
23
+ if (raw == null) return null;
24
+ const text = String(raw)
25
+ .replace(/<\/?user_query>/gi, " ")
26
+ .replace(/\s+/g, " ")
27
+ .trim();
28
+ if (!text) return null;
29
+ if (text.length <= INTENT_MAX) return text;
30
+ return `${text.slice(0, INTENT_MAX - 1)}…`;
31
+ }
32
+
33
+ function readExistingBoot(root) {
34
+ const bootPath = sessionBootPath(root);
35
+ if (!existsSync(bootPath)) return {};
36
+ try {
37
+ const data = JSON.parse(readFileSync(bootPath, "utf8"));
38
+ return data && typeof data === "object" ? data : {};
39
+ } catch {
40
+ return {};
41
+ }
42
+ }
43
+
44
+ function readGitBrief(root) {
45
+ try {
46
+ const opts = { cwd: root, encoding: "utf8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"] };
47
+ const head = execFileSync("git", ["rev-parse", "--short", "HEAD"], opts).trim();
48
+ const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], opts).trim();
49
+ if (!head) return null;
50
+ return { branch: branch || "HEAD", head };
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ function readGitSubject(root) {
57
+ try {
58
+ const opts = { cwd: root, encoding: "utf8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"] };
59
+ const subject = execFileSync("git", ["log", "-1", "--format=%s"], opts).trim();
60
+ return clipIntent(subject);
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+
66
+ function readLiveTodoLine(plan) {
67
+ const phases = Array.isArray(plan.phases) ? plan.phases : [];
68
+ for (const phase of phases) {
69
+ const todos = Array.isArray(phase && phase.todos) ? phase.todos : [];
70
+ for (const todo of todos) {
71
+ const status = String((todo && todo.status) || "pending");
72
+ if (status === "completed" || status === "cancelled") continue;
73
+ const task = clipIntent((todo && (todo.task || todo.description)) || "");
74
+ if (task) return task;
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function readPlanLine(root) {
81
+ const planPath = join(root, ".xray", "state", "lead-dev-plan.json");
82
+ if (existsSync(planPath)) {
83
+ try {
84
+ const plan = JSON.parse(readFileSync(planPath, "utf8"));
85
+ if (plan && plan.active !== false) {
86
+ const live = readLiveTodoLine(plan);
87
+ if (live) return live;
88
+ }
89
+ } catch {
90
+ /* fall through to git subject — stale / empty plan is not the card */
91
+ }
92
+ }
93
+ return readGitSubject(root);
94
+ }
95
+
96
+ function resolveRepertoireProviderModule(root) {
97
+ const mr = readMemoryRoutingConfig(root);
98
+ if (mr && typeof mr.module_path === "string" && mr.module_path) {
99
+ const configured = mr.module_path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(mr.module_path)
100
+ ? mr.module_path
101
+ : join(root, mr.module_path);
102
+ if (existsSync(configured)) return configured;
103
+ }
104
+ const siblingRoot = join(root, "..", "repertoire");
105
+ const siblingProvider = join(siblingRoot, "dist", "provider", "memory-routing-provider.js");
106
+ const nmProvider = join(
107
+ root,
108
+ "node_modules",
109
+ "@0xray",
110
+ "repertoire",
111
+ "dist",
112
+ "provider",
113
+ "memory-routing-provider.js",
114
+ );
115
+ const vendorProvider = join(
116
+ root,
117
+ "vendor",
118
+ "@0xray",
119
+ "repertoire",
120
+ "dist",
121
+ "provider",
122
+ "memory-routing-provider.js",
123
+ );
124
+ if (existsSync(nmProvider)) return nmProvider;
125
+ if (existsSync(vendorProvider)) return vendorProvider;
126
+ try {
127
+ const pkg = JSON.parse(readFileSync(join(siblingRoot, "package.json"), "utf8"));
128
+ if ((pkg.name === "@0xray/repertoire" || pkg.name === "repertoire") && existsSync(siblingProvider)) {
129
+ return siblingProvider;
130
+ }
131
+ } catch {
132
+ /* sibling missing */
133
+ }
134
+ return null;
135
+ }
136
+
137
+ function countCuratedSignals(signalsPath) {
138
+ try {
139
+ const data = JSON.parse(readFileSync(signalsPath, "utf8"));
140
+ if (Array.isArray(data.signals)) return data.signals.length;
141
+ if (Array.isArray(data)) return data.length;
142
+ } catch {
143
+ /* unreadable */
144
+ }
145
+ return null;
146
+ }
147
+
148
+ function readMemoryRoutingConfig(root) {
149
+ const featuresPath = join(root, ".xray", "features.json");
150
+ if (!existsSync(featuresPath)) return {};
151
+ try {
152
+ return JSON.parse(readFileSync(featuresPath, "utf8")).memory_routing || {};
153
+ } catch {
154
+ return {};
155
+ }
156
+ }
157
+
158
+ function isExplicitMemoryRoutingOptOut(mr) {
159
+ return Boolean(mr) && mr.enabled === false && mr.provider === "repertoire";
160
+ }
161
+
162
+ function isLeftoverMemoryRoutingOff(mr) {
163
+ if (!mr || typeof mr !== "object") return true;
164
+ if (mr.enabled === true) return false;
165
+ if (isExplicitMemoryRoutingOptOut(mr)) return false;
166
+ const provider = mr.provider == null || mr.provider === "" ? "null" : mr.provider;
167
+ return provider === "null";
168
+ }
169
+
170
+ function isMemoryRoutingResolvedOn(mr, modulePath) {
171
+ if (!modulePath) return false;
172
+ if (isExplicitMemoryRoutingOptOut(mr)) return false;
173
+ if (mr.enabled === true && (mr.provider === "repertoire" || mr.provider === "custom")) return true;
174
+ return isLeftoverMemoryRoutingOff(mr);
175
+ }
176
+
177
+ function repertoireWorkingPath(root) {
178
+ return join(root, ".xray", "state", "repertoire-working.json");
179
+ }
180
+
181
+ function readRepertoireWorking(root) {
182
+ const dest = repertoireWorkingPath(root);
183
+ if (!existsSync(dest)) return null;
184
+ try {
185
+ const data = JSON.parse(readFileSync(dest, "utf8"));
186
+ return data && typeof data === "object" ? data : null;
187
+ } catch {
188
+ return null;
189
+ }
190
+ }
191
+
192
+ function stationSafeSignals(names) {
193
+ if (!Array.isArray(names)) return [];
194
+ const out = [];
195
+ for (const raw of names) {
196
+ const name = String(raw || "").trim();
197
+ if (!name) continue;
198
+ if (name.toLowerCase().startsWith("bedrock-")) continue;
199
+ out.push(name);
200
+ if (out.length >= 4) break;
201
+ }
202
+ return out;
203
+ }
204
+
205
+ function persistRepertoireWorking(root, snapshot) {
206
+ try {
207
+ mkdirSync(join(root, ".xray", "state"), { recursive: true });
208
+ const prev = readRepertoireWorking(root) || {};
209
+ const next = { ...prev, ...snapshot, updatedAt: new Date().toISOString() };
210
+ writeFileSync(repertoireWorkingPath(root), `${JSON.stringify(next, null, 2)}\n`);
211
+ return next;
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+
217
+ function matchStationSignalsSync(root, intent) {
218
+ if (!intent) return [];
219
+ const helper = join(HOOKS_DIR, "station-memory-match.mjs");
220
+ if (!existsSync(helper)) return [];
221
+ try {
222
+ const out = execFileSync(process.execPath, [helper, root, intent], {
223
+ encoding: "utf8",
224
+ timeout: 20000,
225
+ stdio: ["ignore", "pipe", "pipe"],
226
+ env: process.env,
227
+ });
228
+ const line = String(out).trim().split("\n").filter(Boolean).at(-1) || "[]";
229
+ const parsed = JSON.parse(line);
230
+ return Array.isArray(parsed) ? parsed : [];
231
+ } catch {
232
+ return [];
233
+ }
234
+ }
235
+
236
+ function ingestCompactFeedbackSync(root, sessionId, hookEvent, signals) {
237
+ if (!signals.length) return;
238
+ const helper = join(HOOKS_DIR, "station-memory-ingest.mjs");
239
+ if (!existsSync(helper)) return;
240
+ try {
241
+ execFileSync(
242
+ process.execPath,
243
+ [helper, root, sessionId || "station", hookEvent || "post_compact", JSON.stringify(signals)],
244
+ {
245
+ encoding: "utf8",
246
+ timeout: 20000,
247
+ stdio: ["ignore", "pipe", "pipe"],
248
+ env: process.env,
249
+ },
250
+ );
251
+ } catch {
252
+ /* working-state file is the session memory; registry ingest is best-effort */
253
+ }
254
+ }
255
+
256
+ function isCompactHook(extra) {
257
+ const hook = String(extra.hookEvent || extra.source || "");
258
+ return /compact/i.test(hook);
259
+ }
260
+
261
+ function formatWorkingLine(working) {
262
+ if (!working || typeof working !== "object") return null;
263
+ const matched = stationSafeSignals(working.matchedSignals);
264
+ if (matched.length) return `Working: ${matched.join(", ")}`;
265
+ const hook = typeof working.hookEvent === "string" ? working.hookEvent : null;
266
+ const head = working.git && working.git.head ? String(working.git.head) : null;
267
+ if (hook && head) return `Working: last ${hook} @ ${head}`;
268
+ if (hook) return `Working: last ${hook}`;
269
+ return "Working: station snapshot";
270
+ }
271
+
272
+ function buildRepertoireResume(root) {
273
+ const modulePath = resolveRepertoireProviderModule(root);
274
+ const mr = readMemoryRoutingConfig(root);
275
+ if (!modulePath) {
276
+ return "Repertoire: not installed (memory_routing stays off)";
277
+ }
278
+ let signalsPath = mr.config && mr.config.signalsPath;
279
+ if (signalsPath && !signalsPath.startsWith("/") && !/^[A-Za-z]:[\\/]/.test(signalsPath)) {
280
+ signalsPath = join(root, signalsPath);
281
+ }
282
+ if (!signalsPath) {
283
+ signalsPath = join(modulePath, "..", "..", "..", "data", "curated_signals.json");
284
+ }
285
+ const n = existsSync(signalsPath) ? countCuratedSignals(signalsPath) : null;
286
+ const count = n == null ? "" : ` — ${n} signals`;
287
+ if (isMemoryRoutingResolvedOn(mr, modulePath)) {
288
+ return `Repertoire: on${count}`;
289
+ }
290
+ return `Repertoire: present, memory_routing off${count}`;
291
+ }
292
+
293
+ function applyStationHeat(root, host, extra = {}, existing = {}) {
294
+ const prevHost = typeof existing.host === "string" ? existing.host : null;
295
+ const nextSwap = prevHost && host && prevHost !== host ? { from: prevHost, to: host } : null;
296
+ const keptSwap =
297
+ existing.hotSwap &&
298
+ typeof existing.hotSwap === "object" &&
299
+ existing.hotSwap.from &&
300
+ existing.hotSwap.to
301
+ ? existing.hotSwap
302
+ : null;
303
+ const hotSwap = nextSwap || keptSwap;
304
+ const intent =
305
+ clipIntent(extra.intent || extra.prompt || extra.userMessage || extra.user_prompt) ||
306
+ (typeof existing.intent === "string" ? existing.intent : null);
307
+ const git = readGitBrief(root);
308
+ const planLine = readPlanLine(root);
309
+ const repertoireResume =
310
+ typeof extra.repertoireResume === "string" ? extra.repertoireResume : buildRepertoireResume(root);
311
+ const swapBit = hotSwap ? `hot-swap ${hotSwap.from} → ${hotSwap.to}` : `host ${host}`;
312
+ const intentBit = intent ? `intent: ${intent}` : "intent: (none yet)";
313
+ const gitBit = git ? `git ${git.branch}@${git.head}` : "git: n/a";
314
+ const planBit = planLine ? `plan: ${planLine}` : "plan: (none)";
315
+ let matchedSignals = stationSafeSignals(extra.matchedSignals);
316
+ const priorWorking = readRepertoireWorking(root);
317
+ if (!matchedSignals.length && intent) {
318
+ if (
319
+ priorWorking &&
320
+ priorWorking.intent === intent &&
321
+ Array.isArray(priorWorking.matchedSignals) &&
322
+ priorWorking.matchedSignals.length
323
+ ) {
324
+ matchedSignals = stationSafeSignals(priorWorking.matchedSignals);
325
+ } else {
326
+ matchedSignals = stationSafeSignals(matchStationSignalsSync(root, intent));
327
+ }
328
+ }
329
+ if (!matchedSignals.length && priorWorking) {
330
+ matchedSignals = stationSafeSignals(priorWorking.matchedSignals);
331
+ }
332
+ const workingSnapshot = {
333
+ host,
334
+ intent,
335
+ git,
336
+ hotSwap,
337
+ hookEvent: extra.hookEvent || extra.source || null,
338
+ memoryRouting: repertoireResume.startsWith("Repertoire: on") ? "on" : "off",
339
+ repertoireResume,
340
+ };
341
+ if (matchedSignals.length) workingSnapshot.matchedSignals = matchedSignals;
342
+ const working = persistRepertoireWorking(root, workingSnapshot);
343
+ if (isCompactHook(extra) && matchedSignals.length) {
344
+ ingestCompactFeedbackSync(
345
+ root,
346
+ typeof extra.sessionId === "string" ? extra.sessionId : "station",
347
+ extra.hookEvent || extra.source || "post_compact",
348
+ matchedSignals,
349
+ );
350
+ }
351
+ const workingLine = formatWorkingLine(working);
352
+ const workingBit = workingLine ? workingLine : "working: (none)";
353
+ const stationLine = `${swapBit}. ${intentBit}. ${planBit}. ${gitBit}. ${repertoireResume}. ${workingBit}`;
354
+ return {
355
+ lastHost: prevHost,
356
+ hotSwap,
357
+ intent,
358
+ git,
359
+ planLine,
360
+ repertoireResume,
361
+ workingLine,
362
+ stationLine,
363
+ };
364
+ }
365
+
366
+ function formatStationMarkdown(fields) {
367
+ const host = fields.host || "unknown";
368
+ const profile = fields.suit_profile || "guided";
369
+ const lines = ["# Station", ""];
370
+ if (fields.hotSwap && fields.hotSwap.from && fields.hotSwap.to) {
371
+ lines.push(`Hot-swap: ${fields.hotSwap.from} → ${fields.hotSwap.to}`);
372
+ }
373
+ lines.push(`Host: ${host} (${profile})`);
374
+ lines.push(`Intent: ${fields.intent || "(none yet)"}`);
375
+ lines.push(`Plan: ${fields.planLine || "(none)"}`);
376
+ if (fields.git && fields.git.head) {
377
+ lines.push(`Git: ${fields.git.branch}@${fields.git.head}`);
378
+ } else {
379
+ lines.push("Git: n/a");
380
+ }
381
+ lines.push(fields.repertoireResume || "Repertoire: not installed (memory_routing stays off)");
382
+ if (fields.workingLine) {
383
+ lines.push(fields.workingLine);
384
+ }
385
+ lines.push("");
386
+ lines.push("Continue this card. Compaction and host change are the same cut. Do not cold-start.");
387
+ lines.push("Grok does not inject this file — Read it. OpenCode injects. Do not thicken the Grok exo.");
388
+ lines.push("");
389
+ return lines.join("\n");
390
+ }
391
+
392
+ function writeStationMarkdown(root, fields) {
393
+ try {
394
+ const dir = join(root, ".xray", "state");
395
+ mkdirSync(dir, { recursive: true });
396
+ const dest = stationMarkdownPath(root);
397
+ writeFileSync(dest, formatStationMarkdown(fields));
398
+ return dest;
399
+ } catch {
400
+ return null;
401
+ }
402
+ }
403
+
404
+ module.exports = {
405
+ stationMarkdownPath,
406
+ sessionBootPath,
407
+ clipIntent,
408
+ readExistingBoot,
409
+ readGitBrief,
410
+ readGitSubject,
411
+ readPlanLine,
412
+ buildRepertoireResume,
413
+ isExplicitMemoryRoutingOptOut,
414
+ isLeftoverMemoryRoutingOff,
415
+ persistRepertoireWorking,
416
+ readRepertoireWorking,
417
+ formatWorkingLine,
418
+ applyStationHeat,
419
+ formatStationMarkdown,
420
+ writeStationMarkdown,
421
+ };