#415 — settings.json data loss (CRITICAL)
- pretooluse.mjs: REMOVED destructive `entries.filter(...!some(isCtxMode))` block
that wiped co-located user SessionStart hooks on every PreToolUse fire
(gated by per-version marker, fired once per machine per version)
- src/adapters/claude-code/index.ts: replaced entry-level filter at the
"allCovered" branch with inner-hook level strip + empty-entry prune;
user-owned siblings now preserved
- Safe block at lines 357-385 (stale-path cleanup with `isAnyContextModeHook`
guard) intentionally untouched
#411 — cached .mcp.json points to previous version
- src/cli.ts upgrade(): write `${CLAUDE_PLUGIN_ROOT}/start.mjs` literal
placeholder instead of `resolve(pluginRoot, "start.mjs")` absolute path.
Prior absolute path baked-in OLD version dir at write time → broken once
#181 lazy cleanup removed that dir → MCP reconnect failed
- Matches Anthropic canonical pattern (claude-mem, our own
.claude-plugin/plugin.json:23-29)
#414 — Windows hook crashes (cjs/loader:1479 + silent fail)
- NEW hooks/run-hook.mjs: universal crash-resilient wrapper. Installs
uncaughtException + unhandledRejection handlers BEFORE handler invocation.
Dynamic-imports side-effect modules (suppress-stderr, ensure-deps) inside
try/catch — static top-level imports threw at parse time and bypassed all
catch blocks (architect's critical finding)
- 5 hook entries (sessionstart, userpromptsubmit, pretooluse, posttooluse,
precompact) refactored to one-line `runHook(async () => {...})` pattern.
No top-level static side-effect imports remain
- Logs to ~/.claude/context-mode/hook-errors.log before exit 0 — silent
ghost hooks no longer possible
- scripts/postinstall.mjs: also normalize hooks.json at install time (was
only normalized on MCP boot — but hooks fire independently of MCP)
#408 — better-sqlite3 binding missing on Windows
- Merged PR #410 by @ousamabenyounes (5/6 prior PRs merged) via squash:
3-layer self-heal in scripts/heal-better-sqlite3.mjs (prebuild-install
via process.execPath → npm install fallback → actionable stderr).
Wired into postinstall, ensure-deps, and cli doctor hint
- All CI green (3 OS matrix)
Tests:
- 4 new behavioral run-hook tests (subprocess + poisoned-import staging)
- 2 new co-location preservation tests for #415
- Inverted .mcp.json placeholder test for #411
- Updated cli.test.ts anchors for new runHook architecture
- 9 pre-existing failures unrelated (8 opencode adapter + 1 continuity
parallel-spawn flake — both pass in isolation, fail before this commit)
Verified by direct repro: /tmp/repro-415.sh exit=1 (bug eliminated),
all hooks crash-safe via `node hooks/X.mjs </dev/null` smoke test.
- postinstall.mjs: removed duplicate self-heal block (was copy-pasted twice)
- Global heal hook: replaced #!/usr/bin/env bash with #!/usr/bin/env node
Bash dependency caused issues on Windows (no bash) and macOS with SIP/MDM
security policies that restrict shell execution. Pure Node.js works everywhere.
- Auto-cleans old .sh hook if present, deploys .mjs version
- Windows: uses junction type for symlinks (no admin required)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Code auto-update can leave installed_plugins.json pointing to a
non-existent directory, breaking all hooks. This adds 4 defense layers:
1. start.mjs startup: reverse heal — symlink from broken registry path to our dir
2. server.ts first tool call: mid-session heal — catches auto-update during session
3. postinstall.mjs: backward symlink on new install for stale registry
4. start.mjs auto-deploy: global SessionStart hook at ~/.claude/hooks/ that
survives total plugin cache breakage
9 TDD tests verify heal behavior with real filesystem fixtures.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(openclaw): register MCP sidecar on install so ctx_* tools surface to agent
context-mode's ctx_* tools live in server.bundle.mjs and are exposed over
stdio MCP. Other adapters (Claude Code, Codex, Cursor) spawn the bundle via
their platform's mcpServers config. The OpenClaw install script wrote
plugins.allow / plugins.entries to openclaw.json but never added
mcp.servers.context-mode, so the gateway never spawned the sidecar and
the agent never saw the ctx_* tool list — while openclaw plugins list,
openclaw doctor, and scripts/ctx-debug.sh all reported healthy state.
Changes
- scripts/lib/register-openclaw-config.mjs (new): extracts step 5 of the
install script into a testable helper. Writes plugins.allow / .entries /
cleans legacy plugins.load.paths (existing behavior) and additionally
registers mcp.servers.context-mode pointing at
<pluginRoot>/server.bundle.mjs. Idempotent: re-running is a no-op;
stale server paths are refreshed.
- scripts/install-openclaw-plugin.sh: step 5 delegates to the helper.
- tests/plugins/openclaw.test.ts: 5 unit tests for the helper (MCP entry
presence, idempotency, stale-path refresh, plugins.allow/entries contract,
legacy plugins.load.paths cleanup).
- docs/adapters/openclaw.md: new troubleshooting entry documenting the MCP
sidecar requirement and the manual openclaw mcp set recovery command.
Verification
- npm run typecheck: clean
- vitest tests/plugins/openclaw.test.ts tests/adapters/openclaw.test.ts:
117/117 pass (baseline 112 + 5 new)
- Live: on OpenClaw 2026.4.22 + context-mode 1.0.89, after the fix the
agent tool inventory includes context-mode__ctx_execute,
context-mode__ctx_search, context-mode__ctx_fetch_and_index, and the
rest of the ctx_* surface (OpenClaw prefixes MCP-sourced tools with
the server name).
Debugging walkthrough (initial wrong diagnosis, cross-check with
@benzntech, server.ts trace that surfaced the registerTool calls,
final resolution):
https://gist.github.com/murataslan1/cd7b27577fcb535d56fe318c2339b400
Companion to issue #45 (follow-up comment:
https://github.com/mksglu/context-mode/issues/45#issuecomment-4313313422).
Pre-existing test failures in tests/hooks/integration.test.ts (Security
Policy Enforcement) are present on main too (reproduced by git-stashing
this PR and re-running), so they are not caused by this change. They
appear related to the PreToolUse relaxation commits (415ce57, 2731ca2,
ece3abb) and are out of scope here.
Scope is limited to OpenClaw adapter files and install path; hooks,
src/server.ts, the session layer, and all non-OpenClaw adapter tests
are untouched.
* fix(openclaw): prefix ctx_* names in AGENTS.md so the model invokes MCP variants
Complements the previous commit. With the MCP sidecar registered, OpenClaw
surfaces the plugin's tools as context-mode__ctx_* (server-name prefix is
automatically applied by OpenClaw's MCP aggregator). The routing guidance
injected via AGENTS.md still referenced the unprefixed names (ctx_execute,
ctx_search, ...), which left the model to bridge the naming gap on its own.
UI QA on OpenClaw 2026.4.22 + context-mode 1.0.89 confirmed this was costly
in practice: the model would invoke context-mode__ctx_fetch_and_index first
(good), but then redundantly fall back to the built-in web_fetch for the
raw content, and skip context-mode__ctx_search entirely in favor of a
web_fetch + ctx_execute combo. The unprefixed guidance made the built-ins
look like the closer match when under pressure.
This commit rewrites all 14 ctx_* mentions in configs/openclaw/AGENTS.md
to use the context-mode__ctx_* form the model actually sees. Sanity check:
$ grep -c 'context-mode__ctx_' configs/openclaw/AGENTS.md # 14
$ grep -cE '\bctx_[a-z]' configs/openclaw/AGENTS.md # 0
Scope is still OpenClaw-only — configs/pi/AGENTS.md and every other
adapter's AGENTS.md are untouched, because those platforms don't apply
the server-name prefix.
Tests unchanged; this is a system-prompt text-only change. typecheck and
the full tests/plugins/openclaw.test.ts + tests/adapters/openclaw.test.ts
suite still pass (117/117).
* fix(openclaw): preserve unrelated MCP server fields; add edge-case tests
Addressing the review points from Codex's independent CLI review of PR #339:
1. The helper previously did a full reset of mcp.servers["context-mode"] on
every path refresh, which would silently drop user-supplied custom fields
(env, cwd, timeout, anything OpenClaw adds in a future release). It now
spreads the existing entry and only overwrites the two fields the helper
owns (command + args).
2. Added two edge-case tests in tests/plugins/openclaw.test.ts:
- "preserves unrelated fields on the existing mcp.servers entry when
refreshing the path" — seeds env/cwd/timeout on the old entry, bumps
pluginRoot, asserts all three survive alongside the new args[0].
- "throws a useful error when the runtime config is not valid JSON" —
exercises the previously-uncovered parse-error path.
Test count 117/117 → 119/119 on `tests/plugins/openclaw.test.ts` +
`tests/adapters/openclaw.test.ts`. Typecheck still clean.
Scope unchanged — still inside OpenClaw adapter/install/tests.
---------
Co-authored-by: Murat Aslan <murataslan1@users.noreply.github.com>
- mysql/redis/https patterns had \3 but only 2 capture groups → \2
- Session DB count: use wc -l instead of grep -c (avoid multiline parse)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Redaction: add postgres/mongodb/mysql/redis/https connection string patterns
(was leaking Supabase passwords in Cursor mcp.json)
- CWD: use git root instead of pwd for project-level config detection
- Adapter detection: scan config dirs (~/.claude, ~/.cursor, etc.) in addition
to env vars — script now works from terminal outside a session
- Process check: exclude context-mode-dashboard, esbuild, wrangler, workerd
- Hook registration: verify PreToolUse/PostToolUse/PreCompact/SessionStart
are all registered (not just file existence)
- Session DBs: check all adapter session dirs, not just ~/.claude/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed runtime/openclaw.runtime.json fallback entirely.
OpenClaw uses openclaw.json as the single config file since v2026.3.28+.
Source: https://docs.openclaw.ai/tools/plugin
- RUNTIME_JSON → OPENCLAW_JSON (no fallback, no backwards compat)
- Updated docs/adapters/openclaw.md references
- Zero legacy runtime.json references remaining in codebase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace deprecated `npm bin -g` with `npm config get prefix`
- Use `where context-mode.cmd` to find ALL shim locations (including
stale ones at C:\nvm4w\nodejs\)
- Create directory junctions at every shim location where node_modules
doesn't exist, so stale .cmd shims can resolve the package
- Rewrite stale shims that reference old `build\cli.js` → `cli.bundle.mjs`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move ensureNativeCompat from native-abi.mjs directly into start.mjs
to prevent missing-file errors when marketplace updater skips new files
- Delete native-abi.mjs (no longer needed as separate module)
- Add scripts/postinstall.mjs with Windows nvm4w directory junction fix:
detects when npm bin dir and node_modules are on different paths and
creates a junction so the .cmd shim can resolve the package
- Update tests: extract function from start.mjs source at test time
via temp module to avoid code duplication
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .pi/extensions/context-mode/package.json had a hardcoded version
that wasn't synced by the version lifecycle hook. Now included in both
the sync script targets and git add staging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install script now respects $OPENCLAW_STATE_DIR env var as fallback:
${1:-${OPENCLAW_STATE_DIR:-/openclaw}}
- Replace echo '\n' with printf in postinstall for POSIX portability
- Update README.md and CONTRIBUTING.md to reference npm run install:openclaw
as the primary install method (aligns with docs/adapters/openclaw.md)
Refs #123
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(openclaw): add preflight guards to install script
Checks for node in PATH, OPENCLAW_STATE_DIR existence, and
runtime/openclaw.runtime.json before proceeding.
Refs #123
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(openclaw): replace Python with node -e for runtime.json registration
Removes the python3 dependency from the install script. The node -e
replacement preserves all four operations: read JSON, deduplicate
load.paths, add to allow, add to entries.
Refs #123
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(openclaw): add install:openclaw alias and conditional postinstall hint
npm run install:openclaw runs the install script. The postinstall
hint only prints when OPENCLAW_STATE_DIR is set, avoiding noise for
non-OpenClaw users.
Refs #123
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(openclaw): update install section with npm alias and troubleshooting
References npm run install:openclaw as the primary method. Adds
prerequisites and troubleshooting for the common runtime.json and
state dir issues reported in #123.
Refs #123
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(openclaw): trailing newline in runtime.json + align docs error strings
Appends \n to JSON.stringify output for POSIX compliance. Updates
troubleshooting docs to match the actual script error messages.
Refs #123
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* test: add OpenClaw E2E synthetic test + CI workflow
Adds a programmatic hook lifecycle test that runs against the real built
plugin and SQLite DB — no gateway process needed. Validated on Raspberry
Pi 5 (aarch64), Node v22.22.1, OpenClaw 2026.3.13: 39/39 checks passed.
- scripts/test-openclaw-e2e.sh — fires all 10 hooks in sequence via a
recording API proxy, verifies session_events, session_resume, and
openclaw_session_map are populated in real SQLite
- .github/workflows/openclaw-e2e.yml — CI job on ubuntu + macos,
pinned to OpenClaw v2026.3.2, with install verification and artifact
upload on failure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(ci): remove npm cache from setup-node — repo uses bun.lock not package-lock.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: update install stats
* ci: update install stats
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Share a single SessionDB across all register() calls via module-level
singleton, reducing connection overhead with concurrent agents
- Add npm run deploy script (build + clear jiti cache + restart gateway)
- Snapshot sessionId in before_prompt_build handler for consistency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Register before_model_resolve hook to capture user decisions, roles,
intents, and large data pastes via extractUserEvents() — mirrors the
UserPromptSubmit hook that Claude Code has but OpenClaw lacked
- Register command:reset and command:stop hooks for session cleanup
(previously only command:new was registered)
- Add OPENCLAW_TOOL_MAP to translate OpenClaw lowercase tool names
(exec, read, write, edit, glob, grep) to Claude Code PascalCase
equivalents that extractEvents() can recognize
- Add generic tool_call fallback event for unrecognized OpenClaw tools
- Accept both result/output and error/isError field variants defensively
to handle both OpenClaw v2+ and older gateway builds
- Add TDD tests for all new hooks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dogfooding at /openclaw revealed three issues:
- register() was async — OpenClaw silently discards the returned Promise,
so no hooks were ever registered. Fix: sync register() with an eager
initPromise that hook handlers await internally.
- "skills" key in openclaw.plugin.json resolved to the global extension
dir (/openclaw/extensions/<id>/skills), not the plugin source. Removed.
- Added scripts/install-openclaw-plugin.sh: one-shot installer that builds,
rebuilds better-sqlite3 for system Node, creates a real extension directory
(OpenClaw does not follow dir symlinks), registers in runtime.json, and
restarts the gateway.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- packages/core/src/ is now the single source of truth for MCP server code
- store.ts imports loadDatabase, applyWALPragmas, PreparedStatement from @context-mode/shared/db-base
- store.ts imports IndexResult, SearchResult, StoreStats from @context-mode/shared/types
- executor.ts imports smartTruncate from @context-mode/shared/truncate
- executor.ts imports ExecResult from @context-mode/shared/types
- Root tsconfig.json converted to project references orchestrator (tsc -b)
- fix-imports.mjs rewrites both build/ and packages/session/dist/
- CI simplified to single typecheck + build step
- Deleted root src/ (-4,959 lines of duplication)
- All 492 tests pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session Continuity captures 14 event categories (file, rule, decision, task,
git, env, cwd, error, mcp, subagent, skill, intent, role, data) plus user
prompts via PostToolUse and UserPromptSubmit hooks.
Key changes:
- Add PostToolUse hook with extractEvents() pipeline (14 extractors)
- Add PreCompact hook that builds priority-sorted resume snapshots
- Add UserPromptSubmit hook to capture every user prompt for seamless
continuation after compact
- Rewrite SessionStart injection: zero-truncation, full event data grouped
by category in <session_events> XML, <last_user_prompt> for exact
continuation, behavioral directives telling LLM to index into context-mode
- Add marketing DISPLAY TO USER table on compact/startup showing what was
preserved with counts and data previews
- Enhance /ctx-stats with Session Continuity section: 4-column table with
"I remember..." previews and "Why it matters" hints, --continue note
- Fix sdb.close() bug in stats (was called before previewRows query)
- Add MCP tool extraction (extractMcp) and CLAUDE.md content capture
- Add packages/session (extract, snapshot, db) and packages/shared
- Add session test suite (db, extract, snapshot, pipeline)
- Add per-package .gitignore for dist/
- Update CONTRIBUTING.md with dev-mode guide
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>