github mksglu/context-mode v1.0.122

latest release: v1.0.123
4 hours ago

v1.0.122

Closes 6 community-reported issues and 2 contributed PRs across MCP routing, Windows DEP0190, language-agnostic session extraction, conda Python, Pi lifecycle, and the .mcp.json architectural cleanup that has been bouncing between two contradictory forms since v1.0.108. Every fix is verified against actual source — Codex Rust references, Node.js deprecation docs, Pi extension API, and live runtime probes. No LLM hallucination.

What broke

#531.mcp.json ships ./start.mjs, breaking every fresh marketplace install

Traced via git log --follow .mcp.json: the source template flip-flopped 3 times between ${CLAUDE_PLUGIN_ROOT}/start.mjs (correct for end-users) and ./start.mjs (suppresses a dev-time warning for contributors). The most recent regression — commit aea633c (PR #253, 2026-04-13) — assumed "Plugin install path is unaffected — it still uses .claude-plugin/plugin.json". Aragorn2046's report proves that assumption wrong: Claude Code DOES read .mcp.json from the plugin cache, and the relative form throws MODULE_NOT_FOUND on every ctx_* tool because the MCP child inherits session CWD (not pluginRoot).

The fix is architectural — stop letting one file serve two contradictory roles:

  • git rm --cached .mcp.json (added to .gitignore — can never regress)
  • Removed from package.json files[] — never ships in the npm tarball
  • New .mcp.json.example is the canonical template; contributors copy it locally
  • scripts/heal-installed-plugins.mjs healMcpJsonArgs() mirrors v1.0.119 healPluginJsonMcpServers() exactly — heals legacy poisoned configs on every MCP boot via start.mjs Layer 5b, on /ctx-upgrade via cli.ts post-bump assertion, and on npm install -g via postinstall.mjs
  • New scripts/assert-asymmetric-drift.mjs is wired into the build chain: asserts .mcp.json.example and .claude-plugin/plugin.json mcpServers args agree on the placeholder. CI fails loud if the two ever diverge again.

Architectural review across all 15 adapters confirms zero of them read repo-root .mcp.json at runtime — every host uses its own platform-specific path (.cursor/mcp.json, .vscode/mcp.json, ~/.kiro/settings/mcp.json, ~/.omp/agent/mcp.json, .codex-plugin/mcp.json).

#529 — External MCP servers (slack, telegram, gdrive, notion, …) flood context unrouted

Across the 15 adapters that support hooks, only Bash, WebFetch, Read, Grep, Agent, and context-mode's own ctx_* tools fired the PreToolUse context-guidance nudge. Every other MCP namespace — Slack channel history, Telegram messages, Google Drive file content — landed in the model's context window unfiltered.

PR #532 (by @ousamabenyounes) fixed Claude Code with mcp__(?!plugin_context-mode_) negative lookahead. v1.0.122 extends the fix to the remaining 5 adapters that face the same problem with their own MCP naming conventions:

  • codex: mcp__(?!.*context-mode)
  • gemini-cli: same mcp__ shape, same exclusion
  • qwen-code: Gemini fork — same shape
  • cursor: MCP:(?!ctx_) — different prefix
  • kiro: @(?!context-mode/) — namespace prefix

hooks/core/routing.mjs isExternalMcpTool() now recognises all three wire shapes (mcp__, MCP:, @<server>/). Per-adapter constants EXTERNAL_MCP_MATCHER_PATTERN are kept in sync via README-mirrored tests.

Five adapters that don't need the fix: vscode-copilot, jetbrains-copilot (empty matcher already catches all), opencode, openclaw, omp (plugin paradigm intercepts every call), pi (bridge-mediated, no PreToolUse). Zed and antigravity have no hook surface — MCP-only, can't fix at hook layer.

#537 — DEP0190 deprecation on Windows

Node.js 23.11/24 deprecates child_process.execFile/spawn with shell: true AND args-array form ("DEP0190" warning, becomes runtime error in v24+). Three call sites tripped this on every MCP boot on Windows:

  • runtime.ts runnableExists()
  • runtime.ts getVersion()
  • executor.ts #spawn() (tsx/ts-node/elixir/bun shell paths)

PR #537 by @frhino collapses the args array into a single quoted shell string when shell: true is needed. Windows-shell-safety architect verified arg sources at all 3 sites are internally controlled (hardcoded runtime names + temp-script paths via mkdtempSync) — zero user-controlled values. The quote predicate was widened from /\s/ to /[\s"&|<>^()%!]/ to cover cmd.exe metacharacters per the architect's recommendation.

tests/runtime.test.ts mocks updated to assert the new execSync('"<cmd>" --version') Windows path instead of the legacy execFileSync(cmd, ["--version"], { shell: true }) shape.

#533 — better-sqlite3 build fails when conda python3 is first in PATH

@rpickmans isolated this cleanly: PYTHON=/usr/bin/python3 npm install better-sqlite3@12.9.0 succeeds in 18s on macOS arm64 + Node 26; without the env override, conda's python3 wins, node-gyp picks it up via find-python.js PATH fallback, and the source build aborts.

scripts/heal-better-sqlite3.mjs now:

  • Probes /usr/bin/python3 on darwin, filters paths matching /opt/anaconda*, /opt/miniconda*, */conda/*, $CONDA_PREFIX/*
  • Builds child env with PYTHON=<safe> AND npm_config_python=<safe> (node-gyp slot 1+2 beat slot 3 PATH per find-python.js resolution order)
  • Strips CONDA_PREFIX/CONDA_DEFAULT_ENV/CONDA_EXE/CONDA_PROMPT_MODIFIER/CONDA_SHLVL from child env
  • Applies the same fix to both the execFileSync (package-missing branch from #514) and the Layer A spawnSync (binding-missing branch)
  • Emits a stderr breadcrumb naming the chosen PYTHON when conda detected and overridden
  • Returns reason python-conda-blocked when no safe python is available

#534pi --help spawns orphaned MCP servers spinning at 100% CPU

Triage confirmed this is NOT a v1.0.121 regression — git log v1.0.120..v1.0.121 -- src/adapters/pi/ is empty. It is a pre-existing lifecycle defect (#311 and #388 were "fixed" without truly fixing). src/adapters/pi/extension.ts:613 unconditionally called bootstrapMCPTools() on every Pi invocation including short-lived pi --help runs. Pi exits in ~50ms; the spawned MCP child gets reparented to systemd --user, stdin pipe stays half-open, and the MCP SDK's StdioServerTransport loops on stdin reads → 100% CPU on a pegged core, ignores SIGTERM, multi-hour orphans accumulate.

Three layers of defense in v1.0.122:

  1. Prevention — short-circuit guard in piExtension() reads process.argv for --help|-h|--version|-v|help (verified against refs/platforms/oh-my-pi/packages/coding-agent/src/cli.ts short-circuit set); on match, returns no-op handle without spawning
  2. Fast recovery — bridge child polls process.ppid !== originalPpid every 1s; SIGKILLs self when orphaned (gated on CONTEXT_MODE_BRIDGE_DEPTH > 0 to avoid #236 regressions)
  3. Stdin EOF handlersrc/server.ts lifecycle entry wires process.stdin.on("end", () => process.exit(0)) so parent pipe close terminates cleanly without relying on the MCP SDK loop

Integration test simulates pi --help end-to-end and asserts zero context-mode children survive parent exit.

#535 — Session extraction was English+Turkish only; the language coverage problem

PR #538 (an earlier attempt that landed CJK keyword arrays) was reverted because the architectural approach — hand-curated keyword arrays per language — cannot scale to the thousands of human languages. Adding Chinese needed 5 pattern arrays; the next user reports Arabic, Russian, Spanish, Hindi, Japanese, Indonesian; the maintenance burden grows linearly with every release.

Replacement: language-agnostic universal-rule detectors based on Unicode/structural patterns instead of word lists. Verified against parallel research into Aider, Cline, OpenHands session-resume architectures + architectural deep-dive.

  • intent: investigate — Unicode question mark family [??؟¿]/u (ASCII U+003F, fullwidth U+FF1F, Arabic U+061F, Spanish opening U+00BF). Greek ; U+037E and Armenian ՞ excluded — they collide with ASCII.
  • intent: implement — codepoint length < 60, no question mark, at least one \p{L} letter
  • decision — clause separator [,;,;、،]/u + codepoint length 15..500 + no question + letters present
  • role — first sentence (split on .!。!\n) of length 8..120 with two space-separated \p{L}+ tokens OR a \p{L}{6,} letter run (CJK fallback). Real-world persona prompts that append context paragraphs still match because we check the FIRST CLAUSE only.
  • blocker — programming-domain error markers (Error:, Exception:, Traceback, JS/Java stack-frame at <id>(...):line:col)
  • blocker_resolved — Unicode checkmark family [✓✔✅☑🎉]/u OR ^(fixed|resolved)\s*: prefix

Safety net: new <recent_user_messages count="N"> block in snapshot.ts ships the last 3 raw user prompts verbatim (400-codepoint truncate each). Even if every structural detector misses, the next session's LLM sees the actual user messages and interprets them itself.

87 multilang tests pass across English, Turkish, Chinese, Arabic, Russian, Spanish, Hindi, Japanese × 5 categories. The new detectors meet or beat the old keyword recall for English/Turkish AND extend to the other 6 languages without per-language code.

Tests

  • 3094 pass · 8 pre-existing baseline failures (OpenCodeAdapter config-paths — verified present on origin/next at v1.0.121, unrelated to this changeset)
  • 87 new multilang fixtures (tests/session/extract-multilang.test.ts)
  • 22 new external-MCP routing tests across 5 adapters
  • 10 new .mcp.json heal slices + CI invariant
  • 14 new tests across Pi lifecycle, conda Python, Windows DEP0190
  • Architectural invariant CI: scripts/assert-asymmetric-drift.mjs blocks any future .mcp.json/plugin.json drift

Compatibility

15 adapters / 3 OS. The CJK keyword arrays from the reverted PR #538 do NOT ship — replaced with language-agnostic structural detection. Codex marketplace discovery (v1.0.121) continues to work. Windows DEP0190 warnings eliminated; CI now enforces the bundle-invariant guardrail on all 3 OS.

Upgrade

npm install -g context-mode@latest
# inside Claude Code:
/ctx-upgrade
# restart your session

If you were on a v1.0.108-v1.0.121 install with a poisoned .mcp.json in the plugin cache (every fresh marketplace install since #253), the boot heal in start.mjs Layer 5b auto-repairs the manifest on next MCP boot. No manual cleanup required.

Thanks

@Aragorn2046 for the staff-grade .mcp.json diagnosis (#531) — citing start.mjs:10, both prior issues (#411, #523), and even the #523 closing-comment connection that made the architectural fix obvious. @ousamabenyounes for the well-scoped #529 fix that unblocked the 15-adapter extension. @frhino for the Node.js docs verification + before/after stderr byte-count comparison on #537. @rpickmans for the cleanest reproducer of the cycle (#533) — PYTHON=/usr/bin/python3 isolating the variable end-to-end. @gebeer for the #534 reproduction with PID/PPID/CPU evidence. @happy9527 and @maxwell-orange for surfacing the language-coverage problem in #535 that drove the structural-detector rewrite. Every report this cycle came with file:line citations and decoded environment details — that's what makes mechanical fixes possible.

Don't miss a new context-mode release

NewReleases is sending notifications on new releases.