0xray 3.2.0 → 3.3.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 (36) hide show
  1. package/.opencode/codex.codex +1 -1
  2. package/dist/CHANGELOG.md +36 -0
  3. package/dist/core/features-config.js +3 -0
  4. package/dist/integrations/grok/grok-cli.js +6 -0
  5. package/dist/mcps/index.js +1 -0
  6. package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
  7. package/dist/mcps/knowledge-skills/storyteller.server.js +167 -0
  8. package/dist/mcps/orchestrator/config/agent-capabilities.js +30 -11
  9. package/dist/mcps/orchestrator/config/memory-routing-bridge.js +70 -0
  10. package/dist/mcps/orchestrator/execution/execution-planner.js +38 -8
  11. package/dist/mcps/orchestrator/handlers/task-handler.js +55 -2
  12. package/dist/mcps/researcher-confidence.js +92 -0
  13. package/dist/mcps/researcher.server.js +19 -3
  14. package/dist/memory-routing/index.js +4 -0
  15. package/dist/memory-routing/null-provider.js +63 -0
  16. package/dist/memory-routing/provider-loader.js +91 -0
  17. package/dist/memory-routing/provider-registry.js +40 -0
  18. package/dist/memory-routing/types.js +7 -0
  19. package/dist/memory-routing/validate-config.js +32 -0
  20. package/dist/nucleus/thin-dispatch.js +29 -1
  21. package/package.json +1 -1
  22. package/scripts/node/install-bridges.cjs +364 -0
  23. package/scripts/node/postinstall.cjs +24 -83
  24. package/scripts/node/setup.cjs +2 -6
  25. package/scripts/node/universal-version-manager.js +2 -2
  26. package/src/mcps/index.ts +1 -0
  27. package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
  28. package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
  29. package/src/mcps/orchestrator/config/agent-capabilities.ts +49 -14
  30. package/src/mcps/orchestrator/config/memory-routing-bridge.ts +80 -0
  31. package/src/mcps/orchestrator/execution/execution-planner.ts +49 -10
  32. package/src/mcps/orchestrator/handlers/task-handler.ts +71 -2
  33. package/src/mcps/orchestrator/types.ts +17 -0
  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
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * install-bridges.cjs — Unified 4-platform bridge installer for consumer postinstall.
4
+ * Mirrors npx 0xray {opencode,grok,hermes,openclaw} install in one synchronous pass.
5
+ */
6
+
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+ const os = require("os");
10
+ const { execSync } = require("child_process");
11
+
12
+ const SKIP_DIRS = new Set(["node_modules", "logs"]);
13
+ const MERGE_FILES = new Set(["xray/features.json", "enforcer-config.json"]);
14
+ const KEEP_IF_EXISTS = new Set([".yml", ".yaml", ".md"]);
15
+
16
+ function resolveConsumerTargetDir(packageRoot, fallbackDir) {
17
+ const resolvedPackage = path.resolve(packageRoot);
18
+ if (resolvedPackage.includes(`${path.sep}node_modules${path.sep}`)) {
19
+ let current = resolvedPackage;
20
+ while (current.includes(`${path.sep}node_modules${path.sep}`)) {
21
+ current = path.dirname(current);
22
+ }
23
+ return current;
24
+ }
25
+ return fallbackDir || process.env.PWD || process.cwd();
26
+ }
27
+
28
+ function isConsumerInstall(packageRoot, targetDir) {
29
+ return path.resolve(packageRoot) !== path.resolve(targetDir);
30
+ }
31
+
32
+ function deepMerge(src, dest) {
33
+ if (typeof src !== "object" || src === null) return dest !== undefined ? dest : src;
34
+ if (Array.isArray(src)) return Array.isArray(dest) ? dest : src;
35
+ const result = {};
36
+ for (const key of Object.keys(src)) {
37
+ result[key] = dest && typeof dest[key] !== "undefined" ? deepMerge(src[key], dest[key]) : src[key];
38
+ }
39
+ if (dest && typeof dest === "object") {
40
+ for (const key of Object.keys(dest)) {
41
+ if (!(key in src)) result[key] = dest[key];
42
+ }
43
+ }
44
+ return result;
45
+ }
46
+
47
+ function syncBuiltinSkills(targetSkillsDir, packageRoot) {
48
+ const candidateDirs = [
49
+ path.join(packageRoot, "dist", "skills"),
50
+ path.join(packageRoot, "src", "skills"),
51
+ ];
52
+ const sourceDir = candidateDirs.find((p) => fs.existsSync(p));
53
+ if (!sourceDir) return 0;
54
+
55
+ let copied = 0;
56
+ try {
57
+ if (!fs.existsSync(targetSkillsDir)) fs.mkdirSync(targetSkillsDir, { recursive: true });
58
+ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
59
+ if (!entry.isDirectory()) continue;
60
+ const skillMd = path.join(sourceDir, entry.name, "SKILL.md");
61
+ if (!fs.existsSync(skillMd)) continue;
62
+ const destMd = path.join(targetSkillsDir, entry.name, "SKILL.md");
63
+ const destDir = path.dirname(destMd);
64
+ if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
65
+ if (fs.existsSync(destMd) && fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime) continue;
66
+ fs.copyFileSync(skillMd, destMd);
67
+ copied++;
68
+ }
69
+ } catch {
70
+ // best-effort
71
+ }
72
+ return copied;
73
+ }
74
+
75
+ function copyTree(src, dest, relPath = "") {
76
+ if (!fs.existsSync(src)) return;
77
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
78
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
79
+ if (SKIP_DIRS.has(entry.name)) continue;
80
+ const srcPath = path.join(src, entry.name);
81
+ const destPath = path.join(dest, entry.name);
82
+ const rel = path.join(relPath, entry.name);
83
+ if (entry.isDirectory()) {
84
+ copyTree(srcPath, destPath, rel);
85
+ } else if (MERGE_FILES.has(rel)) {
86
+ try {
87
+ const srcData = JSON.parse(fs.readFileSync(srcPath, "utf8"));
88
+ if (fs.existsSync(destPath)) {
89
+ const destData = JSON.parse(fs.readFileSync(destPath, "utf8"));
90
+ fs.writeFileSync(destPath, JSON.stringify(deepMerge(srcData, destData), null, 2));
91
+ } else {
92
+ fs.copyFileSync(srcPath, destPath);
93
+ }
94
+ } catch {
95
+ fs.copyFileSync(srcPath, destPath);
96
+ }
97
+ } else if (KEEP_IF_EXISTS.has(path.extname(srcPath)) && fs.existsSync(destPath)) {
98
+ continue;
99
+ } else {
100
+ fs.copyFileSync(srcPath, destPath);
101
+ }
102
+ }
103
+ }
104
+
105
+ function copyPluginDir(src, dest) {
106
+ if (!fs.existsSync(src)) return false;
107
+ fs.cpSync(src, dest, { recursive: true, force: true });
108
+ return true;
109
+ }
110
+
111
+ function installOpencodeBridge(targetDir, packageRoot, log) {
112
+ const opencodeSource = path.join(packageRoot, ".opencode");
113
+ const opencodeDest = path.join(targetDir, ".opencode");
114
+ if (!fs.existsSync(opencodeSource)) {
115
+ log("opencode-bridge", "skipped", "warn", { reason: ".opencode source missing" });
116
+ return;
117
+ }
118
+
119
+ if (!fs.existsSync(opencodeDest)) {
120
+ copyTree(opencodeSource, opencodeDest);
121
+ log("opencode-bridge", "copied .opencode tree", "info");
122
+ } else {
123
+ log("opencode-bridge", ".opencode exists — merging skills/plugin only", "info");
124
+ }
125
+
126
+ const pluginSource = path.join(packageRoot, "dist", "plugin", "xray-codex-injection.js");
127
+ const pluginDest = path.join(opencodeDest, "plugin", "xray-codex-injection.js");
128
+ if (fs.existsSync(pluginSource)) {
129
+ const pluginDestDir = path.dirname(pluginDest);
130
+ if (!fs.existsSync(pluginDestDir)) fs.mkdirSync(pluginDestDir, { recursive: true });
131
+ const shouldCopy =
132
+ !fs.existsSync(pluginDest) ||
133
+ fs.statSync(pluginSource).mtime > fs.statSync(pluginDest).mtime;
134
+ if (shouldCopy) {
135
+ fs.copyFileSync(pluginSource, pluginDest);
136
+ log("opencode-bridge", "plugin updated", "info");
137
+ }
138
+ }
139
+
140
+ const copied = syncBuiltinSkills(path.join(opencodeDest, "skills"), packageRoot);
141
+ if (copied > 0) log("opencode-bridge", `skills synced (${copied})`, "info", { path: ".opencode/skills/" });
142
+ }
143
+
144
+ function findGrokPluginSource(packageRoot) {
145
+ const candidates = [
146
+ path.join(packageRoot, "src", "integrations", "grok", "plugin", "0xray"),
147
+ path.join(packageRoot, ".grok", "plugins", "0xray"),
148
+ ];
149
+ return candidates.find((p) => fs.existsSync(p));
150
+ }
151
+
152
+ function registerGrokMcpServers(packageRoot, targetDir, log) {
153
+ try {
154
+ execSync("which grok", { stdio: "ignore" });
155
+ } catch {
156
+ log("grok-bridge", "grok CLI not on PATH — skipped MCP registration", "info");
157
+ return;
158
+ }
159
+
160
+ const govServer = path.join(packageRoot, "dist/mcps/governance.server.js");
161
+ const skillsServer = path.join(packageRoot, "dist/mcps/knowledge-skills/skill-invocation.server.js");
162
+ const orchServer = path.join(packageRoot, "dist/mcps/orchestrator.server.js");
163
+ const enforcerServer = path.join(packageRoot, "dist/mcps/enforcer-tools.server.js");
164
+
165
+ const servers = [
166
+ ["xray-governance", govServer, `XRAY_FORCE_MCP_GOVERNANCE=true`],
167
+ ["xray-skills", skillsServer, null],
168
+ ["xray-orchestrator", orchServer, null],
169
+ ["xray-enforcer", enforcerServer, null],
170
+ ];
171
+
172
+ for (const [name, serverPath, extraEnv] of servers) {
173
+ if (!fs.existsSync(serverPath)) continue;
174
+ try {
175
+ const envFlags = extraEnv
176
+ ? `--env "${extraEnv}" --env "XRAY_ROOT=${targetDir}"`
177
+ : `--env "XRAY_ROOT=${targetDir}"`;
178
+ execSync(
179
+ `grok mcp add ${name} --command node --args "${serverPath}" ${envFlags}`,
180
+ { stdio: "pipe" }
181
+ );
182
+ log("grok-bridge", `registered ${name}`, "info");
183
+ } catch {
184
+ // already registered or grok config conflict — non-blocking
185
+ }
186
+ }
187
+
188
+ for (const pluginDir of [
189
+ path.join(targetDir, ".grok", "plugins", "0xray"),
190
+ path.join(os.homedir(), ".grok", "plugins", "0xray"),
191
+ ]) {
192
+ if (!fs.existsSync(pluginDir)) continue;
193
+ try {
194
+ execSync(`grok plugins trust "${pluginDir}"`, { stdio: "ignore" });
195
+ log("grok-bridge", "plugin trusted", "info", { path: pluginDir });
196
+ break;
197
+ } catch {
198
+ // best-effort
199
+ }
200
+ }
201
+ }
202
+
203
+ function installGrokBridge(targetDir, packageRoot, log) {
204
+ const sourceDir = findGrokPluginSource(packageRoot);
205
+ if (!sourceDir) {
206
+ log("grok-bridge", "skipped", "warn", { reason: "grok plugin source missing" });
207
+ return;
208
+ }
209
+
210
+ const home = os.homedir();
211
+ const targets = [
212
+ path.join(targetDir, ".grok", "plugins", "0xray"),
213
+ path.join(home, ".grok", "plugins", "0xray"),
214
+ ];
215
+
216
+ for (const dest of targets) {
217
+ if (copyPluginDir(sourceDir, dest)) {
218
+ const rel = dest.startsWith(home) ? dest.replace(home, "~") : path.relative(targetDir, dest);
219
+ log("grok-bridge", "plugin copied", "info", { path: rel || dest });
220
+ const copied = syncBuiltinSkills(path.join(dest, "skills"), packageRoot);
221
+ if (copied > 0) log("grok-bridge", `plugin skills synced (${copied})`, "info", { path: rel });
222
+ }
223
+ }
224
+
225
+ // Grok Build / Cursor also reads ~/.grok/skills/ for agent_skills
226
+ const grokGlobalSkills = path.join(home, ".grok", "skills");
227
+ const globalCopied = syncBuiltinSkills(grokGlobalSkills, packageRoot);
228
+ if (globalCopied > 0) {
229
+ log("grok-bridge", `global skills synced (${globalCopied})`, "info", { path: "~/.grok/skills/" });
230
+ }
231
+
232
+ registerGrokMcpServers(packageRoot, targetDir, log);
233
+ }
234
+
235
+ function installHermesBridge(packageRoot, log) {
236
+ const sources = [
237
+ path.join(packageRoot, "dist", "integrations", "hermes-agent"),
238
+ path.join(packageRoot, "src", "integrations", "hermes-agent"),
239
+ ];
240
+ const sourceDir = sources.find((p) => fs.existsSync(p));
241
+ if (!sourceDir) {
242
+ log("hermes-bridge", "skipped", "warn", { reason: "hermes plugin source missing" });
243
+ return;
244
+ }
245
+
246
+ const targetPluginDir = path.join(os.homedir(), ".hermes", "plugins", "xray-hermes");
247
+ fs.mkdirSync(targetPluginDir, { recursive: true });
248
+ for (const entry of fs.readdirSync(sourceDir)) {
249
+ const src = path.join(sourceDir, entry);
250
+ const dst = path.join(targetPluginDir, entry);
251
+ if (fs.statSync(src).isDirectory()) {
252
+ fs.cpSync(src, dst, { recursive: true, force: true });
253
+ } else {
254
+ fs.copyFileSync(src, dst);
255
+ }
256
+ }
257
+ log("hermes-bridge", "plugin copied", "info", { path: "~/.hermes/plugins/xray-hermes" });
258
+
259
+ const copied = syncBuiltinSkills(path.join(targetPluginDir, "skills"), packageRoot);
260
+ if (copied > 0) log("hermes-bridge", `skills synced (${copied})`, "info");
261
+ }
262
+
263
+ function installOpenclawBridge(targetDir, packageRoot, log) {
264
+ const configPath = path.join(targetDir, ".xray", "config", "openclaw.json");
265
+ if (!fs.existsSync(configPath)) {
266
+ const configDir = path.dirname(configPath);
267
+ fs.mkdirSync(configDir, { recursive: true });
268
+ const sampleConfig = {
269
+ gatewayUrl: "ws://127.0.0.1:18789",
270
+ authToken: process.env.OPENCLAW_AUTH_TOKEN || "",
271
+ deviceId: process.env.OPENCLAW_DEVICE_ID || "your-device-id",
272
+ autoReconnect: true,
273
+ maxReconnectAttempts: 5,
274
+ reconnectDelay: 1000,
275
+ apiServer: {
276
+ enabled: true,
277
+ port: 18431,
278
+ host: "127.0.0.1",
279
+ ...(process.env.OPENCLAW_API_KEY ? { apiKey: process.env.OPENCLAW_API_KEY } : {}),
280
+ },
281
+ hooks: {
282
+ enabled: true,
283
+ toolBefore: true,
284
+ toolAfter: true,
285
+ includeArgs: true,
286
+ includeResult: true,
287
+ },
288
+ enabled: true,
289
+ debug: false,
290
+ logLevel: "info",
291
+ };
292
+ fs.writeFileSync(configPath, JSON.stringify(sampleConfig, null, 2) + "\n");
293
+ log("openclaw-bridge", "config created", "info", { path: ".xray/config/openclaw.json" });
294
+ }
295
+
296
+ const copied = syncBuiltinSkills(path.join(os.homedir(), ".openclaw", "skills"), packageRoot);
297
+ if (copied > 0) log("openclaw-bridge", `skills synced (${copied})`, "info", { path: "~/.openclaw/skills/" });
298
+ }
299
+
300
+ function deployXrayConfig(targetDir, packageRoot, log) {
301
+ const xraySourceDir = path.join(packageRoot, ".xray");
302
+ const xrayTargetDir = path.join(targetDir, ".xray");
303
+ const configFiles = ["codex.json", "features.json", "config.json"];
304
+ if (!fs.existsSync(xraySourceDir)) return;
305
+
306
+ if (!fs.existsSync(xrayTargetDir)) fs.mkdirSync(xrayTargetDir, { recursive: true });
307
+ let copied = 0;
308
+ for (const file of configFiles) {
309
+ const src = path.join(xraySourceDir, file);
310
+ const dst = path.join(xrayTargetDir, file);
311
+ if (!fs.existsSync(src)) continue;
312
+ const shouldCopy = !fs.existsSync(dst) || fs.statSync(src).mtime > fs.statSync(dst).mtime;
313
+ if (shouldCopy) {
314
+ fs.copyFileSync(src, dst);
315
+ copied++;
316
+ }
317
+ }
318
+ if (copied > 0) log("xray-config", `${copied} files deployed`, "info", { path: ".xray/" });
319
+ }
320
+
321
+ function installGitHooks(packageRoot, log) {
322
+ const installHooks = path.join(packageRoot, "scripts", "hooks", "install-hooks.cjs");
323
+ if (!fs.existsSync(installHooks)) return;
324
+ try {
325
+ execSync(`node "${installHooks}"`, { stdio: "pipe" });
326
+ log("hooks", "pre-commit hook installed", "info");
327
+ } catch {
328
+ // non-git or hook failure — not blocking
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Install all 4 platform bridges for a consumer project.
334
+ * @param {{ targetDir: string, packageRoot: string, log?: Function }} opts
335
+ */
336
+ function installAllBridges(opts) {
337
+ const packageRoot = path.resolve(opts.packageRoot);
338
+ const targetDir = path.resolve(opts.targetDir);
339
+ const log =
340
+ opts.log ||
341
+ ((_component, _action, _status, _details) => {
342
+ /* noop */
343
+ });
344
+
345
+ if (!isConsumerInstall(packageRoot, targetDir)) return;
346
+
347
+ log("install-bridges", "starting 4-platform install", "info");
348
+
349
+ deployXrayConfig(targetDir, packageRoot, log);
350
+ installOpencodeBridge(targetDir, packageRoot, log);
351
+ installGrokBridge(targetDir, packageRoot, log);
352
+ installHermesBridge(packageRoot, log);
353
+ installOpenclawBridge(targetDir, packageRoot, log);
354
+ installGitHooks(packageRoot, log);
355
+
356
+ log("install-bridges", "4-platform install complete", "success");
357
+ }
358
+
359
+ module.exports = {
360
+ installAllBridges,
361
+ resolveConsumerTargetDir,
362
+ syncBuiltinSkills,
363
+ isConsumerInstall,
364
+ };
@@ -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
+ }
@@ -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 } = 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);
@@ -136,9 +136,9 @@ const CALCULATED_COUNTS = calculateCounts();
136
136
  const OFFICIAL_VERSIONS = {
137
137
  // Framework version
138
138
  framework: {
139
- version: "3.1.1",
139
+ version: "3.3.0",
140
140
  displayName: "xray: Self-Healing AI Governance OS",
141
- lastUpdated: "2026-06-17",
141
+ lastUpdated: "2026-06-18",
142
142
  // Counts (auto-calculated, but can be overridden)
143
143
  ...CALCULATED_COUNTS,
144
144
  },
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)) {