v1.0.138
Single coordinated fix for three issues that share the same architectural family — paths from a specific install moment getting baked into files that outlive that moment. Plus two system-level guards so this class of bug stops shipping silently.
The architectural fix
Three issues, same root cause: persisted files were writing absolute paths that became stale across upgrades or leaked outside their owner machine. Past releases patched each surface as it surfaced. v1.0.138 fixes the underlying contract.
Persistence tiers (new invariant):
- Tier A — plugin cache dirs (
~/.claude/plugins/cache/...): absolute paths OK if a heal pass owns them. - Tier B — user home configs (
~/.kiro/...,~/.claude.json): absolute paths OK if a heal pass owns them. - Tier C — workspace-committed files (
.github/hooks/,.cursor/hooks.json): MUST use portable commands. These get committed to git, shared with teammates, and run on CI under different paths.
Fixes
.mcp.json stale-write loop closed (#609). v1.0.135 wrote a fresh ~/.claude/plugins/cache/context-mode/context-mode/<version>/.mcp.json with an absolute path on every install. The next upgrade left the previous version's file behind, and Claude Code's MCP loader sometimes picked the wrong one — command not found: /...../1.0.135/start.mjs after upgrading to 1.0.137. v1.0.138 stops writing the file (the canonical plugin.json.mcpServers block with ${CLAUDE_PLUGIN_ROOT} placeholder is what Claude Code reads first), and sweeps any leftover .mcp.json files from prior version dirs on next upgrade. Closes #609.
Hook normalization no longer one-way (#604). hooks/normalize-hooks.mjs only ran while the ${CLAUDE_PLUGIN_ROOT} placeholder was still present. Once the v1 boot consumed the placeholder into an absolute path, the v2 boot saw a "regular" file with no placeholder and short-circuited — never healing back. v1.0.138 detects stale absolute paths pointing at non-current version dirs and rewrites them. Linux + Windows users (macOS skipped the normalizer entirely) are now self-healing across upgrades. Reporter @jowch's bisect and unit test caught this exactly. Closes #604.
VS Code Copilot + JetBrains Copilot hooks portable (#613). buildHookCommand always embedded process.execPath — under fnm on Windows, that's a per-shell-session shim (fnm_multishells/<PID>_<timestamp>/node.exe) which dies when the shell exits. Worse, the resulting .github/hooks/context-mode.json gets committed to git, so teammates and CI inherited the original author's local Node path. v1.0.138 uses the portable CLI dispatcher form (context-mode hook vscode-copilot pretooluse) for workspace-committed configs. Reporter @iskandersierra's bisect was right. Closes #613.
System-level guards (so this stops happening)
ctx_doctor proactively flags this bug class. Two new checks: (a) any absolute path detected in .github/hooks/context-mode.json or .cursor/hooks.json → solution-first [FAIL] with one command to fix; (b) leftover .mcp.json files in cache version dirs → [WARN] (harmless, gets cleaned on next upgrade). Messages rewritten for clarity — no jargon, no fear, one actionable command per issue.
CI lint guard prevents recurrence. New invariant test scans configs/**/*.json templates for absolute paths, fnm shim references, and shell-expansion paths. Any future PR that ships an absolute path in a config template fails CI with a specific file:key:value pointer.
Tests
- 14 new tests slotted into existing files (CONTRIBUTING L275 — no new test files)
- 3 new doctor tests + 1 new lint test
- 4 existing test blocks amended, each with git-blame archaeology preserving prior bug-class protections
- Targeted across 91 affected files: 2018+ tests pass
- Pre-existing baseline failures unchanged
Compatibility
15 adapters, 3 OS. No schema migration. engines.node >= 22.5.0 preserved.
Upgrade
npm install -g context-mode@latest
# Restart Claude Code (Cmd+Q + reopen) — `/reload-plugins` alone won't pick up the new MCP server.Credits
- @jowch — bisected #604 + shipped a staff-grade RED test, used verbatim
- @sebastianbreguel — PR #618 architectural direction
- @iskandersierra — bisected #613 to commit
f5c9d02+ shipped fix snippet - @ousamabenyounes — diagnosed #610 as session-restart issue (closed separately)