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.
Files changed (38) hide show
  1. package/.opencode/codex.codex +1 -1
  2. package/.opencode/enforcer-config.json +1 -4
  3. package/dist/CHANGELOG.md +22 -0
  4. package/dist/analytics/routing-refiner.js +0 -2
  5. package/dist/core/boot-orchestrator.js +9 -1
  6. package/dist/core/features-config.js +0 -1
  7. package/dist/integrations/grok/grok-cli.js +53 -25
  8. package/dist/integrations/hermes-agent/bridge.mjs +11 -1
  9. package/dist/mcps/index.js +1 -0
  10. package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
  11. package/dist/mcps/knowledge-skills/storyteller.server.js +167 -0
  12. package/dist/mcps/orchestrator/execution/execution-planner.js +14 -2
  13. package/dist/mcps/registry.json +0 -1
  14. package/dist/mcps/researcher-confidence.js +92 -0
  15. package/dist/mcps/researcher.server.js +19 -3
  16. package/dist/memory-routing/null-provider.js +12 -0
  17. package/dist/orchestrator/universal-registry-bridge.js +1 -1
  18. package/dist/skills/registry.json +0 -1
  19. package/package.json +1 -1
  20. package/scripts/node/enforce-agents-md.mjs +0 -26
  21. package/scripts/node/enforce-version-compliance.sh +0 -1
  22. package/scripts/node/enforce-version-compliance.ts +0 -10
  23. package/scripts/node/install-bridges.cjs +466 -0
  24. package/scripts/node/postinstall.cjs +24 -83
  25. package/scripts/node/pre-publish-guard.js +20 -16
  26. package/scripts/node/setup.cjs +11 -10
  27. package/scripts/node/universal-version-manager.js +17 -3
  28. package/src/mcps/index.ts +1 -0
  29. package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
  30. package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
  31. package/src/mcps/orchestrator/execution/execution-planner.ts +14 -2
  32. package/src/mcps/orchestrator/types.ts +4 -0
  33. package/src/mcps/registry.json +0 -1
  34. package/src/mcps/researcher-confidence.ts +144 -0
  35. package/src/mcps/researcher.server.ts +28 -3
  36. package/src/opencode/codex.codex +1 -1
  37. package/src/opencode/enforcer-config.json +1 -4
  38. package/src/skills/registry.json +0 -1
@@ -2,112 +2,53 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { execSync } = require("child_process");
5
+ const {
6
+ installAllBridges,
7
+ resolveConsumerTargetDir,
8
+ isConsumerInstall,
9
+ } = require("./install-bridges.cjs");
6
10
 
7
- // Structured logging shim (fwLogger discipline for script context; mirrors FrameworkUsageLogger format to logs/framework/activity.log in full runs)
8
11
  function structuredLog(component, action, status, details) {
9
12
  const ts = new Date().toISOString();
10
- const detailsPart = details ? ` | ${JSON.stringify(details)}` : '';
13
+ const detailsPart = details ? ` | ${JSON.stringify(details)}` : "";
11
14
  console.log(`${ts} [${component}] ${action} - ${String(status).toUpperCase()}${detailsPart}`);
12
15
  }
13
16
 
14
17
  const packageRoot = path.join(__dirname, "..", "..");
15
-
16
- let targetDir;
17
- if (__dirname.includes("node_modules/xray")) {
18
- // plain xray (final identity)
19
- targetDir = path.join(__dirname, "..", "..", "..", "..");
20
- } else {
21
- targetDir = process.env.PWD || process.cwd();
22
- }
23
-
18
+ const targetDir = resolveConsumerTargetDir(packageRoot, process.env.PWD || process.cwd());
24
19
  const resolvedPackage = path.resolve(packageRoot);
25
20
  const resolvedTarget = path.resolve(targetDir);
26
21
 
27
-
28
- const SKIP_DIRS = new Set(["node_modules", "logs"]);
29
-
30
22
  // Copy AGENTS-consumer.md → AGENTS.md
31
23
  const agentsConsumer = path.join(packageRoot, "AGENTS-consumer.md");
32
24
  const agentsDest = path.join(targetDir, "AGENTS.md");
33
- if (fs.existsSync(agentsConsumer) && resolvedPackage !== resolvedTarget) {
25
+ if (fs.existsSync(agentsConsumer) && isConsumerInstall(resolvedPackage, resolvedTarget)) {
34
26
  fs.copyFileSync(agentsConsumer, agentsDest);
35
27
  }
36
28
 
37
29
  // Copy .gitignore.default → .gitignore (never overwrite existing)
38
30
  const gitignoreSource = path.join(packageRoot, ".gitignore.default");
39
31
  const gitignoreDest = path.join(targetDir, ".gitignore");
40
- if (!fs.existsSync(gitignoreDest) && resolvedPackage !== resolvedTarget) {
32
+ if (!fs.existsSync(gitignoreDest) && isConsumerInstall(resolvedPackage, resolvedTarget)) {
41
33
  fs.copyFileSync(gitignoreSource, gitignoreDest);
42
- structuredLog('postinstall', 'Created .gitignore from template', 'info');
34
+ structuredLog("postinstall", "Created .gitignore from template", "info");
43
35
  }
44
36
 
45
- // Sync core skills from dist/ → .opencode/skills/
46
- const skillsSource = path.join(packageRoot, 'dist', 'skills');
47
- const skillsDest = path.join(targetDir, '.opencode', 'skills');
48
- if (fs.existsSync(skillsSource) && resolvedPackage !== resolvedTarget) {
37
+ // Unified 4-platform bridge install (OpenCode, Grok, Hermes, OpenClaw)
38
+ if (isConsumerInstall(resolvedPackage, resolvedTarget)) {
49
39
  try {
50
- if (!fs.existsSync(skillsDest)) fs.mkdirSync(skillsDest, { recursive: true });
51
- const skillDirs = fs.readdirSync(skillsSource, { withFileTypes: true });
52
- let copied = 0, skipped = 0;
53
- for (const entry of skillDirs) {
54
- if (!entry.isDirectory()) continue;
55
- const skillMd = path.join(skillsSource, entry.name, 'SKILL.md');
56
- if (!fs.existsSync(skillMd)) continue;
57
- const destMd = path.join(skillsDest, entry.name, 'SKILL.md');
58
- const destDir = path.dirname(destMd);
59
- if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
60
- if (fs.existsSync(destMd)) {
61
- if (fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime) continue;
62
- }
63
- fs.copyFileSync(skillMd, destMd);
64
- copied++;
65
- }
66
- if (copied > 0) structuredLog('postinstall', `Skills: ${copied} installed to .opencode/skills/`, 'info');
40
+ installAllBridges({
41
+ targetDir,
42
+ packageRoot,
43
+ log: structuredLog,
44
+ });
67
45
  } catch (e) {
68
- structuredLog('postinstall', `Skills sync skipped: ${e.message}`, 'warn');
46
+ structuredLog("postinstall", "Bridge install failed", "warn", { error: e.message });
69
47
  }
70
- }
71
-
72
- // Register MCP servers with Grok CLI (if available) using absolute paths to installed dist
73
- // Full v2 surface: governance + skills + orchestrator + enforcer (additive, preserves prior behavior)
74
- if (resolvedPackage !== resolvedTarget) {
75
- try {
76
- execSync('which grok', { stdio: 'ignore' });
77
- const govServer = path.join(packageRoot, 'dist/mcps/governance.server.js');
78
- const skillsServer = path.join(packageRoot, 'dist/mcps/knowledge-skills/skill-invocation.server.js');
79
- const orchServer = path.join(packageRoot, 'dist/mcps/orchestrator.server.js');
80
- const enforcerServer = path.join(packageRoot, 'dist/mcps/enforcer-tools.server.js');
81
- const xrayRoot = targetDir;
82
48
 
83
- execSync(
84
- `grok mcp add xray-governance --command node --args "${govServer}" --env "XRAY_FORCE_MCP_GOVERNANCE=true" --env "XRAY_ROOT=${xrayRoot}"`,
85
- { stdio: 'pipe' }
86
- );
87
- structuredLog('postinstall', 'Registered xray-governance with Grok CLI', 'info');
88
-
89
- execSync(
90
- `grok mcp add xray-skills --command node --args "${skillsServer}" --env "XRAY_ROOT=${xrayRoot}"`,
91
- { stdio: 'pipe' }
92
- );
93
- structuredLog('postinstall', 'Registered xray-skills with Grok CLI', 'info');
94
-
95
- execSync(
96
- `grok mcp add xray-orchestrator --command node --args "${orchServer}" --env "XRAY_ROOT=${xrayRoot}"`,
97
- { stdio: 'pipe' }
98
- );
99
- structuredLog('postinstall', 'Registered xray-orchestrator with Grok CLI', 'info');
100
-
101
- execSync(
102
- `grok mcp add xray-enforcer --command node --args "${enforcerServer}" --env "XRAY_ROOT=${xrayRoot}"`,
103
- { stdio: 'pipe' }
104
- );
105
- structuredLog('postinstall', 'Registered xray-enforcer with Grok CLI', 'info');
106
- } catch (_e) {
107
- // grok not on PATH or registration failed — skip gracefully
108
- }
109
- }
110
-
111
- if (resolvedPackage !== resolvedTarget) {
112
- structuredLog('postinstall', 'xray v2 framework installed. Run `npx xray setup` for full configuration (hooks, Hermes, symlinks).', 'success');
113
- }
49
+ structuredLog(
50
+ "postinstall",
51
+ "0xRay framework installed (4 bridges). Run `npx 0xray setup` for symlinks/Hermes skill extras.",
52
+ "success"
53
+ );
54
+ }
@@ -92,6 +92,7 @@ function checkGitStatus() {
92
92
  'logs/',
93
93
  'backups/',
94
94
  'Users/',
95
+ '.xray/inference/',
95
96
  ];
96
97
 
97
98
  const realChanges = lines.filter(l => {
@@ -128,32 +129,35 @@ function checkGitStatus() {
128
129
 
129
130
  function checkVersionSync() {
130
131
  log('Checking version synchronization...', 'step');
131
-
132
- // Load version manager
132
+
133
+ const pkgPath = path.join(rootDir, 'package.json');
134
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
135
+ const pkgVersion = pkg.version;
136
+
133
137
  const vmPath = path.join(rootDir, 'scripts/node/universal-version-manager.js');
134
138
  const vmContent = fs.readFileSync(vmPath, 'utf-8');
135
- const versionMatch = vmContent.match(/version:\s*"([^"]+)"/);
136
-
139
+ const usesPackageSsot =
140
+ vmContent.includes('readPackageVersion()') &&
141
+ /version:\s*PACKAGE_VERSION/.test(vmContent);
142
+
143
+ if (usesPackageSsot) {
144
+ log(`Version SSOT: package.json (${pkgVersion})`, 'success');
145
+ return true;
146
+ }
147
+
148
+ const versionMatch = vmContent.match(/framework:\s*\{[\s\S]*?version:\s*"([^"]+)"/);
137
149
  if (!versionMatch) {
138
- errors.push('Cannot find version in version manager');
150
+ errors.push('Cannot find framework version in version manager');
139
151
  return false;
140
152
  }
141
-
153
+
142
154
  const vmVersion = versionMatch[1];
143
-
144
- // Load package.json
145
- const pkgPath = path.join(rootDir, 'package.json');
146
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
147
- const pkgVersion = pkg.version;
148
-
149
155
  if (pkgVersion !== vmVersion) {
150
- // package.json is often 1 ahead of UVM after npm version patch — non-blocking
151
156
  warnings.push(`Version mismatch: package.json (${pkgVersion}) != version manager (${vmVersion})`);
152
157
  log(`Version mismatch: package.json=${pkgVersion}, version-manager=${vmVersion}`, 'warn');
153
- return true; // non-blocking — matches pre-commit hook behavior
158
+ } else {
159
+ log(`Versions aligned: ${pkgVersion}`, 'success');
154
160
  }
155
-
156
- log(`Versions aligned: ${pkgVersion}`, 'success');
157
161
  return true;
158
162
  }
159
163
 
@@ -6,12 +6,8 @@ const path = require("path");
6
6
  const packageRoot = path.join(__dirname, "..", "..");
7
7
  const homeDir = require("os").homedir();
8
8
 
9
- let targetDir;
10
- if (__dirname.includes("node_modules/xray")) {
11
- targetDir = path.join(__dirname, "..", "..", "..", "..");
12
- } else {
13
- targetDir = process.env.PWD || process.cwd();
14
- }
9
+ const { resolveConsumerTargetDir, isConsumerInstall } = require("./install-bridges.cjs");
10
+ let targetDir = resolveConsumerTargetDir(packageRoot, process.env.PWD || process.cwd());
15
11
 
16
12
  const resolvedPackage = path.resolve(packageRoot);
17
13
  const resolvedTarget = path.resolve(targetDir);
@@ -159,8 +155,13 @@ if (fs.existsSync(distSource)) {
159
155
  }
160
156
 
161
157
  // 6. Convert MCP paths in consumer opencode.json
162
- const isConsumer = fs.existsSync(path.join(targetDir, "node_modules", "xray", "package.json"));
163
- if (!hasHermes && isConsumer) {
158
+ const isConsumer = isConsumerInstall(packageRoot, targetDir);
159
+ const consumerPkgName = isConsumer
160
+ ? ["0xray", "xray"].find((name) =>
161
+ fs.existsSync(path.join(targetDir, "node_modules", name, "package.json"))
162
+ )
163
+ : null;
164
+ if (!hasHermes && isConsumer && consumerPkgName) {
164
165
  const mainOpencodePath = path.join(targetDir, "opencode.json");
165
166
  if (fs.existsSync(mainOpencodePath)) {
166
167
  try {
@@ -171,13 +172,13 @@ if (!hasHermes && isConsumer) {
171
172
  if (server.command && typeof server.command === "string") {
172
173
  const normalized = server.command.replace(
173
174
  /^[.]{0,2}\/dist\/mcps\//,
174
- "node_modules/xray/dist/mcps/"
175
+ `node_modules/${consumerPkgName}/dist/mcps/`
175
176
  );
176
177
  if (normalized !== server.command) { server.command = normalized; modified = true; }
177
178
  }
178
179
  if (Array.isArray(server.command)) {
179
180
  server.command = server.command.map(a =>
180
- a.replace(/^[.]{0,2}\/dist\/mcps\//, "node_modules/xray/dist/mcps/")
181
+ a.replace(/^[.]{0,2}\/dist\/mcps\//, `node_modules/${consumerPkgName}/dist/mcps/`)
181
182
  );
182
183
  modified = true;
183
184
  }
@@ -17,7 +17,6 @@
17
17
  * - Historical docs (docs/reflections, docs/archive)
18
18
  * - Test files with assertions
19
19
  *
20
- * @version 2.0.0
21
20
  * @since 2026-01-15
22
21
  * @updated 2026-03-09 - Enhanced with documentation management, validation, and backup
23
22
  */
@@ -28,6 +27,19 @@ const require = createRequire(import.meta.url);
28
27
  import fs from "fs";
29
28
  import path from "path";
30
29
  import { execSync } from "child_process";
30
+ import { fileURLToPath } from "url";
31
+
32
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
33
+ const rootDir = path.resolve(__dirname, "../..");
34
+
35
+ function readPackageVersion() {
36
+ try {
37
+ const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf-8"));
38
+ return pkg.version;
39
+ } catch {
40
+ return "0.0.0";
41
+ }
42
+ }
31
43
 
32
44
  /**
33
45
  * Auto-calculate framework counts from filesystem
@@ -133,10 +145,12 @@ const CALCULATED_COUNTS = calculateCounts();
133
145
  * Framework version information - SINGLE SOURCE OF TRUTH
134
146
  * Version manager updates this, then propagates to all files
135
147
  */
148
+ const PACKAGE_VERSION = readPackageVersion();
149
+
136
150
  const OFFICIAL_VERSIONS = {
137
- // Framework version
151
+ // Framework version — SSOT: package.json
138
152
  framework: {
139
- version: "3.1.1",
153
+ version: PACKAGE_VERSION,
140
154
  displayName: "xray: Self-Healing AI Governance OS",
141
155
  lastUpdated: "2026-06-17",
142
156
  // Counts (auto-calculated, but can be overridden)
package/src/mcps/index.ts CHANGED
@@ -50,6 +50,7 @@ export const MCP_SERVERS: McpServerEntry[] = [
50
50
  { serverName: "refactoring-strategies", sourceFile: "mcps/knowledge-skills/refactoring-strategies.server.ts", type: "knowledge-skill", registered: true, description: "Refactoring strategies" },
51
51
  { serverName: "security-audit", sourceFile: "mcps/knowledge-skills/security-audit.server.ts", type: "knowledge-skill", registered: true, description: "Security auditing" },
52
52
  { serverName: "seo-consultant", sourceFile: "mcps/knowledge-skills/seo-consultant.server.ts", type: "knowledge-skill", registered: false, description: "SEO consulting" },
53
+ { serverName: "storyteller", sourceFile: "mcps/knowledge-skills/storyteller.server.ts", type: "knowledge-skill", registered: false, description: "Reflections, sagas, and technical narratives" },
53
54
  { serverName: "session-management", sourceFile: "mcps/knowledge-skills/session-management.server.ts", type: "knowledge-skill", registered: true, description: "Session management" },
54
55
  { serverName: "skill-invocation", sourceFile: "mcps/knowledge-skills/skill-invocation.server.ts", type: "knowledge-skill", registered: true, description: "Skill invocation" },
55
56
  { serverName: "strategist", sourceFile: "mcps/knowledge-skills/strategist.server.ts", type: "knowledge-skill", registered: false, description: "Strategic planning" },
@@ -186,7 +186,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
186
186
  "strategist", "skill-invocation",
187
187
  ];
188
188
  const knowledgeSkills = [
189
- "architecture-patterns", "strategist", "tech-writer",
189
+ "architecture-patterns", "strategist", "tech-writer", "storyteller",
190
190
  "seo-consultant", "content-creator", "growth-strategist",
191
191
  "bug-triage-specialist", "log-monitor",
192
192
  "mobile-development", "git-workflow", "session-management",
@@ -236,6 +236,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
236
236
  "tech-writer": "documentation-generation",
237
237
  "frontend-ui-ux-engineer": "ui-ux-design",
238
238
  "frontend-engineer": "project-analysis",
239
+ "storyteller": "storyteller",
239
240
  };
240
241
 
241
242
  const resolvedSkill = skillAliases[skillName] || skillName;
@@ -253,7 +254,7 @@ class SkillInvocationServer extends XrayKnowledgeSkillBase {
253
254
  "git-workflow", "content-creator", "ui-ux-design",
254
255
  "multimodal-looker", "refactoring-strategies",
255
256
  "project-analysis", "testing-best-practices",
256
- "architecture-patterns",
257
+ "architecture-patterns", "storyteller",
257
258
  ];
258
259
 
259
260
  if (!availableServers.includes(resolvedSkill)) {
@@ -0,0 +1,206 @@
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
+
8
+ import { execSync } from 'node:child_process';
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
10
+ import { dirname, join } from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { XrayKnowledgeSkillBase } from '../shared/knowledge-skill-base.js';
13
+
14
+ const STORY_TYPES = ['reflection', 'saga', 'journey', 'narrative'] as const;
15
+ type StoryType = (typeof STORY_TYPES)[number];
16
+
17
+ interface WriteStoryArgs {
18
+ title?: string;
19
+ context?: Record<string, unknown>;
20
+ framework?: string;
21
+ output?: string;
22
+ }
23
+
24
+ function resolveSkillMd(): string {
25
+ const here = dirname(fileURLToPath(import.meta.url));
26
+ const candidates = [
27
+ join(here, '../../../skills/storyteller/SKILL.md'),
28
+ join(here, '../../skills/storyteller/SKILL.md'),
29
+ join(process.cwd(), 'node_modules/0xray/dist/skills/storyteller/SKILL.md'),
30
+ join(process.cwd(), 'node_modules/xray/dist/skills/storyteller/SKILL.md'),
31
+ ];
32
+
33
+ for (const path of candidates) {
34
+ if (existsSync(path)) {
35
+ return readFileSync(path, 'utf-8');
36
+ }
37
+ }
38
+
39
+ return '# Storyteller\n\nWrite warmly candid technical reflections with scene-first openings.';
40
+ }
41
+
42
+ function getStoryMeta(storyType: StoryType) {
43
+ const defaults: Record<
44
+ StoryType,
45
+ { location: string; min_words: number; ideal_words: number; framework: string }
46
+ > = {
47
+ reflection: {
48
+ location: 'docs/reflections/',
49
+ min_words: 2000,
50
+ ideal_words: 5000,
51
+ framework: 'three_act_structure',
52
+ },
53
+ saga: {
54
+ location: 'docs/reflections/deep/',
55
+ min_words: 5000,
56
+ ideal_words: 15000,
57
+ framework: 'hero_journey',
58
+ },
59
+ journey: {
60
+ location: 'docs/reflections/deep/',
61
+ min_words: 1500,
62
+ ideal_words: 4000,
63
+ framework: 'three_act_structure',
64
+ },
65
+ narrative: {
66
+ location: 'docs/reflections/',
67
+ min_words: 1000,
68
+ ideal_words: 3000,
69
+ framework: 'three_act_structure',
70
+ },
71
+ };
72
+ return defaults[storyType];
73
+ }
74
+
75
+ function gitContext(): string {
76
+ try {
77
+ const commits = execSync('git log --oneline -20 2>/dev/null', { encoding: 'utf-8' });
78
+ const files = execSync(
79
+ 'git diff --name-only HEAD~10 2>/dev/null || git ls-files 2>/dev/null | head -50',
80
+ { encoding: 'utf-8' },
81
+ );
82
+ return `Recent commits:\n${commits}\n\nRecent files:\n${files}`;
83
+ } catch {
84
+ return 'No git context available in this workspace.';
85
+ }
86
+ }
87
+
88
+ function buildPrompt(
89
+ storyType: StoryType,
90
+ title: string,
91
+ meta: ReturnType<typeof getStoryMeta>,
92
+ extraContext?: Record<string, unknown>,
93
+ ): string {
94
+ const contextBlock = extraContext
95
+ ? `\n## Provided context\n${JSON.stringify(extraContext, null, 2)}\n`
96
+ : '';
97
+
98
+ return `Write a ${storyType} titled "${title}".
99
+
100
+ ## Requirements
101
+ - Framework: ${meta.framework}
102
+ - Target length: ${meta.ideal_words} words (minimum: ${meta.min_words})
103
+ - Save to: ${meta.location}
104
+ - Follow storyteller SKILL.md voice (warmly candid, scene-first, no executive summary)
105
+ - Include Key Takeaways and What Next sections
106
+
107
+ ## Session / repo context
108
+ ${gitContext()}
109
+ ${contextBlock}
110
+
111
+ Write the complete ${storyType} now.`;
112
+ }
113
+
114
+ function writeStoryTool(storyType: StoryType, args: WriteStoryArgs) {
115
+ const meta = getStoryMeta(storyType);
116
+ if (args.framework) meta.framework = args.framework;
117
+
118
+ const title =
119
+ args.title ??
120
+ `${storyType.charAt(0).toUpperCase() + storyType.slice(1)} - ${new Date().toISOString().split('T')[0]}`;
121
+ const skillMd = resolveSkillMd();
122
+ const prompt = buildPrompt(storyType, title, meta, args.context);
123
+ const slug = title
124
+ .toLowerCase()
125
+ .replace(/[^a-z0-9]+/g, '-')
126
+ .replace(/^-|-$/g, '');
127
+ const filename = `${storyType}-${slug}-${new Date().toISOString().split('T')[0]}.md`;
128
+ const outputPath = args.output ?? join(meta.location, filename);
129
+
130
+ const outputDir = join(process.cwd(), dirname(outputPath));
131
+ if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
132
+
133
+ const fullPath = join(process.cwd(), outputPath);
134
+ const frontmatter = `---
135
+ story_type: ${storyType}
136
+ title: "${title}"
137
+ framework: ${meta.framework}
138
+ target_words: ${meta.ideal_words}
139
+ ---
140
+
141
+ `;
142
+
143
+ writeFileSync(fullPath, `${frontmatter}\n${prompt}\n`, 'utf-8');
144
+
145
+ return {
146
+ content: [
147
+ {
148
+ type: 'text' as const,
149
+ text: [
150
+ `Storyteller ${storyType} brief prepared.`,
151
+ `Output template: ${outputPath}`,
152
+ '',
153
+ '--- SKILL (excerpt) ---',
154
+ skillMd.slice(0, 4000),
155
+ skillMd.length > 4000 ? '\n...(truncated; full SKILL.md on disk)' : '',
156
+ '',
157
+ '--- WRITING PROMPT ---',
158
+ prompt,
159
+ '',
160
+ 'Host LLM: write the full story into the output path using the prompt above.',
161
+ ].join('\n'),
162
+ },
163
+ ],
164
+ };
165
+ }
166
+
167
+ class StorytellerServer extends XrayKnowledgeSkillBase {
168
+ constructor() {
169
+ super('storyteller', '1.0.0');
170
+
171
+ const writeSchema = {
172
+ type: 'object' as const,
173
+ properties: {
174
+ title: { type: 'string', description: 'Story title' },
175
+ context: { type: 'object', description: 'Session context (commits, changes, notes)' },
176
+ framework: { type: 'string', description: 'Storytelling framework override' },
177
+ output: { type: 'string', description: 'Relative output path' },
178
+ },
179
+ };
180
+
181
+ this.tools = STORY_TYPES.map((storyType) => ({
182
+ name: `write_${storyType}`,
183
+ description: `Prepare ${storyType} story brief using storyteller SKILL.md`,
184
+ inputSchema: writeSchema,
185
+ }));
186
+
187
+ this.handlers = {
188
+ write_reflection: async (args) => writeStoryTool('reflection', args as WriteStoryArgs),
189
+ write_saga: async (args) => writeStoryTool('saga', args as WriteStoryArgs),
190
+ write_journey: async (args) => writeStoryTool('journey', args as WriteStoryArgs),
191
+ write_narrative: async (args) => writeStoryTool('narrative', args as WriteStoryArgs),
192
+ };
193
+
194
+ this.setupToolHandlers();
195
+ }
196
+ }
197
+
198
+ if (import.meta.url === `file://${fileURLToPath(import.meta.url)}`) {
199
+ const server = new StorytellerServer();
200
+ server.run().catch((error) => {
201
+ process.stderr.write(`FATAL: ${String(error)}\n`);
202
+ process.exit(1);
203
+ });
204
+ }
205
+
206
+ export { StorytellerServer };
@@ -213,15 +213,18 @@ export class ExecutionPlanner {
213
213
  agentAssignments: Map<string, OrchestrationTask[]>
214
214
  ): void {
215
215
  for (const task of tasks) {
216
- const complexity = task.estimatedComplexity || 30;
216
+ const complexity = this.calculateTaskComplexity(task);
217
217
  const requiredCaps = [
218
218
  task.type,
219
219
  ...(task.metadata?.memorySignals ?? []),
220
220
  ];
221
+ const operationDescription = task.metadata?.memoryHighConfidenceTrap
222
+ ? `${task.description} TYPE: ontological-trap`
223
+ : task.description;
221
224
  const agent = this.capabilitiesManager.selectAgentForTask(
222
225
  requiredCaps,
223
226
  complexity,
224
- task.description,
227
+ operationDescription,
225
228
  ) || 'orchestrator';
226
229
 
227
230
  if (!agentAssignments.has(agent)) {
@@ -346,6 +349,15 @@ export class ExecutionPlanner {
346
349
  complexity += task.dependencies.length * 5;
347
350
  }
348
351
 
352
+ if (task.metadata?.memoryComplexityBoost !== undefined) {
353
+ complexity += task.metadata.memoryComplexityBoost;
354
+ } else {
355
+ const provider = getProvider();
356
+ if (provider.id !== 'null' && provider.getTaskConfidence) {
357
+ complexity += provider.getTaskConfidence(toMemoryTask(task)).complexityBoost;
358
+ }
359
+ }
360
+
349
361
  return Math.min(Math.max(Math.round(complexity), 1), 100);
350
362
  }
351
363
 
@@ -19,6 +19,10 @@ export interface OrchestrationTaskMetadata {
19
19
  synthesisContext?: string;
20
20
  ontologicalTrapDetected?: boolean;
21
21
  memoryProviderId?: string;
22
+ memorySignalConfidences?: Record<string, number>;
23
+ memoryAvgConfidence?: number;
24
+ memoryHighConfidenceTrap?: boolean;
25
+ memoryComplexityBoost?: number;
22
26
  }
23
27
 
24
28
  export interface OrchestrationTask {
@@ -1,5 +1,4 @@
1
1
  {
2
- "version": "3.1.0",
3
2
  "description": "0xRay Community MCPs Registry - curated API-focused MCP servers",
4
3
  "sources": [
5
4
  {