context-mode 1.0.143 → 1.0.145

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.
@@ -127,7 +127,15 @@ await runHook(async () => {
127
127
  // Extract the script filename (e.g., sessionstart.mjs, pretooluse.mjs)
128
128
  const scriptMatch = h.command.match(/([a-z]+\.mjs)\s*"?\s*$/);
129
129
  if (scriptMatch) {
130
- h.command = "node " + resolve(targetDir, "hooks", scriptMatch[1]);
130
+ // Issue #636: quote the script path so spaces in targetDir
131
+ // (e.g. Dropbox/iCloud display names like "Lucas Werneck",
132
+ // or CLAUDE_CONFIG_DIR pointed at a synced spaced folder)
133
+ // don't break /bin/sh's word-splitting at hook-spawn time.
134
+ // JSON.stringify is sufficient on Unix and safe on Windows
135
+ // (backslashes get escaped — Claude Code's hook layer
136
+ // normalizes to POSIX on Windows anyway via toHookPath).
137
+ const scriptPath = resolve(targetDir, "hooks", scriptMatch[1]);
138
+ h.command = `node ${JSON.stringify(scriptPath)}`;
131
139
  changed = true;
132
140
  }
133
141
  }
@@ -41,7 +41,7 @@ await runHook(async () => {
41
41
  const { createSessionLoaders } = await import("./session-loaders.mjs");
42
42
  const { join, dirname } = await import("node:path");
43
43
  const { fileURLToPath } = await import("node:url");
44
- const { readFileSync, unlinkSync, readdirSync, rmSync, statSync } = await import("node:fs");
44
+ const { readFileSync, unlinkSync, readdirSync, rmSync, lstatSync } = await import("node:fs");
45
45
 
46
46
  const detectedPlatform = detectPlatformFromEnv();
47
47
  const toolNamer = createToolNamer(detectedPlatform);
@@ -234,6 +234,12 @@ await runHook(async () => {
234
234
 
235
235
  // Age-gated lazy cleanup of old plugin cache version dirs (#181).
236
236
  // Only delete dirs older than 1 hour to avoid breaking active sessions.
237
+ // Use lstatSync (not statSync) so a fresh symlink whose target happens
238
+ // to be old is evaluated against the symlink's own mtime, not the
239
+ // target's — otherwise self-heal hooks that re-create breadcrumb
240
+ // symlinks for previous cache versions would be wiped out and any
241
+ // session pinned to one of those versions would lose its plugin root
242
+ // mid-flight (#644).
237
243
  try {
238
244
  const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
239
245
  if (pluginRoot) {
@@ -246,7 +252,7 @@ await runHook(async () => {
246
252
  for (const d of readdirSync(cacheParent)) {
247
253
  if (d === myDir) continue;
248
254
  try {
249
- const st = statSync(join(cacheParent, d));
255
+ const st = lstatSync(join(cacheParent, d));
250
256
  if (now - st.mtimeMs > ONE_HOUR) {
251
257
  rmSync(join(cacheParent, d), { recursive: true, force: true });
252
258
  }
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.143",
6
+ "version": "1.0.145",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.143",
3
+ "version": "1.0.145",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",