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.
- package/.xray/features.json +5 -1
- package/.xray/features.schema.json +56 -0
- package/AGENTS-consumer.md +1 -1
- package/AGENTS.md +11 -1
- package/README.md +1 -1
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +11 -1
- package/dist/CHANGELOG.md +44 -0
- package/dist/README.md +1 -1
- package/dist/SKILLS.md +1 -1
- package/dist/cli/commands/hermes-install.js +3 -1
- package/dist/cli/index.js +0 -0
- package/dist/core/features-config.d.ts +15 -0
- package/dist/governance/governance-service.d.ts +1 -0
- package/dist/governance/governance-service.js +81 -6
- package/dist/integrations/enforcement-gate.js +15 -5
- package/dist/integrations/grok/grok-cli.js +1 -10
- package/dist/integrations/grok/hooks/grok-hook-utils.js +46 -2
- package/dist/integrations/grok/hooks/post-tool-use.js +21 -0
- package/dist/integrations/grok/hooks/pre-tool-use.js +20 -0
- package/dist/integrations/grok/hooks/session-start.js +28 -0
- package/dist/integrations/hermes-agent/__init__.py +11 -1
- package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
- package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
- package/dist/integrations/hermes-agent/bridge.mjs +73 -10
- package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
- package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
- package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
- package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
- package/dist/mcps/architect-tools.server.js +6 -1
- package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
- package/dist/mcps/orchestrator/handlers/complexity-handler.js +106 -7
- package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
- package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
- package/dist/mcps/orchestrator/server.js +54 -5
- package/dist/memory-routing/provider-loader.js +30 -2
- package/dist/nucleus/aside-worktree.d.ts +41 -0
- package/dist/nucleus/aside-worktree.js +164 -0
- package/dist/nucleus/confer.d.ts +57 -0
- package/dist/nucleus/confer.js +308 -0
- package/dist/nucleus/delegation-gate.d.ts +4 -0
- package/dist/nucleus/delegation-gate.js +90 -11
- package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
- package/dist/nucleus/lead-dev-plan-persistence.js +201 -19
- package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
- package/dist/nucleus/spawn-plan-resolution.js +42 -0
- package/dist/nucleus/synthesis-consult-receipt.js +18 -2
- package/dist/nucleus/synthesis-context.js +1 -1
- package/dist/nucleus/user-aside.d.ts +70 -0
- package/dist/nucleus/user-aside.js +393 -0
- package/dist/postprocessor/PostProcessor.d.ts +1 -0
- package/dist/postprocessor/PostProcessor.js +11 -0
- package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
- package/dist/processors/implementations/session-summary-processor.js +9 -6
- package/package.json +14 -5
- package/scripts/hooks/install-hooks.cjs +3 -3
- package/scripts/hooks/run-hook.js +171 -86
- package/scripts/mjs/verify-confer-core.mjs +103 -0
- package/scripts/mjs/verify-delegation-gate-core.mjs +48 -2
- package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
- package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
- package/scripts/mjs/verify-user-aside-core.mjs +137 -0
- package/scripts/node/bridge-mcp-wiring.cjs +10 -0
- package/scripts/node/install-bridges.cjs +3 -11
- package/scripts/node/load-reflection-config.mjs +2 -2
- package/scripts/node/release-gate.mjs +8 -4
- package/src/mcps/architect-tools.server.ts +6 -1
- package/src/mcps/orchestrator/handlers/complexity-handler.ts +178 -20
- package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
- package/src/mcps/orchestrator/server.ts +56 -5
- package/xray/features.json +5 -1
- 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
|
-
|
|
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, "
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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;
|