context-mode 1.0.136 → 1.0.138

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 (46) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +9 -24
  7. package/build/adapters/codex/index.js +24 -3
  8. package/build/adapters/jetbrains-copilot/hooks.d.ts +11 -3
  9. package/build/adapters/jetbrains-copilot/hooks.js +11 -7
  10. package/build/adapters/opencode/index.d.ts +1 -0
  11. package/build/adapters/opencode/index.js +25 -0
  12. package/build/adapters/opencode/plugin.d.ts +22 -0
  13. package/build/adapters/opencode/plugin.js +52 -0
  14. package/build/adapters/pi/extension.js +20 -4
  15. package/build/adapters/pi/mcp-bridge.d.ts +2 -1
  16. package/build/adapters/pi/mcp-bridge.js +49 -3
  17. package/build/adapters/vscode-copilot/hooks.d.ts +27 -3
  18. package/build/adapters/vscode-copilot/hooks.js +27 -12
  19. package/build/cli.js +199 -32
  20. package/build/lifecycle.d.ts +2 -51
  21. package/build/lifecycle.js +3 -67
  22. package/build/openclaw-plugin.d.ts +130 -0
  23. package/build/openclaw-plugin.js +626 -0
  24. package/build/opencode-plugin.d.ts +122 -0
  25. package/build/opencode-plugin.js +372 -0
  26. package/build/pi-extension.d.ts +14 -0
  27. package/build/pi-extension.js +451 -0
  28. package/build/server.d.ts +19 -0
  29. package/build/server.js +145 -59
  30. package/build/session/db.d.ts +6 -0
  31. package/build/session/db.js +17 -3
  32. package/build/util/db-lock.d.ts +65 -0
  33. package/build/util/db-lock.js +166 -0
  34. package/build/util/sibling-mcp.d.ts +0 -40
  35. package/build/util/sibling-mcp.js +11 -116
  36. package/cli.bundle.mjs +181 -166
  37. package/configs/kilo/kilo.json +0 -11
  38. package/configs/opencode/opencode.json +0 -11
  39. package/hooks/normalize-hooks.mjs +101 -19
  40. package/hooks/session-db.bundle.mjs +3 -3
  41. package/openclaw.plugin.json +1 -1
  42. package/package.json +1 -1
  43. package/scripts/heal-installed-plugins.mjs +115 -1
  44. package/scripts/postinstall.mjs +16 -18
  45. package/server.bundle.mjs +112 -110
  46. package/start.mjs +11 -14
@@ -26,70 +26,19 @@
26
26
  * cross-platform without spawning real processes.
27
27
  */
28
28
  import { execFileSync } from "node:child_process";
29
- // Match every shape an installed context-mode MCP server can take in argv:
30
- //
31
- // 1. `~/.claude/plugins/cache/context-mode/context-mode/<v>/start.mjs`
32
- // and `~/.claude/plugins/marketplaces/context-mode/start.mjs` Claude
33
- // Code plugin shapes (original #559 case).
34
- // 2. `<prefix>/node_modules/context-mode/start.mjs` or `.../server.bundle.mjs`
35
- // — npm-global, marketplace, manual installs.
36
- // 3. `<prefix>/bin/context-mode` — npm-global bin shim. This is the argv
37
- // OpenCode + KiloCode see when they spawn `mcp.command = ["context-mode"]`.
38
- // Without this entry, sibling discovery missed the 26-child / 1.6 GB
39
- // RSS accumulation reported in #565.
40
- // 4. `bun .../context-mode/server.bundle.mjs` — Pi/Bun hosts.
41
- //
42
- // All four can be alive concurrently — VERDICT R1 dump confirmed multi-version
43
- // coexistence on real macOS, and #565 confirmed concurrent OpenCode children
44
- // alongside Claude Code children on Linux.
45
- const POSIX_PGREP_PATTERN = "(node|bun).*(plugins/(cache|marketplaces)/.*context-mode.*start\\.mjs" +
46
- "|context-mode/start\\.mjs" +
47
- "|context-mode/server\\.bundle\\.mjs" +
48
- "|bin/context-mode($|[^a-zA-Z0-9_-]))";
29
+ // Match BOTH `~/.claude/plugins/cache/context-mode/context-mode/<v>/start.mjs`
30
+ // AND `~/.claude/plugins/marketplaces/context-mode/start.mjs` shapes.
31
+ // Both can be alive concurrently — VERDICT R1 dump confirmed all four
32
+ // PIDs simultaneously across three different versions on a real Mac.
33
+ const POSIX_PGREP_PATTERN = "node.*plugins/(cache|marketplaces)/.*context-mode.*start\\.mjs";
49
34
  // Windows: PowerShell + Get-CimInstance (wmic deprecated since Win11 22H2).
50
- // Filter includes both node.exe and bun.exe (Pi/Bun hosts). CommandLine
51
- // matching covers the same four install shapes as POSIX_PGREP_PATTERN.
35
+ // Filter on CommandLine because Win32_Process.Name is just "node.exe".
36
+ // Two backslashes inside `start\.mjs` are needed because the Like operator
37
+ // uses regex-ish escaping at the JS layer.
52
38
  const WIN_PS_SCRIPT = "Get-CimInstance Win32_Process " +
53
- "-Filter \"Name='node.exe' OR Name='bun.exe'\" | " +
54
- "Where-Object { $_.CommandLine -match " +
55
- "'plugins[\\\\/](cache|marketplaces)[\\\\/].*context-mode.*start\\.mjs" +
56
- "|context-mode[\\\\/]start\\.mjs" +
57
- "|context-mode[\\\\/]server\\.bundle\\.mjs" +
58
- "|bin[\\\\/]context-mode($|[^a-zA-Z0-9_-])' } | " +
39
+ "-Filter \"Name='node.exe'\" | " +
40
+ "Where-Object { $_.CommandLine -match 'plugins[\\\\/](cache|marketplaces)[\\\\/].*context-mode.*start\\.mjs' } | " +
59
41
  "Select-Object -ExpandProperty ProcessId";
60
- /** POSIX ppid probe — empty / NaN on failure. */
61
- function readPpidPosix(pid) {
62
- try {
63
- const out = execFileSync("ps", ["-o", "ppid=", "-p", String(pid)], {
64
- encoding: "utf-8",
65
- timeout: 2000,
66
- stdio: ["ignore", "pipe", "ignore"],
67
- }).trim();
68
- const n = Number.parseInt(out, 10);
69
- return Number.isFinite(n) ? n : NaN;
70
- }
71
- catch {
72
- return NaN;
73
- }
74
- }
75
- /** Windows ppid probe — empty / NaN on failure. */
76
- function readPpidWin32(pid) {
77
- try {
78
- const out = execFileSync("powershell", [
79
- "-NoProfile",
80
- "-Command",
81
- `(Get-CimInstance Win32_Process -Filter "ProcessId=${pid}").ParentProcessId`,
82
- ], { encoding: "utf-8", timeout: 2000, stdio: ["ignore", "pipe", "ignore"] }).trim();
83
- const n = Number.parseInt(out, 10);
84
- return Number.isFinite(n) ? n : NaN;
85
- }
86
- catch {
87
- return NaN;
88
- }
89
- }
90
- function defaultReadPpid(pid) {
91
- return process.platform === "win32" ? readPpidWin32(pid) : readPpidPosix(pid);
92
- }
93
42
  const defaultRun = (cmd, args) => execFileSync(cmd, [...args], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
94
43
  const defaultIsAlive = (pid) => {
95
44
  try {
@@ -145,18 +94,7 @@ export function discoverSiblingMcpPids(opts) {
145
94
  catch {
146
95
  return [];
147
96
  }
148
- const candidates = parsePidList(stdout).filter((pid) => pid !== opts.ownPid && pid !== opts.ownPpid);
149
- if (!opts.sameParentOnly)
150
- return candidates;
151
- // Startup-sweep mode (#565): only reap siblings sharing OUR ppid. This
152
- // prevents an opencode-spawned MCP child from killing a Claude Code MCP
153
- // child (or another concurrent opencode host's children) when both are
154
- // present on the same machine.
155
- const readPpid = opts.readPpid ?? defaultReadPpid;
156
- return candidates.filter((pid) => {
157
- const ppid = readPpid(pid);
158
- return Number.isFinite(ppid) && ppid === opts.ownPpid;
159
- });
97
+ return parsePidList(stdout).filter((pid) => pid !== opts.ownPid && pid !== opts.ownPpid);
160
98
  }
161
99
  /** Sleep helper — Promise-based for use inside the kill polling loop. */
162
100
  function delay(ms) {
@@ -241,46 +179,3 @@ export async function killSiblingMcpServers(opts) {
241
179
  totalKilled: terminatedBySigterm + terminatedBySigkill,
242
180
  };
243
181
  }
244
- /**
245
- * Startup-time sibling sweep (#565).
246
- *
247
- * Discovers any context-mode MCP server pids that share OUR parent process
248
- * (i.e. other MCP children of the same host like `opencode serve`) and
249
- * terminates them. The intent is "exactly one MCP child per host" — when a
250
- * new MCP client spawns inside an opencode host that already has 25 stale
251
- * idle siblings, this sweep reclaims them at boot rather than waiting for
252
- * the idle timeout to fire on each one independently.
253
- *
254
- * Gated by env (default-on but easy to disable):
255
- *
256
- * CONTEXT_MODE_STARTUP_SWEEP=0 → disabled
257
- * CONTEXT_MODE_STARTUP_SWEEP=1 → enabled (default)
258
- *
259
- * Safety:
260
- * - `sameParentOnly: true` — never touches MCP children of a different host.
261
- * - Best-effort throughout: failures never block server startup.
262
- * - Composes with the idle-timeout path: if a sibling is actively in use
263
- * by another session, the parent process will simply spawn a new MCP
264
- * child on its next request. The cost is one cold-start (~1–3 s) for
265
- * that session, which is identical to opencode's existing behaviour
266
- * of spawning a fresh MCP child per session anyway.
267
- */
268
- export async function startupSiblingSweep(env = process.env) {
269
- const empty = { terminatedBySigterm: 0, terminatedBySigkill: 0, totalKilled: 0 };
270
- const raw = env.CONTEXT_MODE_STARTUP_SWEEP;
271
- if (raw === "0" || raw === "false")
272
- return empty;
273
- try {
274
- const pids = discoverSiblingMcpPids({
275
- ownPid: process.pid,
276
- ownPpid: process.ppid,
277
- sameParentOnly: true,
278
- });
279
- if (pids.length === 0)
280
- return empty;
281
- return await killSiblingMcpServers({ pids });
282
- }
283
- catch {
284
- return empty;
285
- }
286
- }