0xray 3.3.0 → 3.4.1
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/.opencode/codex.codex +1 -1
- package/.opencode/enforcer-config.json +1 -4
- package/dist/CHANGELOG.md +22 -0
- package/dist/analytics/routing-refiner.js +0 -2
- package/dist/core/boot-orchestrator.js +9 -1
- package/dist/core/features-config.js +0 -1
- package/dist/integrations/grok/grok-cli.js +53 -25
- package/dist/integrations/hermes-agent/bridge.mjs +11 -1
- package/dist/mcps/index.js +1 -0
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
- package/dist/mcps/knowledge-skills/storyteller.server.js +167 -0
- package/dist/mcps/orchestrator/execution/execution-planner.js +14 -2
- package/dist/mcps/registry.json +0 -1
- package/dist/mcps/researcher-confidence.js +92 -0
- package/dist/mcps/researcher.server.js +19 -3
- package/dist/memory-routing/null-provider.js +12 -0
- package/dist/orchestrator/universal-registry-bridge.js +1 -1
- package/dist/skills/registry.json +0 -1
- package/package.json +1 -1
- package/scripts/node/enforce-agents-md.mjs +0 -26
- package/scripts/node/enforce-version-compliance.sh +0 -1
- package/scripts/node/enforce-version-compliance.ts +0 -10
- package/scripts/node/install-bridges.cjs +466 -0
- package/scripts/node/postinstall.cjs +24 -83
- package/scripts/node/pre-publish-guard.js +20 -16
- package/scripts/node/setup.cjs +11 -10
- package/scripts/node/universal-version-manager.js +17 -3
- package/src/mcps/index.ts +1 -0
- package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
- package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
- package/src/mcps/orchestrator/execution/execution-planner.ts +14 -2
- package/src/mcps/orchestrator/types.ts +4 -0
- package/src/mcps/registry.json +0 -1
- package/src/mcps/researcher-confidence.ts +144 -0
- package/src/mcps/researcher.server.ts +28 -3
- package/src/opencode/codex.codex +1 -1
- package/src/opencode/enforcer-config.json +1 -4
- package/src/skills/registry.json +0 -1
package/.opencode/codex.codex
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.3.
|
|
2
|
+
"version": "3.3.1",
|
|
3
3
|
"terms": [
|
|
4
4
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60
|
|
5
5
|
],
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"version": "3.1.0",
|
|
4
|
-
"description": "Codex-compliant framework configuration for Credible UI project",
|
|
2
|
+
"description": "Codex-compliant framework configuration",
|
|
5
3
|
"thresholds": {
|
|
6
4
|
"bundleSize": {
|
|
7
5
|
"maxSize": "3MB",
|
|
@@ -220,7 +218,6 @@
|
|
|
220
218
|
}
|
|
221
219
|
},
|
|
222
220
|
"codex": {
|
|
223
|
-
"version": "3.1.0",
|
|
224
221
|
"terms": [
|
|
225
222
|
1,
|
|
226
223
|
2,
|
package/dist/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Conventional Commits](https://www.conventionalcommits.org/).
|
|
6
6
|
|
|
7
|
+
## [3.4.1] - 2026-06-17
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
- Unified 4-platform bridge installer (`install-bridges.cjs`) runs on consumer `npm i 0xray` postinstall — OpenCode, Grok, Hermes, OpenClaw
|
|
11
|
+
- All 7 MCP servers registered via `npx -y 0xray mcp <cmd>` (no brittle `dist/` paths)
|
|
12
|
+
- `npx 0xray grok install` aligned with postinstall: 7 servers via npx, idempotent skill/MCP sync on re-run
|
|
13
|
+
- Grok skills synced to both `~/.grok/plugins/0xray/skills/` and `~/.grok/skills/` (Grok Build / Cursor)
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug Fixes
|
|
16
|
+
- `setup.cjs` consumer detection uses `isConsumerInstall()` — works for both `node_modules/0xray` and `node_modules/xray`
|
|
17
|
+
- Hermes bridge honors `XRAY_ROOT` env and `xray-consumer-root.txt` marker for correct project root resolution
|
|
18
|
+
|
|
19
|
+
## [3.3.0] - 2026-06-18
|
|
20
|
+
|
|
21
|
+
### 🔄 Changes
|
|
22
|
+
|
|
23
|
+
### 🔎 Other Changes
|
|
24
|
+
- Wire orchestrator confidence gate into execution planning (d7910b610)
|
|
25
|
+
- tweet: v3.3.0 release tweet — Pluggable Memory Routing (7fb6c9f70)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
7
29
|
## [3.2.0] - 2026-06-17
|
|
8
30
|
|
|
9
31
|
### 🔄 Changes
|
|
@@ -35,7 +35,6 @@ class RoutingRefiner {
|
|
|
35
35
|
const optimizations = this.suggestMappingOptimizations(promptAnalysis, performanceReport);
|
|
36
36
|
const warnings = this.generateWarnings(newMappings, optimizations);
|
|
37
37
|
return {
|
|
38
|
-
version: "3.1.0",
|
|
39
38
|
generatedAt: new Date(),
|
|
40
39
|
summary: {
|
|
41
40
|
newMappings: newMappings.length,
|
|
@@ -247,7 +246,6 @@ class RoutingRefiner {
|
|
|
247
246
|
lines.push("╚════════════════════════════════════════════════════════════╝");
|
|
248
247
|
lines.push("");
|
|
249
248
|
lines.push(`Generated: ${report.configurationUpdate.generatedAt.toISOString()}`);
|
|
250
|
-
lines.push(`Version: ${report.configurationUpdate.version}`);
|
|
251
249
|
lines.push("");
|
|
252
250
|
lines.push("=== Summary ===");
|
|
253
251
|
lines.push(`New Mappings: ${report.configurationUpdate.summary.newMappings}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Boot Orchestrator (min documented consumer runtime compat shim from prior 0xRay releases; primary Xray paths + .xray fallbacks only; 1-line per Scope Rule).
|
|
3
3
|
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
4
5
|
import { XrayContextLoader } from "./context-loader.js";
|
|
5
6
|
import { XrayStateManager } from "../state/state-manager.js";
|
|
6
7
|
import { ProcessorManager } from "../processors/processor-manager.js";
|
|
@@ -721,7 +722,14 @@ export class BootOrchestrator {
|
|
|
721
722
|
try {
|
|
722
723
|
// Consumer runtime compat shim from prior 0xRay releases (1-line min per Scope Rule; primary xray paths + .xray fallbacks)
|
|
723
724
|
const xrayConfig = {
|
|
724
|
-
version:
|
|
725
|
+
version: (() => {
|
|
726
|
+
try {
|
|
727
|
+
return JSON.parse(readFileSync(path.join(process.cwd(), "package.json"), "utf-8")).version;
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
return "unknown";
|
|
731
|
+
}
|
|
732
|
+
})(),
|
|
725
733
|
codex_enabled: true,
|
|
726
734
|
codex_version: "v1.7.5",
|
|
727
735
|
codex_terms: [
|
|
@@ -18,9 +18,44 @@ import { frameworkLogger } from '../../core/framework-logger.js';
|
|
|
18
18
|
import fs from 'fs';
|
|
19
19
|
import path from 'path';
|
|
20
20
|
import { execSync } from 'child_process';
|
|
21
|
+
import { createRequire } from 'module';
|
|
21
22
|
import { syncBuiltinSkills } from '../../cli/commands/skill-install.js';
|
|
22
23
|
// ESM-compatible __dirname (this file is compiled to ESM)
|
|
23
24
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
25
|
+
const packageRoot = path.resolve(__dirname, '..', '..', '..');
|
|
26
|
+
const requireCjs = createRequire(import.meta.url);
|
|
27
|
+
const { resolveConsumerTargetDir } = requireCjs(path.join(packageRoot, 'scripts/node/install-bridges.cjs'));
|
|
28
|
+
/** Canonical 7-server MCP surface — matches install-bridges.cjs and package .mcp.json */
|
|
29
|
+
const XRAY_MCP_SERVERS = [
|
|
30
|
+
{ name: 'xray-governance', mcpCmd: 'governance', env: { XRAY_FORCE_MCP_GOVERNANCE: 'true' } },
|
|
31
|
+
{ name: 'xray-skills', mcpCmd: 'skills', env: {} },
|
|
32
|
+
{ name: 'xray-orchestrator', mcpCmd: 'orchestrator', env: {} },
|
|
33
|
+
{ name: 'xray-enforcer', mcpCmd: 'enforcer', env: {} },
|
|
34
|
+
{ name: 'xray-researcher', mcpCmd: 'researcher', env: {} },
|
|
35
|
+
{ name: 'xray-code-review', mcpCmd: 'code-review', env: {} },
|
|
36
|
+
{ name: 'xray-architect-tools', mcpCmd: 'architect-tools', env: {} },
|
|
37
|
+
];
|
|
38
|
+
function registerGrokMcpServers(targetDir) {
|
|
39
|
+
try {
|
|
40
|
+
execSync('which grok', { stdio: 'ignore' });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
console.log('[Grok] grok CLI not on PATH — plugin .mcp.json still configured');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const s of XRAY_MCP_SERVERS) {
|
|
47
|
+
try {
|
|
48
|
+
const envEntries = { ...s.env, XRAY_ROOT: targetDir };
|
|
49
|
+
const envFlags = Object.entries(envEntries)
|
|
50
|
+
.map(([k, v]) => `--env "${k}=${v}"`)
|
|
51
|
+
.join(' ');
|
|
52
|
+
execSync(`grok mcp add ${s.name} --command npx --args "-y" "0xray" "mcp" "${s.mcpCmd}" ${envFlags}`, { stdio: 'pipe' });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// already registered or grok config conflict — non-blocking
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
24
59
|
export async function installForGrokCLI(options = {}) {
|
|
25
60
|
frameworkLogger.log('grok-integration', 'install-start', 'info', { options });
|
|
26
61
|
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
@@ -39,15 +74,18 @@ export async function installForGrokCLI(options = {}) {
|
|
|
39
74
|
console.log(`[Grok] Dry run: Would copy plugin from ${sourceDir} → ${targetPluginDir}`);
|
|
40
75
|
return;
|
|
41
76
|
}
|
|
77
|
+
const targetDir = resolveConsumerTargetDir(packageRoot, process.cwd());
|
|
42
78
|
try {
|
|
43
|
-
|
|
79
|
+
const pluginExists = fs.existsSync(targetPluginDir);
|
|
80
|
+
if (pluginExists && !options.force) {
|
|
44
81
|
console.log('[Grok] 0xray Grok plugin is already installed.');
|
|
45
|
-
console.log('Use --force to reinstall.');
|
|
46
|
-
|
|
82
|
+
console.log('Use --force to reinstall plugin files.');
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
fs.cpSync(sourceDir, targetPluginDir, { recursive: true, force: true });
|
|
86
|
+
frameworkLogger.log('grok-integration', 'plugin-copied', 'info', { destination: targetPluginDir });
|
|
87
|
+
console.log(`\x1b[32m✓ Copied Grok plugin to ${targetPluginDir}\x1b[0m`);
|
|
47
88
|
}
|
|
48
|
-
fs.cpSync(sourceDir, targetPluginDir, { recursive: true, force: true });
|
|
49
|
-
frameworkLogger.log('grok-integration', 'plugin-copied', 'info', { destination: targetPluginDir });
|
|
50
|
-
console.log(`\x1b[32m✓ Copied Grok plugin to ${targetPluginDir}\x1b[0m`);
|
|
51
89
|
// Sync builtin skills to Grok plugin skills dir
|
|
52
90
|
const grokSkillsDir = path.join(targetPluginDir, 'skills');
|
|
53
91
|
const skillsCopied = syncBuiltinSkills(grokSkillsDir);
|
|
@@ -55,6 +93,12 @@ export async function installForGrokCLI(options = {}) {
|
|
|
55
93
|
console.log(`\x1b[32m✓ Synced ${skillsCopied} builtin skills to Grok plugin\x1b[0m`);
|
|
56
94
|
}
|
|
57
95
|
frameworkLogger.log('grok-integration', 'skills-synced', 'info', { count: skillsCopied });
|
|
96
|
+
// Grok Build / Cursor also reads ~/.grok/skills/ for agent_skills
|
|
97
|
+
const globalSkillsDir = path.join(home, '.grok', 'skills');
|
|
98
|
+
const globalCopied = syncBuiltinSkills(globalSkillsDir);
|
|
99
|
+
if (globalCopied > 0) {
|
|
100
|
+
console.log(`\x1b[32m✓ Synced ${globalCopied} builtin skills to ~/.grok/skills/\x1b[0m`);
|
|
101
|
+
}
|
|
58
102
|
// Attempt auto-trust (best effort)
|
|
59
103
|
try {
|
|
60
104
|
execSync(`grok plugins trust "${targetPluginDir}"`, { stdio: 'ignore' });
|
|
@@ -64,25 +108,9 @@ export async function installForGrokCLI(options = {}) {
|
|
|
64
108
|
console.log('\nPlease run this command to fully trust the plugin:');
|
|
65
109
|
console.log(` grok plugins trust "${targetPluginDir}"`);
|
|
66
110
|
}
|
|
67
|
-
// Register MCP servers via grok mcp add (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const orchMcpPath = path.resolve(__dirname, '..', '..', '..', 'dist/mcps/orchestrator.server.js');
|
|
71
|
-
const enforcerMcpPath = path.resolve(__dirname, '..', '..', '..', 'dist/mcps/enforcer-tools.server.js');
|
|
72
|
-
try {
|
|
73
|
-
execSync(`grok mcp add xray-governance --command node --args "${govMcpPath}" --env "XRAY_FORCE_MCP_GOVERNANCE=true" --env "XRAY_ROOT=${process.cwd()}"`, { stdio: 'pipe' });
|
|
74
|
-
execSync(`grok mcp add xray-skills --command node --args "${skillsMcpPath}" --env "XRAY_ROOT=${process.cwd()}"`, { stdio: 'pipe' });
|
|
75
|
-
execSync(`grok mcp add xray-orchestrator --command node --args "${orchMcpPath}" --env "XRAY_ROOT=${process.cwd()}"`, { stdio: 'pipe' });
|
|
76
|
-
execSync(`grok mcp add xray-enforcer --command node --args "${enforcerMcpPath}" --env "XRAY_ROOT=${process.cwd()}"`, { stdio: 'pipe' });
|
|
77
|
-
console.log('\x1b[32m✓ Registered xray MCP servers with Grok CLI\x1b[0m');
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
console.log('\nCould not auto-register MCP servers. Run manually:');
|
|
81
|
-
console.log(` grok mcp add xray-governance --command node --args "${govMcpPath}"`);
|
|
82
|
-
console.log(` grok mcp add xray-skills --command node --args "${skillsMcpPath}"`);
|
|
83
|
-
console.log(` grok mcp add xray-orchestrator --command node --args "${orchMcpPath}"`);
|
|
84
|
-
console.log(` grok mcp add xray-enforcer --command node --args "${enforcerMcpPath}"`);
|
|
85
|
-
}
|
|
111
|
+
// Register MCP servers via grok mcp add (npx -y 0xray mcp — matches install-bridges)
|
|
112
|
+
registerGrokMcpServers(targetDir);
|
|
113
|
+
console.log('\x1b[32m✓ Registered 7 xray MCP servers with Grok CLI (npx)\x1b[0m');
|
|
86
114
|
console.log('\n✅ 0xRay is now installed as a first-class Grok CLI plugin!');
|
|
87
115
|
console.log('Restart Grok or run `grok` to load the new hooks and MCP servers.');
|
|
88
116
|
}
|
|
@@ -53,9 +53,19 @@ let frameworkLoadAttempted = false;
|
|
|
53
53
|
|
|
54
54
|
// ── Project root detection ───────────────────────────────────
|
|
55
55
|
function findProjectRoot() {
|
|
56
|
-
const envHome = process.env.XRAY_HOME;
|
|
56
|
+
const envHome = process.env.XRAY_HOME || process.env.XRAY_ROOT;
|
|
57
57
|
if (envHome && existsSync(join(envHome, "package.json"))) return envHome;
|
|
58
58
|
|
|
59
|
+
const consumerMarker = join(__dirname, "xray-consumer-root.txt");
|
|
60
|
+
if (existsSync(consumerMarker)) {
|
|
61
|
+
try {
|
|
62
|
+
const marked = readFileSync(consumerMarker, "utf-8").trim();
|
|
63
|
+
if (marked && existsSync(join(marked, "package.json"))) return marked;
|
|
64
|
+
} catch {
|
|
65
|
+
// fall through
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
59
69
|
const candidates = [
|
|
60
70
|
process.cwd(),
|
|
61
71
|
join(homedir(), "dev", "xray"),
|
package/dist/mcps/index.js
CHANGED
|
@@ -40,6 +40,7 @@ export const MCP_SERVERS = [
|
|
|
40
40
|
{ serverName: "refactoring-strategies", sourceFile: "mcps/knowledge-skills/refactoring-strategies.server.ts", type: "knowledge-skill", registered: true, description: "Refactoring strategies" },
|
|
41
41
|
{ serverName: "security-audit", sourceFile: "mcps/knowledge-skills/security-audit.server.ts", type: "knowledge-skill", registered: true, description: "Security auditing" },
|
|
42
42
|
{ serverName: "seo-consultant", sourceFile: "mcps/knowledge-skills/seo-consultant.server.ts", type: "knowledge-skill", registered: false, description: "SEO consulting" },
|
|
43
|
+
{ serverName: "storyteller", sourceFile: "mcps/knowledge-skills/storyteller.server.ts", type: "knowledge-skill", registered: false, description: "Reflections, sagas, and technical narratives" },
|
|
43
44
|
{ serverName: "session-management", sourceFile: "mcps/knowledge-skills/session-management.server.ts", type: "knowledge-skill", registered: true, description: "Session management" },
|
|
44
45
|
{ serverName: "skill-invocation", sourceFile: "mcps/knowledge-skills/skill-invocation.server.ts", type: "knowledge-skill", registered: true, description: "Skill invocation" },
|
|
45
46
|
{ serverName: "strategist", sourceFile: "mcps/knowledge-skills/strategist.server.ts", type: "knowledge-skill", registered: false, description: "Strategic planning" },
|
|
@@ -87,7 +87,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
|
|
|
87
87
|
"strategist", "skill-invocation",
|
|
88
88
|
];
|
|
89
89
|
const knowledgeSkills = [
|
|
90
|
-
"architecture-patterns", "strategist", "tech-writer",
|
|
90
|
+
"architecture-patterns", "strategist", "tech-writer", "storyteller",
|
|
91
91
|
"seo-consultant", "content-creator", "growth-strategist",
|
|
92
92
|
"bug-triage-specialist", "log-monitor",
|
|
93
93
|
"mobile-development", "git-workflow", "session-management",
|
|
@@ -132,6 +132,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
|
|
|
132
132
|
"tech-writer": "documentation-generation",
|
|
133
133
|
"frontend-ui-ux-engineer": "ui-ux-design",
|
|
134
134
|
"frontend-engineer": "project-analysis",
|
|
135
|
+
"storyteller": "storyteller",
|
|
135
136
|
};
|
|
136
137
|
const resolvedSkill = skillAliases[skillName] || skillName;
|
|
137
138
|
const availableServers = [
|
|
@@ -147,7 +148,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
|
|
|
147
148
|
"git-workflow", "content-creator", "ui-ux-design",
|
|
148
149
|
"multimodal-looker", "refactoring-strategies",
|
|
149
150
|
"project-analysis", "testing-best-practices",
|
|
150
|
-
"architecture-patterns",
|
|
151
|
+
"architecture-patterns", "storyteller",
|
|
151
152
|
];
|
|
152
153
|
if (!availableServers.includes(resolvedSkill)) {
|
|
153
154
|
return {
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storyteller MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Returns storyteller SKILL.md guidance plus a session-aware writing prompt.
|
|
5
|
+
* The host LLM writes the story; this server supplies the contract.
|
|
6
|
+
*/
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { XrayKnowledgeSkillBase } from '../shared/knowledge-skill-base.js';
|
|
12
|
+
const STORY_TYPES = ['reflection', 'saga', 'journey', 'narrative'];
|
|
13
|
+
function resolveSkillMd() {
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const candidates = [
|
|
16
|
+
join(here, '../../../skills/storyteller/SKILL.md'),
|
|
17
|
+
join(here, '../../skills/storyteller/SKILL.md'),
|
|
18
|
+
join(process.cwd(), 'node_modules/0xray/dist/skills/storyteller/SKILL.md'),
|
|
19
|
+
join(process.cwd(), 'node_modules/xray/dist/skills/storyteller/SKILL.md'),
|
|
20
|
+
];
|
|
21
|
+
for (const path of candidates) {
|
|
22
|
+
if (existsSync(path)) {
|
|
23
|
+
return readFileSync(path, 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return '# Storyteller\n\nWrite warmly candid technical reflections with scene-first openings.';
|
|
27
|
+
}
|
|
28
|
+
function getStoryMeta(storyType) {
|
|
29
|
+
const defaults = {
|
|
30
|
+
reflection: {
|
|
31
|
+
location: 'docs/reflections/',
|
|
32
|
+
min_words: 2000,
|
|
33
|
+
ideal_words: 5000,
|
|
34
|
+
framework: 'three_act_structure',
|
|
35
|
+
},
|
|
36
|
+
saga: {
|
|
37
|
+
location: 'docs/reflections/deep/',
|
|
38
|
+
min_words: 5000,
|
|
39
|
+
ideal_words: 15000,
|
|
40
|
+
framework: 'hero_journey',
|
|
41
|
+
},
|
|
42
|
+
journey: {
|
|
43
|
+
location: 'docs/reflections/deep/',
|
|
44
|
+
min_words: 1500,
|
|
45
|
+
ideal_words: 4000,
|
|
46
|
+
framework: 'three_act_structure',
|
|
47
|
+
},
|
|
48
|
+
narrative: {
|
|
49
|
+
location: 'docs/reflections/',
|
|
50
|
+
min_words: 1000,
|
|
51
|
+
ideal_words: 3000,
|
|
52
|
+
framework: 'three_act_structure',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
return defaults[storyType];
|
|
56
|
+
}
|
|
57
|
+
function gitContext() {
|
|
58
|
+
try {
|
|
59
|
+
const commits = execSync('git log --oneline -20 2>/dev/null', { encoding: 'utf-8' });
|
|
60
|
+
const files = execSync('git diff --name-only HEAD~10 2>/dev/null || git ls-files 2>/dev/null | head -50', { encoding: 'utf-8' });
|
|
61
|
+
return `Recent commits:\n${commits}\n\nRecent files:\n${files}`;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return 'No git context available in this workspace.';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function buildPrompt(storyType, title, meta, extraContext) {
|
|
68
|
+
const contextBlock = extraContext
|
|
69
|
+
? `\n## Provided context\n${JSON.stringify(extraContext, null, 2)}\n`
|
|
70
|
+
: '';
|
|
71
|
+
return `Write a ${storyType} titled "${title}".
|
|
72
|
+
|
|
73
|
+
## Requirements
|
|
74
|
+
- Framework: ${meta.framework}
|
|
75
|
+
- Target length: ${meta.ideal_words} words (minimum: ${meta.min_words})
|
|
76
|
+
- Save to: ${meta.location}
|
|
77
|
+
- Follow storyteller SKILL.md voice (warmly candid, scene-first, no executive summary)
|
|
78
|
+
- Include Key Takeaways and What Next sections
|
|
79
|
+
|
|
80
|
+
## Session / repo context
|
|
81
|
+
${gitContext()}
|
|
82
|
+
${contextBlock}
|
|
83
|
+
|
|
84
|
+
Write the complete ${storyType} now.`;
|
|
85
|
+
}
|
|
86
|
+
function writeStoryTool(storyType, args) {
|
|
87
|
+
const meta = getStoryMeta(storyType);
|
|
88
|
+
if (args.framework)
|
|
89
|
+
meta.framework = args.framework;
|
|
90
|
+
const title = args.title ??
|
|
91
|
+
`${storyType.charAt(0).toUpperCase() + storyType.slice(1)} - ${new Date().toISOString().split('T')[0]}`;
|
|
92
|
+
const skillMd = resolveSkillMd();
|
|
93
|
+
const prompt = buildPrompt(storyType, title, meta, args.context);
|
|
94
|
+
const slug = title
|
|
95
|
+
.toLowerCase()
|
|
96
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
97
|
+
.replace(/^-|-$/g, '');
|
|
98
|
+
const filename = `${storyType}-${slug}-${new Date().toISOString().split('T')[0]}.md`;
|
|
99
|
+
const outputPath = args.output ?? join(meta.location, filename);
|
|
100
|
+
const outputDir = join(process.cwd(), dirname(outputPath));
|
|
101
|
+
if (!existsSync(outputDir))
|
|
102
|
+
mkdirSync(outputDir, { recursive: true });
|
|
103
|
+
const fullPath = join(process.cwd(), outputPath);
|
|
104
|
+
const frontmatter = `---
|
|
105
|
+
story_type: ${storyType}
|
|
106
|
+
title: "${title}"
|
|
107
|
+
framework: ${meta.framework}
|
|
108
|
+
target_words: ${meta.ideal_words}
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
`;
|
|
112
|
+
writeFileSync(fullPath, `${frontmatter}\n${prompt}\n`, 'utf-8');
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: 'text',
|
|
117
|
+
text: [
|
|
118
|
+
`Storyteller ${storyType} brief prepared.`,
|
|
119
|
+
`Output template: ${outputPath}`,
|
|
120
|
+
'',
|
|
121
|
+
'--- SKILL (excerpt) ---',
|
|
122
|
+
skillMd.slice(0, 4000),
|
|
123
|
+
skillMd.length > 4000 ? '\n...(truncated; full SKILL.md on disk)' : '',
|
|
124
|
+
'',
|
|
125
|
+
'--- WRITING PROMPT ---',
|
|
126
|
+
prompt,
|
|
127
|
+
'',
|
|
128
|
+
'Host LLM: write the full story into the output path using the prompt above.',
|
|
129
|
+
].join('\n'),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
class StorytellerServer extends XrayKnowledgeSkillBase {
|
|
135
|
+
constructor() {
|
|
136
|
+
super('storyteller', '1.0.0');
|
|
137
|
+
const writeSchema = {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
title: { type: 'string', description: 'Story title' },
|
|
141
|
+
context: { type: 'object', description: 'Session context (commits, changes, notes)' },
|
|
142
|
+
framework: { type: 'string', description: 'Storytelling framework override' },
|
|
143
|
+
output: { type: 'string', description: 'Relative output path' },
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
this.tools = STORY_TYPES.map((storyType) => ({
|
|
147
|
+
name: `write_${storyType}`,
|
|
148
|
+
description: `Prepare ${storyType} story brief using storyteller SKILL.md`,
|
|
149
|
+
inputSchema: writeSchema,
|
|
150
|
+
}));
|
|
151
|
+
this.handlers = {
|
|
152
|
+
write_reflection: async (args) => writeStoryTool('reflection', args),
|
|
153
|
+
write_saga: async (args) => writeStoryTool('saga', args),
|
|
154
|
+
write_journey: async (args) => writeStoryTool('journey', args),
|
|
155
|
+
write_narrative: async (args) => writeStoryTool('narrative', args),
|
|
156
|
+
};
|
|
157
|
+
this.setupToolHandlers();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (import.meta.url === `file://${fileURLToPath(import.meta.url)}`) {
|
|
161
|
+
const server = new StorytellerServer();
|
|
162
|
+
server.run().catch((error) => {
|
|
163
|
+
process.stderr.write(`FATAL: ${String(error)}\n`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
export { StorytellerServer };
|
|
@@ -159,12 +159,15 @@ export class ExecutionPlanner {
|
|
|
159
159
|
*/
|
|
160
160
|
assignTasksToAgents(tasks, agentAssignments) {
|
|
161
161
|
for (const task of tasks) {
|
|
162
|
-
const complexity = task
|
|
162
|
+
const complexity = this.calculateTaskComplexity(task);
|
|
163
163
|
const requiredCaps = [
|
|
164
164
|
task.type,
|
|
165
165
|
...(task.metadata?.memorySignals ?? []),
|
|
166
166
|
];
|
|
167
|
-
const
|
|
167
|
+
const operationDescription = task.metadata?.memoryHighConfidenceTrap
|
|
168
|
+
? `${task.description} TYPE: ontological-trap`
|
|
169
|
+
: task.description;
|
|
170
|
+
const agent = this.capabilitiesManager.selectAgentForTask(requiredCaps, complexity, operationDescription) || 'orchestrator';
|
|
168
171
|
if (!agentAssignments.has(agent)) {
|
|
169
172
|
agentAssignments.set(agent, []);
|
|
170
173
|
}
|
|
@@ -264,6 +267,15 @@ export class ExecutionPlanner {
|
|
|
264
267
|
if (task.dependencies) {
|
|
265
268
|
complexity += task.dependencies.length * 5;
|
|
266
269
|
}
|
|
270
|
+
if (task.metadata?.memoryComplexityBoost !== undefined) {
|
|
271
|
+
complexity += task.metadata.memoryComplexityBoost;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
const provider = getProvider();
|
|
275
|
+
if (provider.id !== 'null' && provider.getTaskConfidence) {
|
|
276
|
+
complexity += provider.getTaskConfidence(toMemoryTask(task)).complexityBoost;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
267
279
|
return Math.min(Math.max(Math.round(complexity), 1), 100);
|
|
268
280
|
}
|
|
269
281
|
/**
|
package/dist/mcps/registry.json
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { frameworkLogger } from '../core/framework-logger.js';
|
|
2
|
+
import { getMemoryRoutingProviderSync } from '../memory-routing/index.js';
|
|
3
|
+
const TRAP_LANGUAGE = /TYPE:\s*ontological-trap|ontological[- ]trap/i;
|
|
4
|
+
const DEFAULT_MIN_CONFIDENCE_GATE = 0.55;
|
|
5
|
+
export function shouldQueryRepertoireConfidence(description, proposalType = '') {
|
|
6
|
+
const text = `${description} ${proposalType}`.trim();
|
|
7
|
+
return TRAP_LANGUAGE.test(text);
|
|
8
|
+
}
|
|
9
|
+
export function hasHighConfidencePrimitiveMatch(routingContext, minConfidence = DEFAULT_MIN_CONFIDENCE_GATE) {
|
|
10
|
+
const confidences = routingContext.signalConfidences ?? {};
|
|
11
|
+
return Object.values(confidences).some((confidence) => confidence >= minConfidence);
|
|
12
|
+
}
|
|
13
|
+
function buildTaskText(proposalTitle, proposalDescription, proposalType) {
|
|
14
|
+
return [proposalTitle, proposalDescription, proposalType].filter(Boolean).join('\n');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve Repertoire task confidence for researcher proposal analysis.
|
|
18
|
+
* Triggered by ontological-trap language or high-confidence primitive matches.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveResearcherMemoryContext(input) {
|
|
21
|
+
const proposalTitle = input.proposalTitle ?? '';
|
|
22
|
+
const proposalDescription = input.proposalDescription ?? '';
|
|
23
|
+
const proposalType = input.proposalType ?? '';
|
|
24
|
+
const taskText = buildTaskText(proposalTitle, proposalDescription, proposalType);
|
|
25
|
+
const provider = getMemoryRoutingProviderSync();
|
|
26
|
+
if (provider.id === 'null' || !provider.getTaskConfidence) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const trapLanguageDetected = shouldQueryRepertoireConfidence(proposalDescription, proposalType);
|
|
30
|
+
const routingContext = provider.buildRoutingContext(taskText);
|
|
31
|
+
const primitiveMatchDetected = hasHighConfidencePrimitiveMatch(routingContext);
|
|
32
|
+
if (!trapLanguageDetected && !primitiveMatchDetected) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const confidence = provider.getTaskConfidence({
|
|
36
|
+
id: 'researcher-proposal',
|
|
37
|
+
description: taskText,
|
|
38
|
+
type: proposalType || 'governance',
|
|
39
|
+
});
|
|
40
|
+
const matchedSignals = confidence.matchedSignals.length > 0
|
|
41
|
+
? confidence.matchedSignals
|
|
42
|
+
: routingContext.matchedSignals;
|
|
43
|
+
const context = {
|
|
44
|
+
providerId: provider.id,
|
|
45
|
+
confidence,
|
|
46
|
+
matchedSignals,
|
|
47
|
+
recommendedAgent: confidence.recommendedAgent,
|
|
48
|
+
triggeredBy: trapLanguageDetected ? 'trap-language' : 'high-confidence-primitives',
|
|
49
|
+
};
|
|
50
|
+
if (confidence.highConfidenceTrapPresent) {
|
|
51
|
+
frameworkLogger.log('mcps/researcher', 'repertoire-trap-detected', 'info', {
|
|
52
|
+
providerId: provider.id,
|
|
53
|
+
matchedSignals,
|
|
54
|
+
complexityBoost: confidence.complexityBoost,
|
|
55
|
+
recommendedAgent: confidence.recommendedAgent,
|
|
56
|
+
triggeredBy: context.triggeredBy,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
61
|
+
export function buildMemoryRoutingEvidence(context) {
|
|
62
|
+
const { confidence, matchedSignals, recommendedAgent } = context;
|
|
63
|
+
const signalSummary = confidence.signals
|
|
64
|
+
.map((entry) => `${entry.name} (${entry.confidence.toFixed(2)})`)
|
|
65
|
+
.join(', ');
|
|
66
|
+
const lines = [
|
|
67
|
+
`Repertoire memory routing (${context.providerId}, trigger: ${context.triggeredBy})`,
|
|
68
|
+
`Matched signals: ${matchedSignals.join(', ') || 'none'}`,
|
|
69
|
+
`Signal confidences: ${signalSummary || 'none'}`,
|
|
70
|
+
`High-confidence trap present: ${confidence.highConfidenceTrapPresent}`,
|
|
71
|
+
`Complexity boost: ${confidence.complexityBoost}`,
|
|
72
|
+
];
|
|
73
|
+
if (recommendedAgent) {
|
|
74
|
+
lines.push(`Recommended agent for trap handling: ${recommendedAgent}`);
|
|
75
|
+
}
|
|
76
|
+
return lines;
|
|
77
|
+
}
|
|
78
|
+
export function formatMemoryRoutingBlock(context) {
|
|
79
|
+
const { confidence, matchedSignals, recommendedAgent } = context;
|
|
80
|
+
const lines = [
|
|
81
|
+
'MEMORY_ROUTING:',
|
|
82
|
+
` provider: ${context.providerId}`,
|
|
83
|
+
` trigger: ${context.triggeredBy}`,
|
|
84
|
+
` matchedSignals: ${matchedSignals.join(', ') || 'none'}`,
|
|
85
|
+
` highConfidenceTrapPresent: ${confidence.highConfidenceTrapPresent}`,
|
|
86
|
+
` complexityBoost: ${confidence.complexityBoost}`,
|
|
87
|
+
];
|
|
88
|
+
if (recommendedAgent) {
|
|
89
|
+
lines.push(` recommendedAgent: ${recommendedAgent}`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
|
@@ -11,6 +11,7 @@ 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 { buildMemoryRoutingEvidence, formatMemoryRoutingBlock, resolveResearcherMemoryContext, } from "./researcher-confidence.js";
|
|
14
15
|
import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
|
|
15
16
|
class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
16
17
|
constructor() {
|
|
@@ -377,13 +378,28 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
|
377
378
|
*/
|
|
378
379
|
async analyzeProposal(args) {
|
|
379
380
|
const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args || {};
|
|
380
|
-
const
|
|
381
|
+
const memoryContext = resolveResearcherMemoryContext({
|
|
382
|
+
proposalTitle,
|
|
383
|
+
proposalDescription,
|
|
384
|
+
proposalType,
|
|
385
|
+
});
|
|
386
|
+
const enrichedEvidence = [...(evidence || [])];
|
|
387
|
+
if (memoryContext?.confidence.highConfidenceTrapPresent) {
|
|
388
|
+
enrichedEvidence.push(...buildMemoryRoutingEvidence(memoryContext));
|
|
389
|
+
}
|
|
390
|
+
const vote = await tryLLMGovernance("researcher", proposalTitle || "", proposalDescription || "", enrichedEvidence, proposalType || "");
|
|
391
|
+
const memoryRoutingBlock = memoryContext?.confidence.highConfidenceTrapPresent
|
|
392
|
+
? `\n${formatMemoryRoutingBlock(memoryContext)}`
|
|
393
|
+
: "";
|
|
381
394
|
if (!vote) {
|
|
395
|
+
const abstainReason = memoryContext?.confidence.highConfidenceTrapPresent
|
|
396
|
+
? "No LLM governance provider configured. Repertoire detected a high-confidence ontological trap — route to the recommended agent before proceeding."
|
|
397
|
+
: "No LLM governance provider configured. Set XRAY_GOVERNANCE_LLM_ENABLED=true + XRAY_LLM_ENDPOINT, or install Hermes and run: hermes auth add xai-oauth --no-browser";
|
|
382
398
|
return {
|
|
383
399
|
content: [
|
|
384
400
|
{
|
|
385
401
|
type: "text",
|
|
386
|
-
text:
|
|
402
|
+
text: `DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: ${abstainReason}${memoryRoutingBlock}`,
|
|
387
403
|
},
|
|
388
404
|
],
|
|
389
405
|
};
|
|
@@ -392,7 +408,7 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
|
392
408
|
content: [
|
|
393
409
|
{
|
|
394
410
|
type: "text",
|
|
395
|
-
text: `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${vote.reasoning}`,
|
|
411
|
+
text: `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${vote.reasoning}${memoryRoutingBlock}`,
|
|
396
412
|
},
|
|
397
413
|
],
|
|
398
414
|
};
|
|
@@ -45,6 +45,18 @@ export class NullMemoryRoutingProvider {
|
|
|
45
45
|
context: { ...EMPTY_CONTEXT },
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
getTaskConfidence() {
|
|
49
|
+
return {
|
|
50
|
+
signals: [],
|
|
51
|
+
matchedSignals: [],
|
|
52
|
+
avgConfidence: 0,
|
|
53
|
+
maxConfidence: 0,
|
|
54
|
+
highConfidenceTrapPresent: false,
|
|
55
|
+
ontologicalTrapDetected: false,
|
|
56
|
+
complexityBoost: 0,
|
|
57
|
+
recommendedAgent: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
export function createMemoryRoutingProvider() {
|
|
50
62
|
return new NullMemoryRoutingProvider();
|