0xray 3.4.1 → 3.4.2

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 (56) hide show
  1. package/.xray/codex.json +598 -0
  2. package/.xray/config.json +25 -0
  3. package/.xray/features.json +146 -0
  4. package/.xray/features.schema.json +33 -0
  5. package/AGENTS-consumer.md +127 -10
  6. package/AGENTS.md +172 -56
  7. package/README.md +159 -82
  8. package/SKILLS.md +79 -53
  9. package/dist/AGENTS.md +172 -56
  10. package/dist/CHANGELOG.md +79 -52
  11. package/dist/README.md +159 -82
  12. package/dist/SKILLS.md +79 -53
  13. package/dist/core/features-config.js +5 -0
  14. package/dist/integrations/grok/hooks/grok-hook-activity.js +53 -0
  15. package/dist/integrations/grok/hooks/grok-hook-utils.js +261 -0
  16. package/dist/integrations/grok/hooks/pre-tool-use.js +85 -115
  17. package/dist/integrations/grok/hooks/session-start.js +52 -0
  18. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +20 -3
  19. package/dist/mcps/config/server-config-registry.js +4 -3
  20. package/dist/mcps/orchestrator/handlers/complexity-handler.js +33 -3
  21. package/dist/mcps/orchestrator/server.js +1 -1
  22. package/dist/mcps/researcher.server.js +3 -0
  23. package/dist/nucleus/autonomy-kernel.js +176 -0
  24. package/dist/nucleus/index.js +1 -0
  25. package/dist/skills/boot-orchestrator/SKILL.md +1 -5
  26. package/dist/skills/orchestrator/SKILL.md +35 -12
  27. package/dist/skills/researcher/SKILL.md +17 -4
  28. package/dist/skills/strategist/SKILL.md +12 -0
  29. package/dist/skills/testing-strategy/SKILL.md +13 -2
  30. package/package.json +23 -8
  31. package/scripts/node/consumer-install-smoke.mjs +163 -0
  32. package/scripts/node/install-bridges.cjs +19 -6
  33. package/scripts/node/pre-publish-guard.js +107 -168
  34. package/scripts/node/reconcile-version.mjs +147 -0
  35. package/scripts/node/release-gate.mjs +66 -0
  36. package/scripts/node/release.js +5 -147
  37. package/scripts/node/release.mjs +106 -181
  38. package/scripts/node/setup.cjs +12 -22
  39. package/scripts/node/sync-versions.mjs +12 -134
  40. package/scripts/node/universal-version-manager.js +4 -0
  41. package/scripts/node/validate-release-docs.mjs +299 -0
  42. package/scripts/node/version-manager.mjs +225 -49
  43. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +20 -3
  44. package/src/mcps/config/__tests__/server-config-registry.test.ts +9 -0
  45. package/src/mcps/config/server-config-registry.ts +5 -3
  46. package/src/mcps/orchestrator/handlers/complexity-handler.ts +54 -3
  47. package/src/mcps/orchestrator/server.ts +1 -1
  48. package/src/mcps/researcher.server.ts +4 -0
  49. package/src/skills/boot-orchestrator/SKILL.md +1 -5
  50. package/src/skills/orchestrator/SKILL.md +35 -12
  51. package/src/skills/researcher/SKILL.md +17 -4
  52. package/src/skills/strategist/SKILL.md +12 -0
  53. package/src/skills/testing-strategy/SKILL.md +13 -2
  54. package/xray/config.json +25 -0
  55. package/xray/features.json +146 -0
  56. package/xray/features.schema.json +33 -0
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared utilities for Grok CLI hooks — codex load, features, surface checks.
4
+ * Pure JS (no TS build) so hooks run from dist/ without compilation surprises.
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+
10
+ export const LEAD_DEV_RULES = [
11
+ 'Phased plan + todos; assign best subagent; monitor output',
12
+ 'Lead dev loops test→fix until green; no permission pings',
13
+ 'Per-suite test triage; full suite last',
14
+ 'Lead stays main; subagents execute; update todos',
15
+ 'Read all output; triage fix rerun',
16
+ 'No buck-passing — resolve every error',
17
+ 'Zero open errors at phase completion',
18
+ ];
19
+
20
+ /** Blocking codex patterns detectable in proposed edits (term → regex + message) */
21
+ export const CODEX_BLOCK_PATTERNS = [
22
+ { terms: [11], pattern: /:\s*any\b|as\s+any\b/g, message: 'Codex 11 Type Safety: no `any` types' },
23
+ { terms: [11], pattern: /@ts-ignore|@ts-expect-error/g, message: 'Codex 11 Type Safety: no @ts-ignore / @ts-expect-error' },
24
+ { terms: [29], pattern: /\beval\s*\(/g, message: 'Codex 29 Security: eval() prohibited' },
25
+ { terms: [2], pattern: /\bTODO\b|\bFIXME\b|\bSTUB\b/g, message: 'Codex 2: no TODO/FIXME/STUB in production code' },
26
+ { terms: [7], pattern: /console\.log\s*\(/g, message: 'Codex 7: no console.log debugging' },
27
+ ];
28
+
29
+ /** New-surface paths blocked when no_new_surface is on (codex 69) */
30
+ export const SURFACE_DENY_PATTERNS = [
31
+ { pattern: /(?:^|\/)src\/mcps\/[^/]+\.server\.(ts|js)$/i, message: 'Codex 69: new MCP server — rewire existing mcps/' },
32
+ { pattern: /(?:^|\/)src\/skills\/[^/]+\/SKILL\.md$/i, message: 'Codex 69: new skill — extend orchestrator skill or existing mcps' },
33
+ { pattern: /(?:^|\/)src\/mcps\/orchestrator\/handlers\/[^/]+-handler\.(ts|js)$/i, message: 'Codex 69: new orchestrator handler — merge into complexity-handler' },
34
+ { pattern: /(?:^|\/)src\/nucleus\/autonomy-kernel\.(ts|js)$/i, message: 'Codex 69: parallel autonomy kernel — use existing nucleus' },
35
+ { pattern: /(?:^|\/)src\/mcps\/[^/]+\/handlers\/autonomy-handler\.(ts|js)$/i, message: 'Codex 69: autonomy-handler removed — use complexity-handler' },
36
+ ];
37
+
38
+ const SUBAGENT_TOOLS = new Set([
39
+ 'spawn_subagent',
40
+ 'task',
41
+ 'Task',
42
+ ]);
43
+
44
+ const WRITE_TOOLS = new Set([
45
+ 'search_replace',
46
+ 'write',
47
+ 'Write',
48
+ 'Edit',
49
+ 'MultiEdit',
50
+ 'edit_notebook',
51
+ 'EditNotebook',
52
+ ]);
53
+
54
+ export function workspaceRoot() {
55
+ return (
56
+ process.env.GROK_WORKSPACE_ROOT ||
57
+ process.env.CLAUDE_PROJECT_DIR ||
58
+ process.env.XRAY_ROOT ||
59
+ process.cwd()
60
+ );
61
+ }
62
+
63
+ export function resolveFeaturesPath(root = workspaceRoot()) {
64
+ for (const p of [
65
+ path.join(root, '.xray', 'features.json'),
66
+ path.join(process.cwd(), '.xray', 'features.json'),
67
+ ]) {
68
+ if (fs.existsSync(p)) return p;
69
+ }
70
+ return null;
71
+ }
72
+
73
+ export function resolveCodexPath(root = workspaceRoot()) {
74
+ for (const p of [
75
+ path.join(root, '.xray', 'codex.json'),
76
+ path.join(process.cwd(), '.xray', 'codex.json'),
77
+ ]) {
78
+ if (fs.existsSync(p)) return p;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ export function loadFeatures() {
84
+ const featuresPath = resolveFeaturesPath();
85
+ if (!featuresPath) return { lead_dev_mode: true, no_new_surface: true, per_suite_triage: true };
86
+ try {
87
+ const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
88
+ const orch = data.multi_agent_orchestration ?? {};
89
+ return {
90
+ lead_dev_mode: orch.enabled !== false && orch.lead_dev_mode !== false,
91
+ no_new_surface: orch.no_new_surface !== false,
92
+ per_suite_test_triage: orch.per_suite_test_triage !== false,
93
+ };
94
+ } catch {
95
+ return { lead_dev_mode: true, no_new_surface: true, per_suite_triage: true };
96
+ }
97
+ }
98
+
99
+ export function loadBlockingCodexTerms() {
100
+ const codexPath = resolveCodexPath();
101
+ if (!codexPath) return [];
102
+ try {
103
+ const data = JSON.parse(fs.readFileSync(codexPath, 'utf8'));
104
+ const terms = data.terms ?? {};
105
+ return Object.values(terms).filter(
106
+ (t) => t.enforcementLevel === 'blocking' || t.zeroTolerance === true,
107
+ );
108
+ } catch {
109
+ return [];
110
+ }
111
+ }
112
+
113
+ export function leadDevPlanPath(root = workspaceRoot()) {
114
+ return path.join(root, '.xray', 'state', 'lead-dev-plan.json');
115
+ }
116
+
117
+ export function hasFreshLeadDevPlan(maxAgeMs = 4 * 60 * 60 * 1000) {
118
+ const planPath = leadDevPlanPath();
119
+ if (!fs.existsSync(planPath)) return false;
120
+ try {
121
+ const stat = fs.statSync(planPath);
122
+ if (Date.now() - stat.mtimeMs > maxAgeMs) return false;
123
+ const plan = JSON.parse(fs.readFileSync(planPath, 'utf8'));
124
+ return plan.active === true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+
130
+ export function extractToolContext(event) {
131
+ const toolName = event.toolName || process.env.TOOL_NAME || 'unknown';
132
+ const toolInput = event.toolInput ?? {};
133
+ const paths = [];
134
+ let content = '';
135
+
136
+ if (toolInput.path) paths.push(String(toolInput.path));
137
+ if (toolInput.file_path) paths.push(String(toolInput.file_path));
138
+ if (toolInput.target_notebook) paths.push(String(toolInput.target_notebook));
139
+ if (Array.isArray(toolInput.paths)) paths.push(...toolInput.paths.map(String));
140
+
141
+ if (toolInput.new_string) content += String(toolInput.new_string);
142
+ if (toolInput.contents) content += String(toolInput.contents);
143
+ if (toolInput.command) content += String(toolInput.command);
144
+ if (toolInput.prompt) content += String(toolInput.prompt);
145
+
146
+ return { toolName, toolInput, paths, content, cmd: String(toolInput.command || '') };
147
+ }
148
+
149
+ export function checkCodexPatterns(content) {
150
+ if (!content) return null;
151
+ for (const { message, pattern } of CODEX_BLOCK_PATTERNS) {
152
+ if (pattern.test(content)) return message;
153
+ pattern.lastIndex = 0;
154
+ }
155
+ return null;
156
+ }
157
+
158
+ export function checkSurfaceArea(paths, root = workspaceRoot()) {
159
+ for (const rel of paths) {
160
+ const normalized = rel.replace(/\\/g, '/');
161
+ const abs = path.isAbsolute(rel) ? rel : path.join(root, rel);
162
+ if (fs.existsSync(abs)) continue;
163
+ for (const { pattern, message } of SURFACE_DENY_PATTERNS) {
164
+ if (pattern.test(normalized)) return `${message} (blocked: ${normalized})`;
165
+ }
166
+ }
167
+ return null;
168
+ }
169
+
170
+ export function checkSubagentGate(toolName, features) {
171
+ if (!features.lead_dev_mode) return null;
172
+ if (!SUBAGENT_TOOLS.has(toolName)) return null;
173
+ if (hasFreshLeadDevPlan()) return null;
174
+ return (
175
+ 'Codex 59/67: call xray-orchestrator analyze-complexity first — ' +
176
+ 'writes .xray/state/lead-dev-plan.json required before spawn_subagent'
177
+ );
178
+ }
179
+
180
+ export function checkFullTestSuite(cmd, features) {
181
+ if (!features.per_suite_test_triage) return null;
182
+ const cmdText = cmd.toLowerCase();
183
+ const isFullTestSuite =
184
+ /\b(npm\s+(run\s+)?test|vitest\s+run|pnpm\s+test|yarn\s+test)\b/.test(cmdText) &&
185
+ !/--\s+\S+\.test/.test(cmdText) &&
186
+ !/vitest\s+run\s+\S+\.test/.test(cmdText);
187
+ if (!isFullTestSuite) return null;
188
+ return (
189
+ 'Lead-dev test protocol: run individual test files first; ' +
190
+ 'full suite only after per-suite passes (override with focused test path)'
191
+ );
192
+ }
193
+
194
+ export function isWriteTool(toolName) {
195
+ return WRITE_TOOLS.has(toolName) || /write|edit|replace/i.test(toolName);
196
+ }
197
+
198
+ export function isShellTool(toolName) {
199
+ return /terminal|bash|shell|run_terminal/i.test(toolName);
200
+ }
201
+
202
+ export async function readStdinJson() {
203
+ const chunks = [];
204
+ for await (const chunk of process.stdin) chunks.push(chunk);
205
+ const raw = Buffer.concat(chunks).toString('utf8').trim();
206
+ if (!raw) return {};
207
+ try {
208
+ return JSON.parse(raw);
209
+ } catch {
210
+ return {};
211
+ }
212
+ }
213
+
214
+ export function sessionBootPath(root = workspaceRoot()) {
215
+ return path.join(root, '.xray', 'state', 'session-boot.json');
216
+ }
217
+
218
+ export function buildSessionBootPayload(root, source = '0xray/grok-session-start', extra = {}) {
219
+ const features = loadFeatures();
220
+ const blockingTerms = loadBlockingCodexTerms();
221
+ return {
222
+ hook: source,
223
+ lead_dev_mode: features.lead_dev_mode,
224
+ no_new_surface: features.no_new_surface,
225
+ codexBlockingTermCount: blockingTerms.length,
226
+ codexTerms: [59, 67, 68, 69],
227
+ rules: features.lead_dev_mode ? LEAD_DEV_RULES : [],
228
+ mcpIntake: 'xray-orchestrator → analyze-complexity (required before spawn_subagent)',
229
+ enforcement: 'PreToolUse hook — codex patterns + surface area + spawn gate',
230
+ workspaceRoot: root,
231
+ sessionId: process.env.GROK_SESSION_ID || null,
232
+ timestamp: new Date().toISOString(),
233
+ source,
234
+ ...extra,
235
+ };
236
+ }
237
+
238
+ export function writeSessionBoot(root, payload) {
239
+ try {
240
+ const stateDir = path.join(root, '.xray', 'state');
241
+ fs.mkdirSync(stateDir, { recursive: true });
242
+ fs.writeFileSync(sessionBootPath(root), JSON.stringify(payload, null, 2));
243
+ return sessionBootPath(root);
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+
249
+ /** Boot on SessionStart, UserPromptSubmit, or first PreToolUse if missing. */
250
+ export function ensureSessionBoot(root = workspaceRoot(), source = '0xray/grok-boot') {
251
+ const bootPath = sessionBootPath(root);
252
+ if (fs.existsSync(bootPath)) {
253
+ try {
254
+ const existing = JSON.parse(fs.readFileSync(bootPath, 'utf8'));
255
+ if (existing.lead_dev_mode !== undefined) return bootPath;
256
+ } catch {
257
+ /* rewrite corrupt boot */
258
+ }
259
+ }
260
+ return writeSessionBoot(root, buildSessionBootPayload(root, source));
261
+ }
@@ -1,134 +1,104 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Grok CLI PreToolUse Hook Handler for 0xRay — First Class Citizen
4
- *
5
- * Real governance enforcement hook.
6
- * When Grok is about to execute a tool (write_file, edit, terminal cmd, etc.),
7
- * this script is spawned and runs the Dynamo Solar SSOT decision matrix.
3
+ * Grok CLI PreToolUse — ironclad OS gate
4
+ * Contract: stdin JSON → stdout {"decision":"allow"} | {"decision":"deny","reason":"..."}
8
5
  */
9
6
 
10
- import fs from 'fs';
11
- import path from 'path';
7
+ import {
8
+ checkCodexPatterns,
9
+ checkFullTestSuite,
10
+ checkSubagentGate,
11
+ checkSurfaceArea,
12
+ ensureSessionBoot,
13
+ isShellTool,
14
+ isWriteTool,
15
+ loadFeatures,
16
+ readStdinJson,
17
+ workspaceRoot,
18
+ } from './grok-hook-utils.js';
19
+ import { appendHookActivity } from './grok-hook-activity.js';
12
20
 
13
- const log = (msg) => { try { console.error(`[0xRay:GrokHook] ${msg}`); } catch { /* noop */ } };
21
+ function finish(root, decision, reason, hint, toolName) {
22
+ const out = { decision };
23
+ if (reason) out.reason = reason;
24
+ if (hint) out.hint = hint;
25
+ console.log(JSON.stringify(out));
26
+ appendHookActivity(root, 'grok-pre-tool-use', decision, decision === 'deny' ? 'error' : 'info', {
27
+ tool: toolName,
28
+ reason: reason || hint || null,
29
+ livePath: true,
30
+ });
31
+ process.exit(0);
32
+ }
14
33
 
15
- function findGovernanceCore() {
16
- const here = path.dirname(new URL(import.meta.url).pathname);
34
+ async function main() {
35
+ const root = workspaceRoot();
36
+ let toolName = 'unknown';
17
37
 
18
- // Priority: explicit dev root
19
- const devRoot = process.env.XRAY_ROOT;
20
- if (devRoot) {
21
- const devCandidate = path.resolve(devRoot, 'dist/governance/governance-core.js');
22
- if (fs.existsSync(devCandidate)) return devCandidate;
23
- }
38
+ try {
39
+ const event = await readStdinJson();
40
+ const eventRoot = event.workspaceRoot || event.cwd || root;
41
+ ensureSessionBoot(eventRoot, '0xray/grok-pre-tool-use-boot');
24
42
 
25
- const candidates = [
26
- // Published package layout
27
- path.resolve(here, '../../../governance/governance-core.js'),
28
- path.resolve(here, '../../../../governance/governance-core.js'),
29
- // node_modules installed layout
30
- path.resolve(process.cwd(), 'node_modules/0xray/dist/governance/governance-core.js'),
31
- // Dev layout fallbacks (no extra dist/)
32
- path.resolve(here, '../../../../../governance/governance-core.js'),
33
- path.resolve(process.cwd(), 'dist/governance/governance-core.js'),
34
- // Strong dev fallback: walk up looking for package.json
35
- ...walkUpForCore(here),
36
- ];
37
-
38
- for (const c of candidates) {
39
- if (fs.existsSync(c)) return c;
40
- }
41
- return null;
42
- }
43
+ const features = loadFeatures();
44
+ const ctx = extractFromEvent(event);
45
+ toolName = ctx.toolName;
46
+ const { paths, content, cmd } = ctx;
43
47
 
44
- function walkUpForCore(startDir, maxLevels = 8) {
45
- const results = [];
46
- let current = startDir;
47
- for (let i = 0; i < maxLevels; i++) {
48
- const pkgPath = path.resolve(current, 'package.json');
49
- if (fs.existsSync(pkgPath)) {
50
- try {
51
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
52
- if (pkg.name === '0xray') {
53
- results.push(path.resolve(current, 'dist/governance/governance-core.js'));
54
- results.push(path.resolve(current, 'governance/governance-core.js'));
55
- }
56
- } catch {}
48
+ const subagentBlock = checkSubagentGate(toolName, features);
49
+ if (subagentBlock) finish(eventRoot, 'deny', subagentBlock, null, toolName);
50
+
51
+ if (features.no_new_surface && isWriteTool(toolName) && paths.length) {
52
+ const surfaceBlock = checkSurfaceArea(paths, eventRoot);
53
+ if (surfaceBlock) finish(eventRoot, 'deny', surfaceBlock, null, toolName);
57
54
  }
58
- const parent = path.dirname(current);
59
- if (parent === current) break;
60
- current = parent;
61
- }
62
- return results;
63
- }
64
55
 
65
- function deriveResonance(toolName, extra = '') {
66
- const text = (toolName + ' ' + extra).toLowerCase();
67
- let score = 0.85; // baseline good
68
- const penalties = [
69
- ['any', -0.25],
70
- ['eval(', -0.35],
71
- ['console.log', -0.15],
72
- ['// @ts-ignore', -0.2],
73
- ['require(', -0.1],
74
- ];
75
- for (const [pat, pen] of penalties) {
76
- if (text.includes(pat)) score += pen;
77
- }
78
- return Math.max(0.1, Math.min(0.99, score));
79
- }
56
+ if (isWriteTool(toolName) && content) {
57
+ const codexBlock = checkCodexPatterns(content);
58
+ if (codexBlock) finish(eventRoot, 'deny', codexBlock, null, toolName);
59
+ }
80
60
 
81
- async function main() {
82
- try {
83
- const toolName = process.env.TOOL_NAME || process.env.HOOK_TOOL || 'unknown_tool';
84
- const cwd = process.env.PWD || process.cwd();
85
- const extraContext = process.env.HOOK_ARGS || process.env.TOOL_ARGS || '';
86
-
87
- log(`PreToolUse: ${toolName}`);
88
-
89
- const corePath = findGovernanceCore();
90
- let recommendation = 'ALLOW';
91
- let resonance = deriveResonance(toolName, extraContext);
92
- let govDetails = null;
93
-
94
- if (corePath) {
95
- try {
96
- const core = await import(`file://${corePath}`);
97
- if (typeof core.applyDecisionMatrix === 'function') {
98
- const result = core.applyDecisionMatrix({
99
- resonance,
100
- isotopicRatio: resonance > 0.8 ? 0.96 : 0.7,
101
- vortexVolume: 1_000_000,
102
- historicalCoherence: 0.82,
103
- solarActivity: 'active',
104
- });
105
- govDetails = result;
106
- recommendation = result.recommendation || 'NEEDS_REVISION';
107
- log(`Solar decision: ${recommendation} (resonance=${resonance.toFixed(2)})`);
108
- }
109
- } catch (e) {
110
- log(`Governance core error: ${e.message}`);
111
- }
112
- } else {
113
- log('Governance core not located — using safe default');
61
+ if (isShellTool(toolName) && cmd) {
62
+ const destructive =
63
+ /\brm\s+-rf\s+\/\b|\bmkfs\b|\bdd\s+if=|:()\s*\{\s*:\|&\s*\}\s*;:/i.test(cmd);
64
+ if (destructive) finish(eventRoot, 'deny', 'Blocked destructive shell command', null, toolName);
65
+
66
+ const testHint = checkFullTestSuite(cmd, features);
67
+ if (testHint) finish(eventRoot, 'allow', null, testHint, toolName);
114
68
  }
115
69
 
116
- const decision = {
117
- tool: toolName,
118
- decision: recommendation === 'PASS' ? 'allow' : 'allow_with_governance_note',
119
- resonance: Number(resonance.toFixed(3)),
120
- solar_recommendation: recommendation,
121
- gov: govDetails,
122
- timestamp: new Date().toISOString(),
123
- source: '0xray/grok-pre-tool-use',
124
- };
125
-
126
- console.log(JSON.stringify(decision));
127
- process.exit(0); // non-blocking rollout; future versions can exit(1) on strong reject
70
+ finish(eventRoot, 'allow', null, null, toolName);
128
71
  } catch (err) {
129
- log(`Hook failure (non-fatal): ${err.message}`);
130
- process.exit(0);
72
+ appendHookActivity(root, 'grok-pre-tool-use', 'hook-error', 'error', {
73
+ tool: toolName,
74
+ error: err.message,
75
+ });
76
+ finish(root, 'allow', null, null, toolName);
131
77
  }
132
78
  }
133
79
 
80
+ function extractFromEvent(event) {
81
+ const toolName = event.toolName || process.env.TOOL_NAME || 'unknown';
82
+ const toolInput = event.toolInput ?? {};
83
+ const paths = [];
84
+ let content = '';
85
+
86
+ if (toolInput.path) paths.push(String(toolInput.path));
87
+ if (toolInput.file_path) paths.push(String(toolInput.file_path));
88
+ if (toolInput.target_notebook) paths.push(String(toolInput.target_notebook));
89
+ if (Array.isArray(toolInput.paths)) paths.push(...toolInput.paths.map(String));
90
+
91
+ if (toolInput.new_string) content += String(toolInput.new_string);
92
+ if (toolInput.contents) content += String(toolInput.contents);
93
+ if (toolInput.command) content += String(toolInput.command);
94
+ if (toolInput.prompt) content += String(toolInput.prompt);
95
+
96
+ return {
97
+ toolName,
98
+ paths,
99
+ content,
100
+ cmd: String(toolInput.command || ''),
101
+ };
102
+ }
103
+
134
104
  main();
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Grok SessionStart / UserPromptSubmit — writes session-boot.json + activity.log.
4
+ * Stdout is ignored by Grok for these events; side effects are the suit contract.
5
+ */
6
+
7
+ import {
8
+ buildSessionBootPayload,
9
+ ensureSessionBoot,
10
+ readStdinJson,
11
+ workspaceRoot,
12
+ writeSessionBoot,
13
+ } from './grok-hook-utils.js';
14
+ import { appendHookActivity } from './grok-hook-activity.js';
15
+
16
+ const HOOK_EVENT = process.env.GROK_HOOK_EVENT || 'session_start';
17
+
18
+ async function main() {
19
+ const root = workspaceRoot();
20
+ try {
21
+ const event = await readStdinJson();
22
+ const eventRoot = event.workspaceRoot || event.cwd || root;
23
+ const source =
24
+ HOOK_EVENT === 'user_prompt_submit'
25
+ ? '0xray/grok-user-prompt-submit'
26
+ : '0xray/grok-session-start';
27
+
28
+ const payload = buildSessionBootPayload(eventRoot, source, {
29
+ hookEvent: HOOK_EVENT,
30
+ sessionId: event.sessionId || process.env.GROK_SESSION_ID || null,
31
+ });
32
+
33
+ const bootPath = writeSessionBoot(eventRoot, payload) || ensureSessionBoot(eventRoot, source);
34
+
35
+ appendHookActivity(eventRoot, 'grok-session-start', 'session-boot-written', 'success', {
36
+ bootPath,
37
+ hookEvent: HOOK_EVENT,
38
+ lead_dev_mode: payload.lead_dev_mode,
39
+ });
40
+
41
+ console.log(JSON.stringify(payload));
42
+ process.exit(0);
43
+ } catch (err) {
44
+ appendHookActivity(root, 'grok-session-start', 'session-boot-error', 'error', {
45
+ error: err.message,
46
+ hookEvent: HOOK_EVENT,
47
+ });
48
+ process.exit(0);
49
+ }
50
+ }
51
+
52
+ main();
@@ -20,11 +20,28 @@
20
20
  "hooks": [
21
21
  {
22
22
  "type": "command",
23
- "command": "echo",
24
- "args": ["[0xRay] Grok session started with 0xRay governance enabled"]
23
+ "command": "node",
24
+ "args": [
25
+ "${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js"
26
+ ],
27
+ "timeout": 15000
28
+ }
29
+ ]
30
+ }
31
+ ],
32
+ "UserPromptSubmit": [
33
+ {
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": "node",
38
+ "args": [
39
+ "${XRAY_AI_PATH:-node_modules/0xray}/dist/integrations/grok/hooks/session-start.js"
40
+ ],
41
+ "timeout": 15000
25
42
  }
26
43
  ]
27
44
  }
28
45
  ]
29
46
  }
30
- }
47
+ }
@@ -38,10 +38,11 @@ function resolveFrameworkPaths() {
38
38
  if (fs.existsSync(pkgPath)) {
39
39
  try {
40
40
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
41
- if (pkg.name === '0xray' && pkg.xray) {
42
- const xrayCfg = pkg.xray;
41
+ if (pkg.name === '0xray') {
42
+ const xrayCfg = (pkg.xray ?? {});
43
43
  const frameworkRoot = currentDir;
44
- const declaredMcp = xrayCfg.mcpServersPath || (xrayCfg.dist ? join(xrayCfg.dist, 'mcps') : 'dist/mcps');
44
+ const declaredMcp = xrayCfg.mcpServersPath ||
45
+ (xrayCfg.dist ? join(xrayCfg.dist, 'mcps') : 'dist/mcps');
45
46
  return {
46
47
  frameworkRoot,
47
48
  mcpServersPath: join(frameworkRoot, declaredMcp),
@@ -4,6 +4,7 @@
4
4
  * Handles complexity analysis requests
5
5
  */
6
6
  import { frameworkLogger } from '../../../core/framework-logger.js';
7
+ import { buildLeadDevPlan, isLeadDevModeActive, persistLeadDevPlan, } from '../../../nucleus/autonomy-kernel.js';
7
8
  import { getExecutionPlanner } from '../execution/execution-planner.js';
8
9
  import { addObservations, extractComplexityObservations } from '../aside-context.js';
9
10
  /**
@@ -24,11 +25,24 @@ export class ComplexityHandler {
24
25
  if (asideId) {
25
26
  addObservations(asideId, extractComplexityObservations(analysis));
26
27
  }
28
+ const primaryDescription = tasks.map((t) => t.description).filter(Boolean).join(' ');
29
+ const taskTypes = tasks.map((t) => t.type).filter(Boolean);
30
+ const leadDevPlan = isLeadDevModeActive() && primaryDescription
31
+ ? buildLeadDevPlan(primaryDescription, taskTypes.length ? taskTypes : ['implement'])
32
+ : null;
33
+ if (leadDevPlan) {
34
+ try {
35
+ persistLeadDevPlan(leadDevPlan);
36
+ }
37
+ catch (err) {
38
+ await frameworkLogger.log('orchestrator.server', 'lead-dev-plan-persist-failed', 'warning', { error: err instanceof Error ? err.message : String(err) });
39
+ }
40
+ }
27
41
  return {
28
42
  content: [
29
43
  {
30
44
  type: 'text',
31
- text: this.formatComplexityResponse(analysis, recommendations, tasks.length),
45
+ text: this.formatComplexityResponse(analysis, recommendations, tasks.length, leadDevPlan),
32
46
  },
33
47
  ],
34
48
  };
@@ -72,7 +86,23 @@ export class ComplexityHandler {
72
86
  /**
73
87
  * Format complexity response
74
88
  */
75
- formatComplexityResponse(analysis, recommendations, taskCount) {
89
+ formatComplexityResponse(analysis, recommendations, taskCount, leadDevPlan = null) {
90
+ const leadDevSection = leadDevPlan
91
+ ? `
92
+
93
+ **Lead-dev plan (codex ${leadDevPlan.codexTerms.join(', ')}):**
94
+ ${leadDevPlan.phases
95
+ .map((p) => `### ${p.id} — ${p.name}\n${p.todos
96
+ .map((t) => `- [ ] ${t.id} (${t.subagent}): ${t.task}`)
97
+ .join('\n')}`)
98
+ .join('\n\n')}
99
+
100
+ **Test protocol:** ${leadDevPlan.testProtocol.hint}
101
+
102
+ \`\`\`json
103
+ ${JSON.stringify(leadDevPlan, null, 2)}
104
+ \`\`\``
105
+ : '';
76
106
  return `🔍 Complexity Analysis Results
77
107
 
78
108
  **Tasks Analyzed:** ${taskCount}
@@ -93,6 +123,6 @@ ${analysis.agentAssignments
93
123
  ${recommendations.map((r) => `• 💡 ${r}`).join('\n')}
94
124
 
95
125
  **Execution Estimate:** ${analysis.estimatedDuration}ms
96
- **Parallel Potential:** ${Math.round(analysis.parallelPotential * 100)}%`;
126
+ **Parallel Potential:** ${Math.round(analysis.parallelPotential * 100)}%${leadDevSection}`;
97
127
  }
98
128
  }
@@ -105,7 +105,7 @@ export class OrchestratorServer {
105
105
  },
106
106
  {
107
107
  name: 'analyze-complexity',
108
- description: 'Analyze task complexity and recommend optimal orchestration strategy',
108
+ description: 'Analyze task complexity and recommend orchestration strategy. When lead_dev_mode is on (features.json), includes phased plan + todos + subagent routes.',
109
109
  inputSchema: {
110
110
  type: 'object',
111
111
  properties: {
@@ -11,11 +11,14 @@ import * as fs from "fs";
11
11
  import * as path from "path";
12
12
  import { frameworkLogger } from "../core/framework-logger.js";
13
13
  import { tryLLMGovernance } from "../governance/llm-governance-provider.js";
14
+ import { initializeMemoryRouting } from "../memory-routing/index.js";
14
15
  import { buildMemoryRoutingEvidence, formatMemoryRoutingBlock, resolveResearcherMemoryContext, } from "./researcher-confidence.js";
15
16
  import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
16
17
  class XrayLibrarianServer extends XrayKnowledgeSkillBase {
17
18
  constructor() {
18
19
  super("researcher", "3.1.0");
20
+ // Kick off MemoryRoutingProvider load so trap routing is ready in MCP subprocess mode.
21
+ initializeMemoryRouting();
19
22
  this.tools = [
20
23
  {
21
24
  name: "search_codebase",