0xray 3.5.4 → 3.5.5

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 (73) hide show
  1. package/.xray/features.json +5 -1
  2. package/.xray/features.schema.json +56 -0
  3. package/AGENTS-consumer.md +1 -1
  4. package/AGENTS.md +11 -1
  5. package/README.md +1 -1
  6. package/SKILLS.md +1 -1
  7. package/dist/AGENTS.md +11 -1
  8. package/dist/CHANGELOG.md +44 -0
  9. package/dist/README.md +1 -1
  10. package/dist/SKILLS.md +1 -1
  11. package/dist/cli/commands/hermes-install.js +3 -1
  12. package/dist/cli/index.js +0 -0
  13. package/dist/core/features-config.d.ts +15 -0
  14. package/dist/governance/governance-service.d.ts +1 -0
  15. package/dist/governance/governance-service.js +81 -6
  16. package/dist/integrations/enforcement-gate.js +15 -5
  17. package/dist/integrations/grok/grok-cli.js +1 -10
  18. package/dist/integrations/grok/hooks/grok-hook-utils.js +46 -2
  19. package/dist/integrations/grok/hooks/post-tool-use.js +21 -0
  20. package/dist/integrations/grok/hooks/pre-tool-use.js +20 -0
  21. package/dist/integrations/grok/hooks/session-start.js +28 -0
  22. package/dist/integrations/hermes-agent/__init__.py +11 -1
  23. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  25. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  26. package/dist/integrations/hermes-agent/bridge.mjs +73 -10
  27. package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
  28. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
  29. package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
  30. package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
  31. package/dist/mcps/architect-tools.server.js +6 -1
  32. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
  33. package/dist/mcps/orchestrator/handlers/complexity-handler.js +106 -7
  34. package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
  35. package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
  36. package/dist/mcps/orchestrator/server.js +54 -5
  37. package/dist/memory-routing/provider-loader.js +30 -2
  38. package/dist/nucleus/aside-worktree.d.ts +41 -0
  39. package/dist/nucleus/aside-worktree.js +164 -0
  40. package/dist/nucleus/confer.d.ts +57 -0
  41. package/dist/nucleus/confer.js +308 -0
  42. package/dist/nucleus/delegation-gate.d.ts +4 -0
  43. package/dist/nucleus/delegation-gate.js +90 -11
  44. package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
  45. package/dist/nucleus/lead-dev-plan-persistence.js +201 -19
  46. package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
  47. package/dist/nucleus/spawn-plan-resolution.js +42 -0
  48. package/dist/nucleus/synthesis-consult-receipt.js +18 -2
  49. package/dist/nucleus/synthesis-context.js +1 -1
  50. package/dist/nucleus/user-aside.d.ts +70 -0
  51. package/dist/nucleus/user-aside.js +393 -0
  52. package/dist/postprocessor/PostProcessor.d.ts +1 -0
  53. package/dist/postprocessor/PostProcessor.js +11 -0
  54. package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
  55. package/dist/processors/implementations/session-summary-processor.js +9 -6
  56. package/package.json +14 -5
  57. package/scripts/hooks/install-hooks.cjs +3 -3
  58. package/scripts/hooks/run-hook.js +171 -86
  59. package/scripts/mjs/verify-confer-core.mjs +103 -0
  60. package/scripts/mjs/verify-delegation-gate-core.mjs +48 -2
  61. package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
  62. package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
  63. package/scripts/mjs/verify-user-aside-core.mjs +137 -0
  64. package/scripts/node/bridge-mcp-wiring.cjs +10 -0
  65. package/scripts/node/install-bridges.cjs +3 -11
  66. package/scripts/node/load-reflection-config.mjs +2 -2
  67. package/scripts/node/release-gate.mjs +8 -4
  68. package/src/mcps/architect-tools.server.ts +6 -1
  69. package/src/mcps/orchestrator/handlers/complexity-handler.ts +178 -20
  70. package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
  71. package/src/mcps/orchestrator/server.ts +56 -5
  72. package/xray/features.json +5 -1
  73. package/xray/features.schema.json +56 -0
@@ -15,6 +15,12 @@ import {
15
15
  } from './grok-hook-utils.js';
16
16
  import { appendHookActivity } from './grok-hook-activity.js';
17
17
  import { recordSynthesisTurnSlice } from '../../hooks/synthesis-hook-runtime.mjs';
18
+ import { archiveStaleLeadDevPlan } from '../../hooks/plan-hook-runtime.mjs';
19
+ import {
20
+ maybeRunReflectionStub,
21
+ scheduleAutonomousReportingMarker,
22
+ runInferenceImprovementLight,
23
+ } from '../../hooks/pipeline-hook-runtime.mjs';
18
24
 
19
25
  function resolveHookEvent(event) {
20
26
  if (process.env.GROK_HOOK_EVENT) return process.env.GROK_HOOK_EVENT;
@@ -47,6 +53,18 @@ async function main() {
47
53
  sessionId,
48
54
  });
49
55
  }
56
+
57
+ try {
58
+ const archived = archiveStaleLeadDevPlan(eventRoot);
59
+ if (archived.archived) {
60
+ appendHookActivity(eventRoot, 'grok-session-start', 'stale-plan-archived', 'info', {
61
+ archivePath: archived.archivePath,
62
+ reason: archived.reason,
63
+ });
64
+ }
65
+ } catch {
66
+ /* non-blocking */
67
+ }
50
68
  const source =
51
69
  HOOK_EVENT === 'user_prompt_submit'
52
70
  ? '0xray/grok-user-prompt-submit'
@@ -65,6 +83,16 @@ async function main() {
65
83
  lead_dev_mode: payload.lead_dev_mode,
66
84
  });
67
85
 
86
+ try {
87
+ scheduleAutonomousReportingMarker(eventRoot);
88
+ if (HOOK_EVENT === 'session_start') {
89
+ maybeRunReflectionStub(eventRoot);
90
+ runInferenceImprovementLight(eventRoot);
91
+ }
92
+ } catch {
93
+ /* non-blocking pipeline facets */
94
+ }
95
+
68
96
  console.log(JSON.stringify(payload));
69
97
  process.exit(0);
70
98
  } catch (err) {
@@ -206,6 +206,10 @@ def _delegation_gate_block(tool_name: str, args: dict, task_id: str, **kwargs):
206
206
  },
207
207
  timeout=10,
208
208
  )
209
+ if bridge_result.get("error") and bridge_result.get("allow") is not False:
210
+ err = bridge_result.get("error", "delegation-gate bridge error")
211
+ _log_to_file("activity.log", f"[delegation-gate] bridge-error tool={tool_name} {err}")
212
+ return {"action": "block", "message": err}
209
213
  if bridge_result.get("allow") is False:
210
214
  reason = bridge_result.get("reason", "Delegation gate blocked this tool")
211
215
  gate = bridge_result.get("gate", "delegation-gate")
@@ -376,6 +380,8 @@ def _on_post_tool_call(tool_name: str, args: dict, result, task_id: str, **kwarg
376
380
  "args": args or {},
377
381
  "sessionId": session_id,
378
382
  "host": "hermes",
383
+ "toolOutput": result,
384
+ "result": result,
379
385
  },
380
386
  timeout=10,
381
387
  )
@@ -452,6 +458,10 @@ def _on_session_start(session_id: str, platform: str, **kwargs):
452
458
  _ensure_log_dir()
453
459
  _log_to_file("activity.log",
454
460
  f"[session-start] session={session_id} platform={platform}")
461
+ try:
462
+ _call_bridge({"command": "session-start", "sessionId": session_id}, timeout=10)
463
+ except Exception as e:
464
+ logger.debug("[xray] session-start bridge skipped: %s", e)
455
465
  logger.info("[xray] Session %s started on %s", session_id, platform)
456
466
 
457
467
 
@@ -771,7 +781,7 @@ def register(ctx):
771
781
 
772
782
  # Log session-start since on_session_start may not fire in oneshot mode
773
783
  _log_to_file("activity.log",
774
- f"[session-start] session={session_id}")
784
+ "[session-start] session=bootstrap source=plugin-load")
775
785
 
776
786
  logger.info(
777
787
  "[xray] Plugin v2.2 loaded: 6 tools, 2 hooks, "
@@ -15,6 +15,7 @@
15
15
  * codex-check - Check code against codex rules
16
16
  * stats - Return bridge/framework statistics
17
17
  * hooks - Manage git hooks (install, uninstall, list, status)
18
+ * session-start - Session boot: archive stale lead-dev plans (synthesis exempt)
18
19
  * govern - Govern inference proposals through weighted voting
19
20
  * apply - Govern + apply approved inference proposals
20
21
  *
@@ -36,13 +37,42 @@ import {
36
37
  unlinkSync,
37
38
  renameSync,
38
39
  } from "fs";
39
- import { join, dirname, relative } from "path";
40
- import { fileURLToPath } from "url";
40
+ import { join, dirname, relative, resolve } from "path";
41
+ import { fileURLToPath, pathToFileURL } from "url";
41
42
  import { homedir } from "os";
42
- import { findProjectRoot } from "../../../scripts/helpers/find-project-root.mjs";
43
43
 
44
44
  const __dirname = dirname(fileURLToPath(import.meta.url));
45
45
 
46
+ /** Resolve find-project-root from plugin install, dist layout, or consumer node_modules. */
47
+ function resolveFindProjectRootPath() {
48
+ const candidates = [];
49
+ const marker = join(__dirname, "xray-consumer-root.txt");
50
+ if (existsSync(marker)) {
51
+ try {
52
+ const marked = readFileSync(marker, "utf-8").trim();
53
+ if (marked) {
54
+ candidates.push(
55
+ join(marked, "node_modules", "0xray", "scripts", "helpers", "find-project-root.mjs"),
56
+ );
57
+ }
58
+ } catch {
59
+ /* fall through */
60
+ }
61
+ }
62
+ candidates.push(
63
+ join(__dirname, "scripts", "helpers", "find-project-root.mjs"),
64
+ join(__dirname, "..", "..", "..", "scripts", "helpers", "find-project-root.mjs"),
65
+ join(__dirname, "../../../scripts/helpers/find-project-root.mjs"),
66
+ );
67
+ for (const candidate of candidates) {
68
+ const abs = resolve(candidate);
69
+ if (existsSync(abs)) return abs;
70
+ }
71
+ throw new Error("find-project-root.mjs not found — re-run: npx 0xray hermes install --force");
72
+ }
73
+
74
+ const { findProjectRoot } = await import(pathToFileURL(resolveFindProjectRootPath()).href);
75
+
46
76
  // ── Framework components (lazy-loaded) ───────────────────────
47
77
  let ProcessorManager = null;
48
78
  let XrayStateManager = null;
@@ -389,6 +419,27 @@ async function handleHealth(input) {
389
419
  };
390
420
  }
391
421
 
422
+ async function handleSessionStart(input, projectRoot, logDir) {
423
+ const sessionId = input.sessionId || `bridge-${Date.now()}`;
424
+ let planArchive = { archived: false };
425
+
426
+ try {
427
+ const { archiveStaleLeadDevPlan } = await import('../hooks/plan-hook-runtime.mjs');
428
+ planArchive = archiveStaleLeadDevPlan(projectRoot);
429
+ if (planArchive.archived) {
430
+ logToActivity(
431
+ logDir,
432
+ `session-start: stale-plan-archived path=${planArchive.archivePath ?? 'unknown'}`,
433
+ );
434
+ }
435
+ } catch {
436
+ /* non-blocking — dist may be absent during dev */
437
+ }
438
+
439
+ logToActivity(logDir, `session-start: session=${sessionId} source=bridge`);
440
+ return { ok: true, sessionId, planArchive };
441
+ }
442
+
392
443
  async function handlePreProcess(input, projectRoot, logDir) {
393
444
  const { tool, args } = input;
394
445
  const startTime = Date.now();
@@ -766,7 +817,7 @@ async function handleApply(input, projectRoot, logDir) {
766
817
  // ── Known commands for positional-arg mode ──────────────────
767
818
  const KNOWN_COMMANDS = new Set([
768
819
  "health", "stats", "pre-process", "post-process", "validate", "codex-check", "hooks", "govern", "apply",
769
- "skill-install", "skill-registry", "delegation-gate",
820
+ "skill-install", "skill-registry", "delegation-gate", "session-start",
770
821
  ]);
771
822
 
772
823
  async function recordHermesSynthesisTurn(projectRoot, sessionId) {
@@ -792,7 +843,12 @@ async function handleDelegationGate(command, projectRoot, logDir) {
792
843
  if (!gate.isSubagentTool(tool)) {
793
844
  return { allow: true, phase: "post", skipped: true };
794
845
  }
795
- const result = gate.evaluatePostToolSpawn(tool, args, projectRoot);
846
+ const toolOutput =
847
+ command.toolOutput ?? command.result ?? command.output ?? args?.result ?? null;
848
+ const result = gate.evaluatePostToolSpawn(tool, args, projectRoot, {
849
+ sessionId,
850
+ toolOutput,
851
+ });
796
852
  if (result.satisfied.length > 0) {
797
853
  logToActivity(
798
854
  logDir,
@@ -823,7 +879,15 @@ async function handleDelegationGate(command, projectRoot, logDir) {
823
879
 
824
880
  return { phase: "pre", ...outcome };
825
881
  } catch (error) {
826
- return { error: `delegation-gate failed: ${error.message || error}` };
882
+ const message = `delegation-gate failed: ${error.message || error}`;
883
+ logToActivity(logDir, `[delegation-gate] error ${message}`);
884
+ return {
885
+ allow: false,
886
+ phase: "pre",
887
+ gate: "delegation-gate-error",
888
+ reason: message,
889
+ error: message,
890
+ };
827
891
  }
828
892
  }
829
893
 
@@ -910,10 +974,6 @@ async function main() {
910
974
  const projectRoot = resolveBridgeProjectRoot();
911
975
  const logDir = ensureLogDir(projectRoot);
912
976
 
913
- // Log session start for test verification
914
- const sessionId = `bridge-${Date.now()}`;
915
- logToActivity(logDir, `session-start: session=${sessionId} source=bridge`);
916
-
917
977
  // Lazy-load framework on first call
918
978
  if (!frameworkReady && !frameworkLoadAttempted) {
919
979
  await loadFramework(projectRoot);
@@ -956,6 +1016,9 @@ async function main() {
956
1016
  case "skill-registry":
957
1017
  response = await handleSkillRegistry(command, projectRoot, logDir);
958
1018
  break;
1019
+ case "session-start":
1020
+ response = await handleSessionStart(command, projectRoot, logDir);
1021
+ break;
959
1022
  case "delegation-gate":
960
1023
  response = await handleDelegationGate(command, projectRoot, logDir);
961
1024
  break;
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Lazy-load confer SSOT for Grok session-boot hints.
4
+ */
5
+ import { createRequire } from 'node:module';
6
+ import { existsSync } from 'node:fs';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+
12
+ function resolvePackageRoot() {
13
+ let dir = __dirname;
14
+ for (let i = 0; i < 10; i++) {
15
+ const pkg = join(dir, 'package.json');
16
+ if (existsSync(pkg)) {
17
+ try {
18
+ const require = createRequire(join(dir, 'package.json'));
19
+ if (require('./package.json').name === '0xray') return dir;
20
+ } catch {
21
+ /* continue */
22
+ }
23
+ }
24
+ const nm = join(dir, 'node_modules', '0xray', 'package.json');
25
+ if (existsSync(nm)) return dirname(nm);
26
+ const parent = dirname(dir);
27
+ if (parent === dir) break;
28
+ dir = parent;
29
+ }
30
+ return resolve(__dirname, '../../..');
31
+ }
32
+
33
+ function loadConfer() {
34
+ const root = resolvePackageRoot();
35
+ const candidates = [
36
+ join(__dirname, '../../nucleus/confer.js'),
37
+ join(root, 'dist/nucleus/confer.js'),
38
+ join(process.cwd(), 'node_modules/0xray/dist/nucleus/confer.js'),
39
+ ];
40
+ const found = candidates.find((p) => existsSync(p));
41
+ if (!found) return null;
42
+ return createRequire(import.meta.url)(found);
43
+ }
44
+
45
+ export function isConferPendingForSession(projectRoot, sessionId = null) {
46
+ const mod = loadConfer();
47
+ if (!mod?.isConferPending) return false;
48
+ return mod.isConferPending(projectRoot, sessionId);
49
+ }
@@ -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-pre-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,49 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Lazy-load user-aside SSOT for Grok session-boot hints.
4
+ */
5
+ import { createRequire } from 'node:module';
6
+ import { existsSync } from 'node:fs';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+
12
+ function resolvePackageRoot() {
13
+ let dir = __dirname;
14
+ for (let i = 0; i < 10; i++) {
15
+ const pkg = join(dir, 'package.json');
16
+ if (existsSync(pkg)) {
17
+ try {
18
+ const require = createRequire(join(dir, 'package.json'));
19
+ if (require('./package.json').name === '0xray') return dir;
20
+ } catch {
21
+ /* continue */
22
+ }
23
+ }
24
+ const nm = join(dir, 'node_modules', '0xray', 'package.json');
25
+ if (existsSync(nm)) return dirname(nm);
26
+ const parent = dirname(dir);
27
+ if (parent === dir) break;
28
+ dir = parent;
29
+ }
30
+ return resolve(__dirname, '../../..');
31
+ }
32
+
33
+ function loadUserAside() {
34
+ const root = resolvePackageRoot();
35
+ const candidates = [
36
+ join(__dirname, '../../nucleus/user-aside.js'),
37
+ join(root, 'dist/nucleus/user-aside.js'),
38
+ join(process.cwd(), 'node_modules/0xray/dist/nucleus/user-aside.js'),
39
+ ];
40
+ const found = candidates.find((p) => existsSync(p));
41
+ if (!found) return null;
42
+ return createRequire(import.meta.url)(found);
43
+ }
44
+
45
+ export function getActiveUserAsideBoot(projectRoot, sessionId = null) {
46
+ const mod = loadUserAside();
47
+ if (!mod?.getUserAsideBootHints) return null;
48
+ return mod.getUserAsideBootHints(projectRoot, sessionId);
49
+ }
@@ -193,13 +193,18 @@ class XrayArchitectToolsServer extends XrayKnowledgeSkillBase {
193
193
  async architectureAssessment(args) {
194
194
  const projectRoot = args?.projectRoot || "";
195
195
  const assessmentType = args?.assessmentType || "comprehensive";
196
+ const conferPrompt = typeof args?.conferPrompt === "string" ? args.conferPrompt : "";
196
197
  frameworkLogger.log("mcps/architect-tools", "architecture-assessment", "info", { projectRoot });
197
198
  const result = await architectArchitectureAssessment(projectRoot, assessmentType);
199
+ const assessmentJson = JSON.stringify(result, null, 2);
200
+ const text = conferPrompt.trim()
201
+ ? `${conferPrompt.trim()}\n\n## Architecture assessment\n${assessmentJson}\n\nVerdict: CONDITIONAL\nTop risks: review assessment metrics above\nHardening: address high-complexity or coupling findings before resuming`
202
+ : assessmentJson;
198
203
  return {
199
204
  content: [
200
205
  {
201
206
  type: "text",
202
- text: JSON.stringify(result, null, 2),
207
+ text,
203
208
  },
204
209
  ],
205
210
  };
@@ -29,6 +29,13 @@ export declare class ComplexityHandler {
29
29
  sessionId?: string;
30
30
  synthesisCheckpoint?: boolean;
31
31
  synthesisDueReason?: string | null;
32
+ collocatedText?: string | null;
33
+ conferFixture?: boolean;
34
+ userAsideId?: string;
35
+ userAsideTitle?: string;
36
+ worktree?: string;
37
+ branch?: string;
38
+ setActiveAside?: boolean;
32
39
  }, asideId?: string): Promise<{
33
40
  content: Array<{
34
41
  type: string;