context-mode 1.0.134 → 1.0.135
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/adapters/detect.d.ts +3 -1
- package/build/adapters/detect.js +7 -2
- package/build/adapters/pi/mcp-bridge.d.ts +8 -0
- package/build/adapters/pi/mcp-bridge.js +32 -0
- package/build/cli.js +17 -0
- package/build/runtime.js +8 -5
- package/build/session/analytics.d.ts +0 -13
- package/build/session/analytics.js +50 -1
- package/build/util/claude-config.d.ts +12 -6
- package/build/util/claude-config.js +16 -23
- package/cli.bundle.mjs +135 -133
- package/hooks/codex/sessionstart.mjs +23 -1
- package/hooks/core/platform-detect.mjs +1 -1
- package/hooks/normalize-hooks.mjs +5 -2
- package/hooks/security.bundle.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/heal-installed-plugins.mjs +67 -0
- package/server.bundle.mjs +99 -99
- package/start.mjs +73 -11
package/start.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
2
|
+
import { execSync, spawn } from "node:child_process";
|
|
3
3
|
import { existsSync, chmodSync, readFileSync, writeFileSync, readdirSync, symlinkSync, mkdirSync, lstatSync, unlinkSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve, join, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -9,6 +9,24 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
9
9
|
const originalCwd = process.cwd();
|
|
10
10
|
process.chdir(__dirname);
|
|
11
11
|
|
|
12
|
+
// Resolve the Claude Code config dir, honoring $CLAUDE_CONFIG_DIR (incl. leading ~).
|
|
13
|
+
// Mirrors hooks/session-helpers.mjs::resolveConfigDir and hooks/run-hook.mjs (#453).
|
|
14
|
+
// Inlined here because start.mjs runs before any other module loads — we cannot
|
|
15
|
+
// dynamic-import session-helpers without circularity through the bundle path.
|
|
16
|
+
// Fix for #577: cache-heal layer below was hardcoding ~/.claude regardless of
|
|
17
|
+
// the env var, silently no-op'ing for users with a non-default config dir AND
|
|
18
|
+
// creating an unwanted ~/.claude/ directory on disk.
|
|
19
|
+
function resolveClaudeConfigDir() {
|
|
20
|
+
const envVal = process.env.CLAUDE_CONFIG_DIR;
|
|
21
|
+
if (envVal && envVal.trim() !== "") {
|
|
22
|
+
if (envVal.startsWith("~")) {
|
|
23
|
+
return resolve(homedir(), envVal.replace(/^~[/\\]?/, ""));
|
|
24
|
+
}
|
|
25
|
+
return resolve(envVal);
|
|
26
|
+
}
|
|
27
|
+
return resolve(homedir(), ".claude");
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
// Plugin-install-path guard (mirror of src/util/project-dir.ts isPluginInstallPath
|
|
13
31
|
// — duplicated here because start.mjs ships as raw JS and cannot import TS).
|
|
14
32
|
// When Claude Code runs `/ctx-upgrade` it kills + respawns the MCP server with
|
|
@@ -41,6 +59,39 @@ if (!process.env.CONTEXT_MODE_PROJECT_DIR && safeOriginalCwd) {
|
|
|
41
59
|
// - Non-hook platforms: server.ts writeRoutingInstructions() on MCP connect
|
|
42
60
|
// - Future: explicit `context-mode init` command
|
|
43
61
|
|
|
62
|
+
// ── Linux: re-exec with Bun to avoid better-sqlite3 SIGSEGV (#564) ──
|
|
63
|
+
// server.bundle.mjs has two SQLite paths: bun:sqlite (safe) or better-sqlite3
|
|
64
|
+
// (SIGSEGV on Linux under Node's V8). When invoked via node on Linux, detect
|
|
65
|
+
// a Bun installation and re-exec this file under Bun so the bundle takes the
|
|
66
|
+
// safe path. No-op when already running under Bun or on non-Linux platforms.
|
|
67
|
+
if (typeof globalThis.Bun === "undefined" && process.platform === "linux") {
|
|
68
|
+
const bunCandidates = [
|
|
69
|
+
process.env.BUN_INSTALL ? join(process.env.BUN_INSTALL, "bin", "bun") : null,
|
|
70
|
+
join(homedir(), ".bun", "bin", "bun"),
|
|
71
|
+
"/usr/local/bin/bun",
|
|
72
|
+
"/usr/bin/bun",
|
|
73
|
+
].filter(Boolean);
|
|
74
|
+
const bunBin = bunCandidates.find((p) => existsSync(p));
|
|
75
|
+
if (bunBin) {
|
|
76
|
+
const child = spawn(bunBin, [fileURLToPath(import.meta.url)], {
|
|
77
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
78
|
+
env: process.env,
|
|
79
|
+
});
|
|
80
|
+
process.stdin.on("data", (chunk) => {
|
|
81
|
+
if (!child.stdin.destroyed) child.stdin.write(chunk);
|
|
82
|
+
});
|
|
83
|
+
process.stdin.on("end", () => {});
|
|
84
|
+
const _keepAlive = setInterval(() => {}, 2147483647);
|
|
85
|
+
child.on("exit", (code) => {
|
|
86
|
+
clearInterval(_keepAlive);
|
|
87
|
+
process.exit(code ?? 0);
|
|
88
|
+
});
|
|
89
|
+
// Prevent rest of start.mjs from running — child owns the MCP session.
|
|
90
|
+
process.stdin.resume();
|
|
91
|
+
await new Promise(() => {}); // park this process forever
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
44
95
|
// ── Self-heal Layer 1: Fix registry → symlink mismatches (anthropics/claude-code#46915) ──
|
|
45
96
|
// Claude Code auto-update can leave installed_plugins.json pointing to a non-existent
|
|
46
97
|
// directory. We detect this and create symlinks so hooks find the right path.
|
|
@@ -51,7 +102,8 @@ if (cacheMatch) {
|
|
|
51
102
|
try {
|
|
52
103
|
const cacheParent = cacheMatch[1];
|
|
53
104
|
const myVersion = cacheMatch[2];
|
|
54
|
-
const
|
|
105
|
+
const claudeConfigDir = resolveClaudeConfigDir();
|
|
106
|
+
const ipPath = resolve(claudeConfigDir, "plugins", "installed_plugins.json");
|
|
55
107
|
|
|
56
108
|
// Forward heal: if a newer version dir exists, update registry
|
|
57
109
|
const dirs = readdirSync(cacheParent).filter((d) =>
|
|
@@ -83,7 +135,7 @@ if (cacheMatch) {
|
|
|
83
135
|
}
|
|
84
136
|
|
|
85
137
|
// Reverse heal: if registry points to non-existent dir, create symlink to us
|
|
86
|
-
const cacheRoot = resolve(
|
|
138
|
+
const cacheRoot = resolve(claudeConfigDir, "plugins", "cache");
|
|
87
139
|
if (existsSync(ipPath)) {
|
|
88
140
|
const ip = JSON.parse(readFileSync(ipPath, "utf-8"));
|
|
89
141
|
for (const [key, entries] of Object.entries(ip.plugins || {})) {
|
|
@@ -125,9 +177,10 @@ try {
|
|
|
125
177
|
const { healInstalledPlugins, healSettingsEnabledPlugins, healPluginJsonMcpServers, healMcpJsonArgs } =
|
|
126
178
|
await import("./scripts/heal-installed-plugins.mjs");
|
|
127
179
|
const pluginKey = "context-mode@context-mode";
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
const
|
|
180
|
+
const claudeConfigDir = resolveClaudeConfigDir();
|
|
181
|
+
const registryPath = resolve(claudeConfigDir, "plugins", "installed_plugins.json");
|
|
182
|
+
const pluginCacheRoot = resolve(claudeConfigDir, "plugins", "cache");
|
|
183
|
+
const settingsPath = resolve(claudeConfigDir, "settings.json");
|
|
131
184
|
try { healInstalledPlugins({ registryPath, pluginCacheRoot, pluginKey }); }
|
|
132
185
|
catch { /* best effort */ }
|
|
133
186
|
// v1.0.116: Claude Code's plugin loader reads settings.json.enabledPlugins
|
|
@@ -191,7 +244,12 @@ try {
|
|
|
191
244
|
const { buildHookCommand, selfHealCacheHealHook, ensureShebangAndExecBit } =
|
|
192
245
|
await import("./hooks/cache-heal-utils.mjs");
|
|
193
246
|
|
|
194
|
-
|
|
247
|
+
// #577: honor $CLAUDE_CONFIG_DIR — without this, Claude Code spawns hooks
|
|
248
|
+
// from $CLAUDE_CONFIG_DIR/settings.json but we deploy them to ~/.claude/hooks/
|
|
249
|
+
// and register them in ~/.claude/settings.json. The mismatch silently
|
|
250
|
+
// disables the heal AND creates an unwanted ~/.claude directory.
|
|
251
|
+
const claudeConfigDir = resolveClaudeConfigDir();
|
|
252
|
+
const globalHooksDir = resolve(claudeConfigDir, "hooks");
|
|
195
253
|
const healHookPath = resolve(globalHooksDir, "context-mode-cache-heal.mjs");
|
|
196
254
|
// Clean up old bash version if it exists
|
|
197
255
|
const oldBashHook = resolve(globalHooksDir, "context-mode-cache-heal.sh");
|
|
@@ -203,14 +261,17 @@ try {
|
|
|
203
261
|
const healScript = `#!/usr/bin/env node
|
|
204
262
|
// context-mode plugin cache self-heal (auto-deployed)
|
|
205
263
|
// Fixes anthropics/claude-code#46915: auto-update breaks CLAUDE_PLUGIN_ROOT
|
|
264
|
+
// Honors CLAUDE_CONFIG_DIR (#577) — checked at this script's runtime so users
|
|
265
|
+
// who set CLAUDE_CONFIG_DIR after install still get healed correctly.
|
|
206
266
|
// Pure Node.js — no bash/shell dependency.
|
|
207
267
|
import{existsSync,readdirSync,statSync,symlinkSync,lstatSync,unlinkSync,readFileSync}from"node:fs";
|
|
208
268
|
import{dirname,join,resolve,sep}from"node:path";
|
|
209
269
|
import{homedir}from"node:os";
|
|
270
|
+
function cfgDir(){const e=process.env.CLAUDE_CONFIG_DIR;if(e&&e.trim()!==""){return e.startsWith("~")?resolve(homedir(),e.replace(/^~[/\\\\]?/,"")):resolve(e)}return resolve(homedir(),".claude")}
|
|
210
271
|
try{
|
|
211
|
-
const f=resolve(
|
|
272
|
+
const f=resolve(cfgDir(),"plugins","installed_plugins.json");
|
|
212
273
|
if(!existsSync(f))process.exit(0);
|
|
213
|
-
const cacheRoot=resolve(
|
|
274
|
+
const cacheRoot=resolve(cfgDir(),"plugins","cache");
|
|
214
275
|
const ip=JSON.parse(readFileSync(f,"utf-8"));
|
|
215
276
|
for(const[k,es]of Object.entries(ip.plugins||{})){
|
|
216
277
|
if(k!=="context-mode@context-mode")continue;
|
|
@@ -238,8 +299,9 @@ try{
|
|
|
238
299
|
try { ensureShebangAndExecBit(healHookPath); } catch { /* best effort */ }
|
|
239
300
|
}
|
|
240
301
|
|
|
241
|
-
// Register the hook in
|
|
242
|
-
|
|
302
|
+
// Register the hook in $CLAUDE_CONFIG_DIR/settings.json (Claude Code doesn't auto-discover hook files).
|
|
303
|
+
// #577: must follow the same dir resolution as globalHooksDir above.
|
|
304
|
+
const settingsPath = resolve(claudeConfigDir, "settings.json");
|
|
243
305
|
if (existsSync(settingsPath)) {
|
|
244
306
|
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
245
307
|
const hooks = settings.hooks ?? {};
|