context-mode 1.0.74 → 1.0.76

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 (48) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +33 -2
  7. package/build/adapters/claude-code/hooks.d.ts +11 -1
  8. package/build/adapters/claude-code/hooks.js +31 -1
  9. package/build/cli.js +1 -0
  10. package/build/db-base.d.ts +32 -1
  11. package/build/db-base.js +162 -10
  12. package/build/lifecycle.d.ts +5 -2
  13. package/build/lifecycle.js +4 -11
  14. package/build/server.js +51 -26
  15. package/build/session/db.js +1 -1
  16. package/build/store.js +25 -3
  17. package/cli.bundle.mjs +93 -93
  18. package/configs/antigravity/GEMINI.md +1 -1
  19. package/configs/claude-code/CLAUDE.md +1 -1
  20. package/configs/codex/AGENTS.md +1 -1
  21. package/configs/gemini-cli/GEMINI.md +1 -1
  22. package/configs/kilo/AGENTS.md +1 -1
  23. package/configs/kiro/KIRO.md +1 -1
  24. package/configs/openclaw/AGENTS.md +1 -1
  25. package/configs/opencode/AGENTS.md +1 -1
  26. package/configs/pi/AGENTS.md +1 -1
  27. package/configs/vscode-copilot/copilot-instructions.md +1 -1
  28. package/configs/zed/AGENTS.md +1 -1
  29. package/hooks/codex/posttooluse.mjs +6 -5
  30. package/hooks/codex/sessionstart.mjs +5 -5
  31. package/hooks/core/mcp-ready.mjs +31 -0
  32. package/hooks/core/routing.mjs +27 -12
  33. package/hooks/cursor/posttooluse.mjs +6 -5
  34. package/hooks/cursor/sessionstart.mjs +5 -5
  35. package/hooks/cursor/stop.mjs +5 -4
  36. package/hooks/ensure-deps.mjs +38 -27
  37. package/hooks/gemini-cli/aftertool.mjs +5 -4
  38. package/hooks/gemini-cli/precompress.mjs +5 -4
  39. package/hooks/gemini-cli/sessionstart.mjs +5 -4
  40. package/hooks/hooks.json +13 -22
  41. package/hooks/kiro/posttooluse.mjs +6 -5
  42. package/hooks/routing-block.mjs +6 -2
  43. package/hooks/session-db.bundle.mjs +12 -12
  44. package/hooks/vscode-copilot/sessionstart.mjs +6 -2
  45. package/openclaw.plugin.json +1 -1
  46. package/package.json +1 -1
  47. package/server.bundle.mjs +73 -73
  48. package/.claude-plugin/hooks/hooks.json +0 -132
package/build/server.js CHANGED
@@ -1602,38 +1602,51 @@ server.registerTool("ctx_doctor", {
1602
1602
  else {
1603
1603
  lines.push("- [-] Performance: NORMAL — install Bun for 3-5x speed boost");
1604
1604
  }
1605
- // Server test
1606
- try {
1605
+ // Server test — cleanup executor to prevent resource leaks (#247)
1606
+ {
1607
1607
  const testExecutor = new PolyglotExecutor({ runtimes });
1608
- const result = await testExecutor.execute({ language: "javascript", code: 'console.log("ok");', timeout: 5000 });
1609
- if (result.exitCode === 0 && result.stdout.trim() === "ok") {
1610
- lines.push("- [x] Server test: PASS");
1608
+ try {
1609
+ const result = await testExecutor.execute({ language: "javascript", code: 'console.log("ok");', timeout: 5000 });
1610
+ if (result.exitCode === 0 && result.stdout.trim() === "ok") {
1611
+ lines.push("- [x] Server test: PASS");
1612
+ }
1613
+ else {
1614
+ const detail = result.stderr?.trim() ? ` (${result.stderr.trim().slice(0, 200)})` : "";
1615
+ lines.push(`- [ ] Server test: FAIL — exit ${result.exitCode}${detail}`);
1616
+ }
1611
1617
  }
1612
- else {
1613
- const detail = result.stderr?.trim() ? ` (${result.stderr.trim().slice(0, 200)})` : "";
1614
- lines.push(`- [ ] Server test: FAIL — exit ${result.exitCode}${detail}`);
1618
+ catch (err) {
1619
+ lines.push(`- [ ] Server test: FAIL ${err instanceof Error ? err.message : err}`);
1620
+ }
1621
+ finally {
1622
+ testExecutor.cleanupBackgrounded();
1615
1623
  }
1616
1624
  }
1617
- catch (err) {
1618
- lines.push(`- [ ] Server test: FAIL — ${err instanceof Error ? err.message : err}`);
1619
- }
1620
- // FTS5 / SQLite
1621
- try {
1622
- const Database = loadDatabase();
1623
- const db = new Database(":memory:");
1624
- db.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)");
1625
- db.exec("INSERT INTO fts_test(content) VALUES ('hello world')");
1626
- const row = db.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();
1627
- db.close();
1628
- if (row && row.content === "hello world") {
1629
- lines.push("- [x] FTS5 / SQLite: PASS — native module works");
1625
+ // FTS5 / SQLite — close in finally to prevent GC segfault (#247)
1626
+ {
1627
+ let testDb;
1628
+ try {
1629
+ const Database = loadDatabase();
1630
+ testDb = new Database(":memory:");
1631
+ testDb.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)");
1632
+ testDb.exec("INSERT INTO fts_test(content) VALUES ('hello world')");
1633
+ const row = testDb.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();
1634
+ if (row && row.content === "hello world") {
1635
+ lines.push("- [x] FTS5 / SQLite: PASS — native module works");
1636
+ }
1637
+ else {
1638
+ lines.push("- [ ] FTS5 / SQLite: FAIL — unexpected result");
1639
+ }
1630
1640
  }
1631
- else {
1632
- lines.push("- [ ] FTS5 / SQLite: FAIL — unexpected result");
1641
+ catch (err) {
1642
+ lines.push(`- [ ] FTS5 / SQLite: FAIL — ${err instanceof Error ? err.message : err}`);
1643
+ }
1644
+ finally {
1645
+ try {
1646
+ testDb?.close();
1647
+ }
1648
+ catch { /* best effort */ }
1633
1649
  }
1634
- }
1635
- catch (err) {
1636
- lines.push(`- [ ] FTS5 / SQLite: FAIL — ${err instanceof Error ? err.message : err}`);
1637
1650
  }
1638
1651
  // Hook script
1639
1652
  const hookPath = resolve(pluginRoot, "hooks", "pretooluse.mjs");
@@ -1849,6 +1862,8 @@ async function main() {
1849
1862
  if (cleaned > 0) {
1850
1863
  console.error(`Cleaned up ${cleaned} stale DB file(s) from previous sessions`);
1851
1864
  }
1865
+ // MCP readiness sentinel path (#230)
1866
+ const mcpSentinel = join(tmpdir(), `context-mode-mcp-ready-${process.ppid}`);
1852
1867
  // Clean up own DB + backgrounded processes + preload script on shutdown
1853
1868
  const shutdown = () => {
1854
1869
  executor.cleanupBackgrounded();
@@ -1858,6 +1873,11 @@ async function main() {
1858
1873
  unlinkSync(CM_FS_PRELOAD);
1859
1874
  }
1860
1875
  catch { /* best effort */ }
1876
+ // Remove MCP readiness sentinel (#230)
1877
+ try {
1878
+ unlinkSync(mcpSentinel);
1879
+ }
1880
+ catch { /* best effort */ }
1861
1881
  };
1862
1882
  const gracefulShutdown = async () => {
1863
1883
  shutdown();
@@ -1870,6 +1890,11 @@ async function main() {
1870
1890
  startLifecycleGuard({ onShutdown: () => gracefulShutdown() });
1871
1891
  const transport = new StdioServerTransport();
1872
1892
  await server.connect(transport);
1893
+ // Write MCP readiness sentinel (#230)
1894
+ try {
1895
+ writeFileSync(mcpSentinel, String(process.pid));
1896
+ }
1897
+ catch { /* best effort */ }
1873
1898
  // Detect platform adapter — stored for platform-aware session paths
1874
1899
  try {
1875
1900
  const { detectPlatform, getAdapter } = await import("./adapters/detect.js");
@@ -225,7 +225,7 @@ export class SessionDB extends SQLiteBase {
225
225
  // Update meta if session exists
226
226
  this.stmt(S.updateMetaLastEvent).run(sessionId);
227
227
  });
228
- transaction();
228
+ this.withRetry(() => transaction());
229
229
  }
230
230
  /**
231
231
  * Retrieve events for a session with optional filtering.
package/build/store.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * Use for documentation, API references, and any content where
8
8
  * you need EXACT text later — not summaries.
9
9
  */
10
- import { loadDatabase, applyWALPragmas, closeDB, withRetry } from "./db-base.js";
10
+ import { loadDatabase, applyWALPragmas, closeDB, cleanOrphanedWALFiles, withRetry, deleteDBFiles, isSQLiteCorruptionError } from "./db-base.js";
11
11
  import { readFileSync, readdirSync, unlinkSync, existsSync, statSync } from "node:fs";
12
12
  import { tmpdir } from "node:os";
13
13
  import { join } from "node:path";
@@ -267,8 +267,30 @@ export class ContentStore {
267
267
  const Database = loadDatabase();
268
268
  this.#dbPath =
269
269
  dbPath ?? join(tmpdir(), `context-mode-${process.pid}.db`);
270
- this.#db = new Database(this.#dbPath, { timeout: 30000 });
271
- applyWALPragmas(this.#db);
270
+ cleanOrphanedWALFiles(this.#dbPath);
271
+ let db;
272
+ try {
273
+ db = new Database(this.#dbPath, { timeout: 30000 });
274
+ applyWALPragmas(db);
275
+ }
276
+ catch (err) {
277
+ const msg = err instanceof Error ? err.message : String(err);
278
+ if (isSQLiteCorruptionError(msg)) {
279
+ deleteDBFiles(this.#dbPath);
280
+ cleanOrphanedWALFiles(this.#dbPath);
281
+ try {
282
+ db = new Database(this.#dbPath, { timeout: 30000 });
283
+ applyWALPragmas(db);
284
+ }
285
+ catch (retryErr) {
286
+ throw new Error(`Failed to create fresh DB after deleting corrupt file: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`);
287
+ }
288
+ }
289
+ else {
290
+ throw err;
291
+ }
292
+ }
293
+ this.#db = db;
272
294
  this.#initSchema();
273
295
  this.#prepareStatements();
274
296
  }