0xray 3.5.5 → 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 (229) 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 +27 -5
  39. package/.xray/features.schema.json +31 -0
  40. package/AGENTS-consumer.md +16 -6
  41. package/AGENTS.md +11 -3
  42. package/README.md +13 -10
  43. package/SKILLS.md +1 -1
  44. package/dist/AGENTS.md +11 -3
  45. package/dist/CHANGELOG.md +65 -11
  46. package/dist/README.md +13 -10
  47. package/dist/SKILLS.md +1 -1
  48. package/dist/cli/commands/hermes-install.js +3 -0
  49. package/dist/cli/commands/openclaw-install.js +14 -0
  50. package/dist/core/features-config.d.ts +15 -2
  51. package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
  52. package/dist/integrations/grok/grok-cli.d.ts +3 -0
  53. package/dist/integrations/grok/grok-cli.js +92 -4
  54. package/dist/integrations/grok/hooks/grok-hook-utils.js +53 -7
  55. package/dist/integrations/grok/hooks/post-tool-use.js +68 -46
  56. package/dist/integrations/grok/hooks/pre-tool-use.js +14 -31
  57. package/dist/integrations/grok/hooks/session-start.js +86 -4
  58. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  59. package/dist/integrations/hermes-agent/bridge.mjs +19 -1
  60. package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
  61. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +1 -1
  62. package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
  63. package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
  64. package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
  65. package/dist/integrations/hooks/station-memory-match.mjs +47 -0
  66. package/dist/integrations/openclaw/client.d.ts +5 -1
  67. package/dist/integrations/openclaw/client.js +17 -9
  68. package/dist/integrations/openclaw/config.js +0 -1
  69. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
  70. package/dist/integrations/openclaw/index.js +17 -0
  71. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  72. package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  73. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  74. package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
  75. package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
  76. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  77. package/dist/memory-routing/index.d.ts +1 -1
  78. package/dist/memory-routing/index.js +1 -1
  79. package/dist/memory-routing/provider-loader.d.ts +4 -0
  80. package/dist/memory-routing/provider-loader.js +44 -2
  81. package/dist/memory-routing/provider-registry.js +3 -2
  82. package/dist/nucleus/autonomy-kernel.d.ts +2 -2
  83. package/dist/nucleus/autonomy-kernel.js +17 -6
  84. package/dist/nucleus/confer.js +17 -3
  85. package/dist/nucleus/delegation-gate.d.ts +14 -8
  86. package/dist/nucleus/delegation-gate.js +151 -16
  87. package/dist/nucleus/suit-temperament.d.ts +40 -0
  88. package/dist/nucleus/suit-temperament.js +157 -0
  89. package/dist/nucleus/user-aside.js +1 -1
  90. package/dist/plugin/xray-codex-injection.js +14 -3
  91. package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
  92. package/opencode.json +88 -0
  93. package/package.json +14 -4
  94. package/scripts/hooks/install-hooks.cjs +18 -3
  95. package/scripts/hooks/run-hook.js +67 -3
  96. package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
  97. package/scripts/mjs/verify-confer-core.mjs +1 -1
  98. package/scripts/mjs/verify-delegation-gate-core.mjs +29 -2
  99. package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
  100. package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
  101. package/scripts/mjs/verify-temperament-live.mjs +261 -0
  102. package/scripts/node/bridge-mcp-wiring.cjs +277 -29
  103. package/scripts/node/install-bridges.cjs +195 -33
  104. package/scripts/node/postinstall.cjs +50 -27
  105. package/scripts/node/release-gate.mjs +1 -1
  106. package/scripts/node/release.mjs +2 -2
  107. package/scripts/node/validate-release-docs.mjs +20 -33
  108. package/scripts/node/version-manager.mjs +25 -10
  109. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  110. package/src/integrations/openclaw/README.md +134 -0
  111. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  112. package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  113. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +10 -0
  114. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  115. package/vendor/@0xray/repertoire/AGENTS.md +163 -0
  116. package/vendor/@0xray/repertoire/LICENSE +21 -0
  117. package/vendor/@0xray/repertoire/README.md +120 -0
  118. package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
  119. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
  120. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
  121. package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
  122. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
  123. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
  124. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
  125. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
  126. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
  127. package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
  128. package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
  129. package/vendor/@0xray/repertoire/dist/index.js +14 -0
  130. package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
  131. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
  132. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
  133. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
  134. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
  135. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
  136. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
  137. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
  138. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
  139. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
  140. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
  141. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
  142. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
  143. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
  144. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
  145. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
  146. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
  147. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
  148. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
  149. package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
  150. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
  151. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
  152. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
  153. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
  154. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
  155. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
  156. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
  157. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
  158. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
  159. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
  160. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
  161. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
  162. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
  163. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
  164. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
  165. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
  166. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
  167. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
  168. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
  169. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
  170. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
  171. package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
  172. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
  173. package/vendor/@0xray/repertoire/dist/paths.js +74 -0
  174. package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
  175. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
  176. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
  177. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
  178. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
  179. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
  180. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
  181. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
  182. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
  183. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
  184. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
  185. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
  186. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
  187. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
  188. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
  189. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
  190. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
  191. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
  192. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
  193. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
  194. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
  195. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
  196. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
  197. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
  198. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
  199. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
  200. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
  201. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
  202. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
  203. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
  204. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
  205. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
  206. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
  207. package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
  208. package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
  209. package/vendor/@0xray/repertoire/dist/types.js +2 -0
  210. package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
  211. package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
  212. package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
  213. package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
  214. package/vendor/@0xray/repertoire/package.json +124 -0
  215. package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
  216. package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
  217. package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
  218. package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
  219. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
  220. package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
  221. package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
  222. package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
  223. package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
  224. package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
  225. package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
  226. package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
  227. package/xray/codex.json +12 -3
  228. package/xray/features.json +27 -5
  229. package/xray/features.schema.json +31 -0
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify Hermes suit bridge for this consumer repo.
4
+ * Run: node scripts/verify-hermes-suit.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join, resolve } from 'node:path';
10
+ import {
11
+ HERMES_CONFIG_PATH,
12
+ HERMES_PLUGIN_DIR,
13
+ REPERTOIRE_MCP,
14
+ XRAY_MCP_SERVERS,
15
+ ensureConsumerRootMarker,
16
+ } from './suit-bridge-shared.mjs';
17
+
18
+ const root = resolve(import.meta.dirname, '..');
19
+ ensureConsumerRootMarker(
20
+ join(HERMES_PLUGIN_DIR, 'xray-consumer-root.txt'),
21
+ 'npx 0xray hermes install --force',
22
+ );
23
+ let failed = 0;
24
+
25
+ function pass(name, detail = '') {
26
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
27
+ }
28
+
29
+ function fail(name, detail = '') {
30
+ failed++;
31
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
32
+ }
33
+
34
+ // 1. Hermes CLI
35
+ try {
36
+ const hermesPath = execSync('which hermes', { encoding: 'utf8' }).trim();
37
+ pass('hermes cli', hermesPath);
38
+ } catch {
39
+ fail('hermes cli', 'not on PATH');
40
+ }
41
+
42
+ // 2. Plugin installed
43
+ const pluginYaml = join(HERMES_PLUGIN_DIR, 'plugin.yaml');
44
+ existsSync(pluginYaml) ? pass('xray-hermes plugin', HERMES_PLUGIN_DIR) : fail('xray-hermes plugin', 'run: npx 0xray hermes install --force');
45
+
46
+ // 3. Consumer root marker
47
+ const markerPath = join(HERMES_PLUGIN_DIR, 'xray-consumer-root.txt');
48
+ if (existsSync(markerPath)) {
49
+ const marked = readFileSync(markerPath, 'utf8').trim();
50
+ marked === root ? pass('consumer root marker', root) : fail('consumer root marker', `got ${marked}`);
51
+ } else {
52
+ fail('consumer root marker', 'missing');
53
+ }
54
+
55
+ // 4. Plugin enabled
56
+ try {
57
+ const plugins = execSync('hermes plugins list 2>&1', { encoding: 'utf8' })
58
+ .replace(/\x1b\[[0-9;]*m/g, '');
59
+ const enabledLine = plugins.split('\n').find((line) => /│\s*0xray-hermes\b/.test(line));
60
+ if (enabledLine?.includes('enabled') && !enabledLine.includes('not enabled')) {
61
+ pass('0xray-hermes plugin enabled');
62
+ } else {
63
+ fail('0xray-hermes plugin enabled', 'run: hermes plugins enable 0xray-hermes');
64
+ }
65
+ } catch (e) {
66
+ fail('0xray-hermes plugin enabled', e.message?.slice(0, 120));
67
+ }
68
+
69
+ // 5. Plugin .mcp.json
70
+ try {
71
+ const pluginMcp = JSON.parse(readFileSync(join(HERMES_PLUGIN_DIR, '.mcp.json'), 'utf8'));
72
+ const names = Object.keys(pluginMcp.mcpServers ?? {});
73
+ const xrayCount = names.filter((n) => n.startsWith('xray-')).length;
74
+ if (xrayCount === 7) pass('plugin .mcp.json xray servers', '7/7');
75
+ else fail('plugin .mcp.json xray servers', `${xrayCount}/7`);
76
+ } catch (e) {
77
+ fail('plugin .mcp.json', e.message);
78
+ }
79
+
80
+ // 6. Hermes config mcp_servers
81
+ try {
82
+ const listScript = join(root, 'scripts', 'list-hermes-mcp-servers.py');
83
+ const out = execSync(`python3 "${listScript}" "${HERMES_CONFIG_PATH}"`, { encoding: 'utf8' });
84
+ const { names } = JSON.parse(out.trim());
85
+ const xrayCount = names.filter((n) => n.startsWith('xray-')).length;
86
+ if (xrayCount === 7) pass('config.yaml mcp_servers xray', '7/7');
87
+ else fail('config.yaml mcp_servers xray', `${xrayCount}/7`);
88
+ names.includes(REPERTOIRE_MCP.name)
89
+ ? pass('config.yaml mcp_servers repertoire')
90
+ : fail('config.yaml mcp_servers repertoire', 'missing');
91
+ } catch (e) {
92
+ fail('config.yaml mcp_servers', e.message?.slice(0, 160));
93
+ }
94
+
95
+ // 7. Bridge health
96
+ try {
97
+ const out = execSync(
98
+ `printf '%s' '{"command":"health"}' | node "${join(HERMES_PLUGIN_DIR, 'bridge.mjs')}" --cwd "${root}"`,
99
+ { encoding: 'utf8', cwd: root, env: { ...process.env, XRAY_ROOT: root } },
100
+ );
101
+ const health = JSON.parse(out.trim());
102
+ if (health.framework === 'loaded') pass('bridge health', 'framework loaded');
103
+ else fail('bridge health', JSON.stringify(health).slice(0, 120));
104
+ if (health.projectRoot === root) pass('bridge projectRoot', root);
105
+ else fail('bridge projectRoot', `got ${health.projectRoot}`);
106
+ } catch (e) {
107
+ fail('bridge health', e.message?.slice(0, 160));
108
+ }
109
+
110
+ // 8. .xray contract (shared with Grok)
111
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
112
+ existsSync(join(root, '.xray', f)) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
113
+ }
114
+
115
+ // 9. MCP enforcer stdio (same surface Hermes mcp_servers uses)
116
+ try {
117
+ const init = JSON.stringify({
118
+ jsonrpc: '2.0',
119
+ id: 1,
120
+ method: 'initialize',
121
+ params: {
122
+ protocolVersion: '2024-11-05',
123
+ capabilities: {},
124
+ clientInfo: { name: 'verify-hermes-suit', version: '1.0' },
125
+ },
126
+ });
127
+ const out = execSync(
128
+ `printf '%s\\n' '${init.replace(/'/g, "'\\''")}' | timeout 12 npx 0xray mcp enforcer 2>/dev/null | head -1`,
129
+ { cwd: root, encoding: 'utf8', shell: '/bin/bash' },
130
+ );
131
+ if (out.includes('serverInfo')) pass('MCP enforcer stdio probe');
132
+ else fail('MCP enforcer stdio probe', out.slice(0, 120));
133
+ } catch (e) {
134
+ fail('MCP enforcer stdio probe', e.message?.slice(0, 160));
135
+ }
136
+
137
+ // 10. Plugin tool surface (plugin.yaml — live session loads on restart)
138
+ try {
139
+ const pluginYaml = readFileSync(join(HERMES_PLUGIN_DIR, 'plugin.yaml'), 'utf8');
140
+ const pluginTools = ['xray_validate', 'xray_codex_check', 'xray_health', 'xray_hooks'];
141
+ const found = pluginTools.filter((t) => pluginYaml.includes(t));
142
+ if (found.length === pluginTools.length) pass('plugin tools (plugin.yaml)', pluginTools.join(', '));
143
+ else fail('plugin tools (plugin.yaml)', `found ${found.length}/4`);
144
+ } catch (e) {
145
+ fail('plugin tools (plugin.yaml)', e.message?.slice(0, 120));
146
+ }
147
+
148
+ // 11. Delegation gate fixture (0xray@3.5.1+)
149
+ const hermesGateScript = join(root, 'node_modules/0xray/scripts/mjs/verify-hermes-delegation-gate.mjs');
150
+ if (existsSync(hermesGateScript)) {
151
+ try {
152
+ execSync(`node "${hermesGateScript}"`, { cwd: root, stdio: 'pipe', encoding: 'utf8' });
153
+ pass('Hermes delegation gate fixture', '4/4');
154
+ } catch (e) {
155
+ const detail = (e.stdout || e.stderr || e.message || '').split('\n').slice(-3).join(' ').slice(0, 160);
156
+ fail('Hermes delegation gate fixture', detail);
157
+ }
158
+ } else {
159
+ fail('Hermes delegation gate fixture', 'missing script — npm install 0xray@^3.5.1');
160
+ }
161
+
162
+ console.log('\n' + (failed === 0 ? '🎉 Hermes suit wearable.' : `⚠️ ${failed} Hermes check(s) failed.`));
163
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify OpenClaw suit bridge for this consumer repo (native 0xray install).
4
+ */
5
+ import { execSync } from 'node:child_process';
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { join, resolve } from 'node:path';
9
+ import { REPERTOIRE_MCP, XRAY_MCP_SERVERS, ensureConsumerRootMarker } from './suit-bridge-shared.mjs';
10
+
11
+ const root = resolve(import.meta.dirname, '..');
12
+ const OPENCLAW_STATE = join(homedir(), '.openclaw');
13
+ ensureConsumerRootMarker(
14
+ join(OPENCLAW_STATE, 'xray-consumer-root.txt'),
15
+ 'npx 0xray openclaw install --force',
16
+ );
17
+ const OPENCLAW_CONFIG = join(OPENCLAW_STATE, 'openclaw.json');
18
+ let failed = 0;
19
+
20
+ function pass(name, detail = '') {
21
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
22
+ }
23
+
24
+ function fail(name, detail = '') {
25
+ failed++;
26
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
27
+ }
28
+
29
+ // 1. OpenClaw CLI
30
+ try {
31
+ const openclawPath = execSync('which openclaw', { encoding: 'utf8' }).trim();
32
+ pass('openclaw cli', openclawPath);
33
+ } catch {
34
+ fail('openclaw cli', 'not on PATH');
35
+ }
36
+
37
+ // 2. Consumer integration config
38
+ const consumerConfig = join(root, '.xray', 'config', 'openclaw.json');
39
+ existsSync(consumerConfig)
40
+ ? pass('consumer openclaw.json', consumerConfig)
41
+ : fail('consumer openclaw.json', 'run npx 0xray openclaw install');
42
+
43
+ // 3. Consumer root marker
44
+ const markerPath = join(OPENCLAW_STATE, 'xray-consumer-root.txt');
45
+ if (existsSync(markerPath)) {
46
+ const marked = readFileSync(markerPath, 'utf8').trim();
47
+ marked === root ? pass('consumer root marker', root) : fail('consumer root marker', `got ${marked}`);
48
+ } else {
49
+ fail('consumer root marker', 'missing');
50
+ }
51
+
52
+ // 4. xray-orchestrator skill synced
53
+ const orchestratorSkill = join(OPENCLAW_STATE, 'skills', 'xray-orchestrator', 'SKILL.md');
54
+ existsSync(orchestratorSkill)
55
+ ? pass('xray-orchestrator skill', orchestratorSkill)
56
+ : fail('xray-orchestrator skill', 'missing');
57
+
58
+ // 5. openclaw mcp list
59
+ try {
60
+ const listed = execSync('openclaw mcp list 2>&1', { encoding: 'utf8', timeout: 20000 });
61
+ const xrayListed = XRAY_MCP_SERVERS.filter((s) => listed.includes(s.name));
62
+ if (xrayListed.length === 7) pass('openclaw mcp list xray', '7/7');
63
+ else fail('openclaw mcp list xray', `${xrayListed.length}/7`);
64
+ listed.includes(REPERTOIRE_MCP.name)
65
+ ? pass('openclaw mcp list repertoire')
66
+ : fail('openclaw mcp list repertoire', 'not listed');
67
+ } catch (e) {
68
+ fail('openclaw mcp list', e.message?.slice(0, 160));
69
+ }
70
+
71
+ // 6. openclaw.json mcp.servers block
72
+ try {
73
+ const config = JSON.parse(readFileSync(OPENCLAW_CONFIG, 'utf8'));
74
+ const servers = config.mcp?.servers ?? {};
75
+ const xrayCount = Object.keys(servers).filter((n) => n.startsWith('xray-')).length;
76
+ if (xrayCount === 7) pass('openclaw.json mcp.servers xray', '7/7');
77
+ else fail('openclaw.json mcp.servers xray', `${xrayCount}/7`);
78
+ if (servers[REPERTOIRE_MCP.name]) pass('openclaw.json mcp.servers repertoire');
79
+ else fail('openclaw.json mcp.servers repertoire', 'missing');
80
+ if (servers['xray-enforcer']?.env?.XRAY_ROOT === root) {
81
+ pass('openclaw mcp XRAY_ROOT', root);
82
+ } else {
83
+ fail('openclaw mcp XRAY_ROOT', `got ${servers['xray-enforcer']?.env?.XRAY_ROOT}`);
84
+ }
85
+ } catch (e) {
86
+ fail('openclaw.json mcp.servers', e.message?.slice(0, 160));
87
+ }
88
+
89
+ // 7. .xray contract
90
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
91
+ existsSync(join(root, '.xray', f)) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
92
+ }
93
+
94
+ // 8. MCP stdio probe
95
+ try {
96
+ const init = JSON.stringify({
97
+ jsonrpc: '2.0',
98
+ id: 1,
99
+ method: 'initialize',
100
+ params: {
101
+ protocolVersion: '2024-11-05',
102
+ capabilities: {},
103
+ clientInfo: { name: 'verify-openclaw-suit', version: '1.0' },
104
+ },
105
+ });
106
+ const out = execSync(
107
+ `printf '%s\\n' '${init.replace(/'/g, "'\\''")}' | timeout 12 npx 0xray mcp enforcer 2>/dev/null | head -1`,
108
+ { cwd: root, encoding: 'utf8', shell: '/bin/bash' },
109
+ );
110
+ if (out.includes('serverInfo')) pass('MCP enforcer stdio probe');
111
+ else fail('MCP enforcer stdio probe', out.slice(0, 120));
112
+ } catch (e) {
113
+ fail('MCP enforcer stdio probe', e.message?.slice(0, 160));
114
+ }
115
+
116
+ // 9. npx 0xray health
117
+ try {
118
+ execSync('npx 0xray health', { cwd: root, stdio: 'pipe', encoding: 'utf8' });
119
+ pass('npx 0xray health');
120
+ } catch {
121
+ fail('npx 0xray health');
122
+ }
123
+
124
+ console.log('\n' + (failed === 0 ? '🎉 OpenClaw suit wearable.' : `⚠️ ${failed} OpenClaw check(s) failed.`));
125
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify OpenCode suit bridge for this consumer repo.
4
+ * Run: node scripts/verify-opencode-suit.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { join, resolve } from 'node:path';
9
+ import { REPERTOIRE_MCP, XRAY_MCP_SERVERS } from './suit-bridge-shared.mjs';
10
+
11
+ const root = resolve(import.meta.dirname, '..');
12
+ let failed = 0;
13
+
14
+ function pass(name, detail = '') {
15
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
16
+ }
17
+
18
+ function fail(name, detail = '') {
19
+ failed++;
20
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
21
+ }
22
+
23
+ // 1. OpenCode CLI
24
+ try {
25
+ const opencodePath = execSync('which opencode', { encoding: 'utf8' }).trim();
26
+ pass('opencode cli', opencodePath);
27
+ } catch {
28
+ fail('opencode cli', 'not on PATH');
29
+ }
30
+
31
+ // 2. Bridge artifacts
32
+ const pluginPath = join(root, '.opencode', 'plugin', 'xray-codex-injection.js');
33
+ existsSync(pluginPath) ? pass('codex injection plugin', pluginPath) : fail('codex injection plugin', 'run: npx 0xray opencode install --force');
34
+
35
+ const skillsDir = join(root, '.opencode', 'skills', 'orchestrator', 'SKILL.md');
36
+ existsSync(skillsDir) ? pass('orchestrator skill synced') : fail('orchestrator skill synced', 'missing');
37
+
38
+ // 3. opencode.json MCP entries
39
+ try {
40
+ const config = JSON.parse(readFileSync(join(root, 'opencode.json'), 'utf8'));
41
+ const mcp = config.mcp ?? {};
42
+ const xrayEntries = XRAY_MCP_SERVERS.filter((s) => {
43
+ const entry = mcp[s.name];
44
+ return entry?.type === 'local' && entry?.enabled === true && Array.isArray(entry.command);
45
+ });
46
+ if (xrayEntries.length === 7) pass('opencode.json xray mcp', '7/7 enabled local');
47
+ else fail('opencode.json xray mcp', `${xrayEntries.length}/7`);
48
+
49
+ const rep = mcp[REPERTOIRE_MCP.name];
50
+ if (rep?.enabled === true && rep?.type === 'local') pass('opencode.json repertoire mcp');
51
+ else fail('opencode.json repertoire mcp', 'missing or disabled');
52
+ } catch (e) {
53
+ fail('opencode.json mcp', e.message);
54
+ }
55
+
56
+ // 4. Plugin loaded in resolved config
57
+ try {
58
+ const debug = execSync('opencode debug config 2>&1', { cwd: root, encoding: 'utf8', timeout: 15000 });
59
+ if (debug.includes('xray-codex-injection.js')) pass('plugin in resolved config');
60
+ else fail('plugin in resolved config', 'xray-codex-injection.js not found');
61
+ } catch (e) {
62
+ fail('opencode debug config', e.message?.slice(0, 160));
63
+ }
64
+
65
+ // 5. opencode mcp list (runtime registry)
66
+ try {
67
+ const listed = execSync('opencode mcp list 2>&1', { cwd: root, encoding: 'utf8', timeout: 20000 });
68
+ const xrayListed = XRAY_MCP_SERVERS.filter((s) => listed.includes(s.name));
69
+ if (xrayListed.length === 7) pass('opencode mcp list xray', '7/7');
70
+ else fail('opencode mcp list xray', `${xrayListed.length}/7 — ${listed.slice(0, 200)}`);
71
+ listed.includes(REPERTOIRE_MCP.name)
72
+ ? pass('opencode mcp list repertoire')
73
+ : fail('opencode mcp list repertoire', 'not listed');
74
+ } catch (e) {
75
+ fail('opencode mcp list', e.message?.slice(0, 160));
76
+ }
77
+
78
+ // 6. Skills discoverable via OpenCode CLI (output can exceed 64KB — pipe to rg)
79
+ try {
80
+ execSync('opencode debug skill 2>&1 | rg -q "orchestrator/SKILL.md"', {
81
+ cwd: root,
82
+ encoding: 'utf8',
83
+ timeout: 20000,
84
+ shell: '/bin/bash',
85
+ });
86
+ pass('opencode skills', 'orchestrator present');
87
+ } catch (e) {
88
+ fail('opencode skills', e.message?.slice(0, 160));
89
+ }
90
+
91
+ // 7. .xray contract
92
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
93
+ existsSync(join(root, '.xray', f)) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
94
+ }
95
+
96
+ // 8. MCP stdio probe
97
+ try {
98
+ const init = JSON.stringify({
99
+ jsonrpc: '2.0',
100
+ id: 1,
101
+ method: 'initialize',
102
+ params: {
103
+ protocolVersion: '2024-11-05',
104
+ capabilities: {},
105
+ clientInfo: { name: 'verify-opencode-suit', version: '1.0' },
106
+ },
107
+ });
108
+ const out = execSync(
109
+ `printf '%s\\n' '${init.replace(/'/g, "'\\''")}' | timeout 12 npx 0xray mcp skills 2>/dev/null | head -1`,
110
+ { cwd: root, encoding: 'utf8', shell: '/bin/bash' },
111
+ );
112
+ if (out.includes('serverInfo')) pass('MCP skills stdio probe');
113
+ else fail('MCP skills stdio probe', out.slice(0, 120));
114
+ } catch (e) {
115
+ fail('MCP skills stdio probe', e.message?.slice(0, 160));
116
+ }
117
+
118
+ // 9. npx 0xray health from consumer
119
+ try {
120
+ execSync('npx 0xray health', { cwd: root, stdio: 'pipe', encoding: 'utf8' });
121
+ pass('npx 0xray health');
122
+ } catch {
123
+ fail('npx 0xray health');
124
+ }
125
+
126
+ console.log('\n' + (failed === 0 ? '🎉 OpenCode suit wearable.' : `⚠️ ${failed} OpenCode check(s) failed.`));
127
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dogfood synthesis on repertoire core — provider + xray nucleus + live Grok hook.
4
+ * Run: npm run build && node scripts/verify-synthesis-dogfood.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { tmpdir } from 'node:os';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const root = join(__dirname, '..');
14
+ const xrayRoot = join(root, 'node_modules/0xray');
15
+
16
+ let failed = 0;
17
+ function pass(n, d = '') {
18
+ console.log(`✅ ${n}${d ? ` — ${d}` : ''}`);
19
+ }
20
+ function fail(n, d = '') {
21
+ failed++;
22
+ console.error(`❌ ${n}${d ? ` — ${d}` : ''}`);
23
+ }
24
+
25
+ const sessionId = 'repertoire-dogfood';
26
+
27
+ async function main() {
28
+ if (!existsSync(join(root, 'dist/provider/memory-routing-provider.js'))) {
29
+ fail('build', 'run npm run build first');
30
+ process.exit(1);
31
+ }
32
+
33
+ try {
34
+ execSync('npm run health:repertoire', { cwd: root, stdio: 'pipe', env: { ...process.env, REPERTOIRE_EXPECTED_SIGNALS: '145' } });
35
+ pass('health:repertoire', '145 signals drift gate');
36
+ } catch (e) {
37
+ fail('health:repertoire', e.stderr?.toString() || e.message);
38
+ }
39
+
40
+ const features = JSON.parse(readFileSync(join(root, '.xray/features.json'), 'utf8'));
41
+ if (!features.synthesis?.enabled) fail('synthesis.enabled in features.json');
42
+ else pass('synthesis.enabled in features.json');
43
+ if (!features.memory_routing?.enabled) fail('memory_routing.enabled');
44
+ else pass('memory_routing.enabled', features.memory_routing.provider);
45
+
46
+ const { createMemoryRoutingProvider } = await import(
47
+ join(root, 'dist/provider/memory-routing-provider.js')
48
+ );
49
+ const provider = createMemoryRoutingProvider(features.memory_routing.config ?? {});
50
+ if (typeof provider.buildSynthesisContext !== 'function') {
51
+ fail('provider.buildSynthesisContext');
52
+ } else {
53
+ const ctx = provider.buildSynthesisContext({
54
+ projectRoot: root,
55
+ dueReason: 'gate_threshold',
56
+ });
57
+ if (ctx && typeof ctx === 'object') pass('provider.buildSynthesisContext', 'non-null');
58
+ else fail('provider.buildSynthesisContext', 'null');
59
+ }
60
+
61
+ if (typeof provider.refreshMetaInference === 'function') {
62
+ try {
63
+ const { refreshed } = await provider.refreshMetaInference();
64
+ pass('provider.refreshMetaInference', String(refreshed));
65
+ } catch (e) {
66
+ fail('provider.refreshMetaInference', e.message);
67
+ }
68
+ } else {
69
+ fail('provider.refreshMetaInference missing');
70
+ }
71
+
72
+ if (typeof provider.ingestFeedback === 'function') {
73
+ try {
74
+ const result = provider.ingestFeedback({
75
+ timestamp: new Date().toISOString(),
76
+ sessionId,
77
+ taskId: 'dogfood-1',
78
+ assignedAgent: 'researcher',
79
+ memorySignals: ['synthesis-checkpoint'],
80
+ complexity: 20,
81
+ success: true,
82
+ durationMs: 1200,
83
+ });
84
+ if (result?.logPath) pass('provider.ingestFeedback', result.logPath);
85
+ else fail('provider.ingestFeedback', 'no logPath');
86
+ } catch (e) {
87
+ fail('provider.ingestFeedback', e.message);
88
+ }
89
+ }
90
+
91
+ const { recordExecutionSlice, isSynthesisCheckpointDue, loadSynthesisCheckpointState } =
92
+ await import(join(xrayRoot, 'dist/nucleus/synthesis.js'));
93
+ const { evaluatePreToolGate, loadDelegationGateFeatures } = await import(
94
+ join(xrayRoot, 'dist/integrations/hooks/delegation-gate-runtime.mjs'),
95
+ );
96
+ const { prepareSynthesisCollocatedContext } = await import(
97
+ join(xrayRoot, 'dist/nucleus/synthesis-context.js'),
98
+ );
99
+
100
+ const tmp = mkdtempSync(join(tmpdir(), 'repertoire-synth-dogfood-'));
101
+ mkdirSync(join(tmp, '.xray', 'state'), { recursive: true });
102
+ const dogFeatures = {
103
+ ...features,
104
+ synthesis: { ...features.synthesis, every_n_gates: 1, every_n_turns: 0, every_n_todos_completed: 0 },
105
+ memory_routing: {
106
+ ...features.memory_routing,
107
+ module_path: join(root, 'dist/provider/memory-routing-provider.js'),
108
+ },
109
+ };
110
+ writeFileSync(join(tmp, '.xray/features.json'), JSON.stringify(dogFeatures, null, 2));
111
+
112
+ try {
113
+ recordExecutionSlice('gate', { projectRoot: tmp, sessionId });
114
+ if (isSynthesisCheckpointDue(tmp, sessionId)) pass('nucleus gate slice → synthesis due');
115
+ else fail('nucleus synthesis due');
116
+
117
+ const gateFeatures = loadDelegationGateFeatures(tmp);
118
+ const deny = evaluatePreToolGate(
119
+ 'search_replace',
120
+ { path: 'src/a.ts', new_string: 'x' },
121
+ { projectRoot: tmp, sessionId, features: gateFeatures, host: 'grok' },
122
+ );
123
+ if (!deny.allow && deny.gate === 'synthesis-checkpoint') {
124
+ pass('evaluatePreToolGate denies write when due');
125
+ } else {
126
+ fail('evaluatePreToolGate deny', JSON.stringify(deny));
127
+ }
128
+
129
+ const collation = await prepareSynthesisCollocatedContext(root, 'gate_threshold');
130
+ if (
131
+ collation?.collatedText &&
132
+ !collation.collatedText.includes('Enable memory_routing with Repertoire')
133
+ ) {
134
+ pass('prepareSynthesisCollocatedContext', 'repertoire collation');
135
+ } else {
136
+ fail('prepareSynthesisCollocatedContext', JSON.stringify(collation));
137
+ }
138
+
139
+ const cleared = loadSynthesisCheckpointState(tmp);
140
+ if (cleared?.synthesisDue) pass('checkpoint still due before consult completion');
141
+ else fail('checkpoint state');
142
+ } finally {
143
+ rmSync(tmp, { recursive: true, force: true });
144
+ }
145
+
146
+ const preToolHook = join(xrayRoot, 'dist/integrations/grok/hooks/pre-tool-use.js');
147
+ const hookTmp = mkdtempSync(join(tmpdir(), 'repertoire-grok-hook-'));
148
+ mkdirSync(join(hookTmp, '.xray', 'state'), { recursive: true });
149
+ writeFileSync(
150
+ join(hookTmp, '.xray', 'features.json'),
151
+ JSON.stringify({
152
+ synthesis: { enabled: true, every_n_gates: 12, every_n_turns: 0, every_n_todos_completed: 0 },
153
+ multi_agent_orchestration: { enabled: true, lead_dev_mode: true },
154
+ }),
155
+ );
156
+ const now = new Date().toISOString();
157
+ writeFileSync(
158
+ join(hookTmp, '.xray', 'state', 'synthesis-checkpoint.json'),
159
+ JSON.stringify({
160
+ version: 1,
161
+ sessionId,
162
+ slicesSinceLastSynthesis: { gates: 12, turns: 0, todosCompleted: 0 },
163
+ lifetimeSlices: { gates: 12, turns: 0, todosCompleted: 0 },
164
+ synthesisDue: true,
165
+ dueReason: 'gate_threshold',
166
+ lastSynthesisAt: null,
167
+ lastSliceAt: now,
168
+ synthesisCount: 0,
169
+ }),
170
+ );
171
+ try {
172
+ const payload = JSON.stringify({
173
+ toolName: 'search_replace',
174
+ workspaceRoot: hookTmp,
175
+ sessionId,
176
+ toolInput: { path: 'src/a.ts', new_string: 'x' },
177
+ });
178
+ const out = execSync(
179
+ `printf '%s' '${payload.replace(/'/g, "'\\''")}' | node "${preToolHook}"`,
180
+ {
181
+ encoding: 'utf8',
182
+ env: { ...process.env, GROK_WORKSPACE_ROOT: hookTmp, GROK_SESSION_ID: sessionId },
183
+ },
184
+ );
185
+ const parsed = JSON.parse(out.trim());
186
+ if (parsed.decision === 'deny' && parsed.gate === 'synthesis-checkpoint') {
187
+ pass('live Grok pre-tool-use denies write when due');
188
+ } else {
189
+ fail('live Grok hook', JSON.stringify(parsed));
190
+ }
191
+ } finally {
192
+ rmSync(hookTmp, { recursive: true, force: true });
193
+ }
194
+
195
+ console.log(
196
+ '\n' +
197
+ (failed === 0
198
+ ? '🎉 Synthesis dogfood passed on repertoire core.'
199
+ : `⚠️ ${failed} dogfood check(s) failed.`),
200
+ );
201
+ process.exit(failed === 0 ? 0 : 1);
202
+ }
203
+
204
+ main().catch((e) => {
205
+ console.error(e);
206
+ process.exit(1);
207
+ });