25 Commits

Author SHA1 Message Date
Niche-alchemy 6b6193e71c Fix Windows shell runtime and PowerShell UTF-8 handling (#796)
* Fix Windows shell UTF-8 handling

* Fix Windows shell UTF-8 handling

* Fix Windows shell UTF-8 handling

* Fix Windows shell UTF-8 handling

* Fix Windows shell UTF-8 handling

* fix(runtime): gate Windows bash detection through `where bash` so pwsh fallback is reachable

resolveWindowsBash() probed the well-known Git Bash paths with existsSync()
BEFORE consulting `where bash`. On a Windows runner with Git Bash installed,
that shortcut re-detected a real bash even in the 'bash unavailable' scenario,
so resolveWindowsShell() never fell through to pwsh — failing the new
'Windows prefers pwsh over powershell when bash unavailable' test.

Route availability through `where bash` (the surface the test stubs): when it
yields no real candidate, return null so the shell selector reaches pwsh. #826
is preserved — when `where bash` surfaces a Git Bash candidate we still
canonicalize to the absolute Git\\usr\\bin\\bash.exe path (WSL/WindowsApps
skipped) so native git keeps MSYS path conversion.

tests/runtime.test.ts + tests/executor.test.ts green; typecheck clean.

* docs(configs): propagate shell→javascript ctx_execute guidance to 13 adapters

Mirror the claude-code/codex change from this PR to the remaining adapter
instruction docs: the Bash/Shell routing line now recommends
ctx_execute(language: "javascript") (with `language: "shell"` only when code
matches the host shell), and grep/search guidance points at a portable
javascript filter instead of a shell grep. Keeps cross-platform parity now
that shell code is host-shell-specific on Windows.

cursor/context-mode.mdc and pi/AGENTS.md lack the analogous lines (condensed /
stub formats) and were intentionally left untouched. kimi has no doc.

---------

Co-authored-by: Mert Koseoglu <bm.ksglu@gmail.com>
2026-06-21 15:05:56 +03:00
Mert Koseoglu 675fec69c9 fix(codex): drop look-around from PreToolUse matcher (closes #547 partial — 1 of 1)
v1.0.124 shipped Codex matchers containing `(?!.*context-mode)` and
`(?!plugin_context-mode_)`. Codex's Rust `regex` crate does NOT support
look-around, so it rejects the matcher at boot with "look-around not
supported" — every Codex user on v1.0.124 is broken. Pin Codex matchers
to charset `[A-Za-z0-9_|]` so `is_exact_matcher`
(refs/platforms/codex/codex-rs/hooks/src/events/common.rs:152)
short-circuits the regex engine entirely. Hook BODY's `isExternalMcpTool()`
filter (hooks/core/routing.mjs:379) preserves end-to-end semantics.

RED→GREEN: tests/adapters/codex.test.ts "Codex matcher #547 — is_exact_matcher charset compliance"
2026-05-13 16:28:42 +03:00
Mert Koseoglu 996f8c4420 feat(codex): route external MCP tools through PreToolUse (closes #529 partial — codex)
Extends PR #532's external-MCP routing protection from Claude Code to Codex CLI.
Adds `mcp__(?!.*context-mode)` to the PreToolUse matcher so slack / telegram /
gdrive / notion-style MCPs trigger the context-guidance nudge before their
large payloads flood the model's context window.

Verified Codex MCP wire shape (`mcp__<server>__<tool>`) via configs/codex/hooks.json
existing matchers and tests/adapters/codex.test.ts:56-141. Codex own context-mode
tools surface as both bare (ctx_*) and `mcp__<server>__ctx_*` — both already
wired by the explicit entries above the new catch-all, and the negative
lookahead excludes any `mcp__` server segment containing `context-mode` to
prevent double-firing.

- src/adapters/codex/hooks.ts: export EXTERNAL_MCP_MATCHER_PATTERN
- src/adapters/codex/index.ts: append the pattern to PRE_TOOL_USE_MATCHER_PATTERN
- configs/codex/hooks.json: mirror runtime matcher
- README.md: update documented Codex matcher (drift-guarded by codex.test.ts)
- tests/adapters/codex-external-mcp-routing.test.ts: 4-test vertical slice
2026-05-12 13:19:45 +03:00
Mert Koseoglu d05a37584e feat(prose): retire prose-style enforcement entirely (#482)
Issue #482 (makoMakoGo) reported that context-mode's caveman/terse
injection pressures the model toward brevity on its FINAL ANSWER, not
just on tool-output reporting. Cited evidence: Moonshot AI on
kimi-k2.5 (anomalyco/opencode#20258, PR #20259) — aggressive brevity
prompts measurably degrade coding/reasoning benchmarks because the
model drops assumptions, caveats, verification evidence, failure
modes, and security warnings the user actually needs.

Considered:
  A — config switch ("injectCommunicationStyle: false"). Rejected:
      switches default-on become dead code.
  B — close FR with rationale. Rejected: ignores valid evidence.
  C — refine wording ("compress when reporting raw tool output, be
      complete for technical answers"). Rejected: still text
      injection, model-dependent, half-measure.
  D — full strip everywhere. Adopted.

The decision after grilling: context-mode's value is data routing
(sandbox, FTS5, session continuity), not prose styling. The brevity
injection conflated three goals — keeping raw data out of context
(real, hard-enforced), summarizing tool output compactly (LLMs auto-
calibrate), and final-answer prose style (the wrong target). Strip
all 22 sites where prose-style language landed.

Sites stripped (A-Z):

  hooks/routing-block.mjs
    - <communication_style> block  (Terse like caveman, fragments OK,
      auto-expand for security warnings)
    - <response_format> block       (Concise summary, 2-3 bullets)

  src/server.ts (5 MCP tool descriptions + 2 cosmetic comments)
    - ctx_execute            "When reporting results — terse..."
    - ctx_execute_file       same
    - ctx_search             same
    - ctx_fetch_and_index    same (URL + commands shapes, both)
    - cosmetic comment       "Caveman style — terse status line"
    - rewrote concurrency note: "Indexing is serial regardless of
      concurrency" → "Fetches parallelize up to your concurrency
      setting; FTS5 indexing serializes the writes after (SQLite
      single-writer rule)." — same fact, less jargon.

  configs/ (15 adapter MD files — every shipped system prompt)
    antigravity/GEMINI.md, claude-code/CLAUDE.md, codex/AGENTS.md,
    cursor/context-mode.mdc, gemini-cli/GEMINI.md, jetbrains-copilot/
    copilot-instructions.md, kilo/AGENTS.md, kiro/KIRO.md, omp/SYSTEM.md,
    openclaw/AGENTS.md, opencode/AGENTS.md, pi/AGENTS.md, qwen-code/
    QWEN.md, vscode-copilot/copilot-instructions.md, zed/AGENTS.md
    All had identical "## Output" block: 3 caveman lines stripped,
    workflow lines ("Write artifacts to FILES", "Descriptive source
    labels") kept.

  CLAUDE.md (repo root — internal dev instructions)
    Same caveman block stripped. We don't ship this file but we do
    eat our own dog food.

  README.md
    Pillar 4 ("Output Compression — Terse like caveman...") rewritten
    to "No prose-style enforcement" — explicitly cites the kimi-k2.5
    benchmark evidence as the rationale.

  web/index.html
    Removed Ch 4b entirely (the "Output compression" chapter with
    before/after example pushing terse style on the model).

Tests:

  - tests/session/continuity.test.ts: SessionStart routing-block
    assertion flipped from "must include 'Terse like caveman'" to
    "must NOT include caveman/terse-style directive".
  - tests/core/server.test.ts: Task hook injection assertion same
    flip. Two cosmetic comment renames ("Caveman style — terse status
    line" → "Status line: counts + sections + size"), test name
    rename ("caveman style" → "compact format"). Added new
    "prose-style policy (#482)" describe block at end of file with 3
    negative-pin tests covering server.ts MCP descriptions,
    routing-block, and README.

  Full suite: 82/82 files passed, 2645 passed, 20 skipped, 0 failed.
  Net +3 new tests (the policy describe block).

CONTRIBUTING.md
  New "Prose-style policy (#482)" section documents the decision so
  future contributors don't re-add the injection. Cites the Moonshot
  benchmark evidence + the regression test that pins the deletion.

This addresses #482 in full. Closing the issue with a comment that
walks the requester through the decision and links the policy section.
2026-05-10 14:45:41 +03:00
Marcus 043a1f52be [codex] add compact hook support (#492)
* feat(codex): add compact hook support

* chore(codex): address hook review cleanup

---------

Co-authored-by: boederzeng <86715671+boederzeng@users.noreply.github.com>
2026-05-09 21:48:19 +03:00
李明皓 990586a2d6 fix(codex): normalize hook errors and guard patch continuity (#479)
* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* docs(codex): document hooks feature flag rename

* fix(codex): manage native hooks.json lifecycle

* fix(codex): capture apply_patch continuity events

* fix(codex): harden hooks.json lifecycle and apply_patch continuity

Create ~/.codex before writing hooks.json, distinguish missing, invalid JSON, and unreadable hook configs, and refuse to overwrite malformed user hook files.

Only resolve prior Read errors after successful follow-up edits, and make ctx-upgrade fail clearly when hook configuration cannot be updated instead of reporting a false success.

Also document [features].hooks as the preferred key while keeping [features].codex_hooks as a legacy alias in current Codex builds.

* fix(codex): normalize hook errors and guard patch continuity

Normalize Codex PostToolUse error state into the extractor input, block failed apply_patch calls from emitting error_resolved, file continuity, or plan continuity events, and harden hooks.json lifecycle handling against schema-invalid but parseable shapes.

Also keep context-mode upgrade running Codex hook repair on the already-latest path so doctor-driven hook fixes are not skipped.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com>
2026-05-09 12:09:53 +03:00
Mert Köseoğlu 734bdbc2b7 v1.0.104 (#401)
* fix(insight): move showAllInsights useState before early return (React #310)

* 1.0.102

* fix(test): normalize pluginRoot path separators for Windows (#369)

buildNodeCommand() converts backslashes to forward slashes to prevent
MSYS path mangling on Windows. The test assertion was comparing against
raw pluginRoot (backslashes from mkdtempSync) causing CI failure on
windows-latest while macOS and Ubuntu passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(stats): persist counter + show lifetime + auto-memory + business value framing

- Add tool_calls table to SessionDB — counter survives upgrades and --continue
- Show persistent memory totals (events across all sessions)
- Show auto-memory count from ~/.claude/projects/*/memory/
- Replace hardcoded '9 more' with actual category count
- Use Opus pricing ($15/M) for cost calculations
- Replace '3.0x' with '3x longer sessions' phrasing
- Add 'Bottom line' footer with session/lifetime cost summary

Closes the upgrade-resets-stats bug. ctx_stats now correctly shows
that data persists across compaction, restart, and upgrade.

* fix(windows): normalize hooks.json placeholders on startup (#378)

The committed hooks/hooks.json and .claude-plugin/plugin.json use
${CLAUDE_PLUGIN_ROOT} placeholders + bare 'node' command. On Windows
+ Claude Code, this causes runtime loader failures (cjs/loader:1479)
because:
1. bare 'node' may not resolve via PATH (Git Bash issue, see #369)
2. ${CLAUDE_PLUGIN_ROOT} resolution can hit MSYS path mangling (see #372)
3. backslash paths get corrupted in shell quoting

Fix: start.mjs detects Windows on every MCP server boot. If hooks.json
or plugin.json contain unresolved placeholders, rewrites them with:
- process.execPath (absolute, quoted) instead of bare 'node'
- Forward-slash paths (prevent MSYS translation)
- Double-quoted paths (handle spaces)

Idempotent — only rewrites when placeholder pattern is detected.
Survives upgrades — runs at every start.

Closes #378

* fix(cache-heal): use shebang on Unix, self-heal stale node paths

After Brew updates Node, the versioned Cellar path written to
~/.claude/settings.json becomes stale, causing 'session start' errors:

  /opt/homebrew/Cellar/node/25.9.0_2/bin/node  (gone after upgrade)

vs the stable symlink:

  /opt/homebrew/bin/node                       (always current)

Root cause: start.mjs wrote `process.execPath` directly, which on
Brew returns the versioned path snapshot.

Fix (2 layers):
1. New installs on Unix: write cache-heal script with shebang
   (#!/usr/bin/env node) + chmod +x, register hook as bare script
   path. `env` resolves node from PATH at runtime — survives
   any Node upgrade.
2. Self-heal: every MCP boot, check if existing hook command
   references a node path that no longer exists. If stale,
   rewrite using current pattern.

Windows unchanged (no shebang support) — uses process.execPath
+ buildHookCommand pattern, plus self-heal for any breakage.

Reported by @vigo on Discord.

* fix(lifecycle): trigger isParentAlive re-check on stdin EOF to close 30s CPU-spin window (#388) (#389)

* fix(test): normalize pluginRoot path separators for Windows (#369)

buildNodeCommand() converts backslashes to forward slashes to prevent
MSYS path mangling on Windows. The test assertion was comparing against
raw pluginRoot (backslashes from mkdtempSync) causing CI failure on
windows-latest while macOS and Ubuntu passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* ci: update install stats

* fix(lifecycle): trigger isParentAlive re-check on stdin EOF to close 30s CPU-spin window (#311, #388)

The vendored MCP SDK's StdioServerTransport only registers 'data' and
'error' listeners on process.stdin. When the parent (e.g. Claude Code)
dies abruptly without sending SIGTERM, the server keeps reading from a
half-closed pipe and CPU-spins until the 30s ppid poll catches up. In
practice this manifests as orphaned context-mode processes accumulating
~80h of CPU time before being SIGKILL'd manually (#388).

The fix adds a single 'end' listener on process.stdin inside the
lifecycle guard. It does NOT shut down on 'end' alone — that's the
false-positive behavior #236 tore out. Instead, 'end' triggers the same
isParentAlive() probe the periodic timer runs, just earlier:

- parent alive  → no-op (#236 regression test still passes)
- parent dead   → 30s detection window collapses to ~0

Skipped on TTY (OpenCode ts-plugin), where stdin is not the MCP channel.

Tests: added a unit test that emits stdin 'end' under both alive and
dead parent conditions, and updated the existing listener-invariance
test to pin the new contract (only 'end' touched, restored on cleanup).
All existing tests still pass.

---------

Co-authored-by: Mert Koseoglu <bm.ksglu@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ray <cho_meiko@okuribito-funeral.jp>

* fix(executor): hide Windows console + drop .sh extension for shell exec (#384)

On Windows, ctx_execute(language: 'shell', ...) had two problems:

1. Silent output - child_process.spawn without windowsHide:true creates
   a visible console window that intercepts stdout, leaving the MCP
   response empty.

2. Git Bash popup - temp script written as 'script.sh' triggers Windows
   file association for .sh files. bash.exe opens a visible window over
   the user's IDE.

Fix (minimal, two surgical changes):
- spawn(..., { windowsHide: isWin }) via buildSpawnOptions(platform)
- isWin && language === 'shell' ? 'script' : 'script.{ext}' via
  buildScriptFilename(language, platform)

Both changes are Windows-gated. Linux/macOS behavior unchanged.

Does NOT change shell invocation semantics (no bash -c wrapper).
Does NOT add SHELL env override. Both deferred - separate features.

Helpers exposed as pure functions for unit testing without mocking
spawn or filesystem.

Closes #384.
Supersedes #385 with smaller surface area.

* fix(executor): full Windows shell coverage — bash -c source + SHELL override (#384)

Builds on commit 9d1f44f (windowsHide + no .sh extension) with the
remaining two root causes:

1. MSYS2 path mangling on non-C: drives. When bash.exe receives a
   script as a direct argument, MSYS rewrites paths like
   D:\tmp\script to D:\c\tmp\script, breaking execution. Fix: wrap
   in bash -c "source 'path'". The -c flag prevents MSYS from
   touching the file argument.

2. SHELL env var override. Users with non-standard shell setups
   (WSL, custom bash location, msys2 installations) need to point
   context-mode at their preferred shell. detectRuntimes() now
   checks process.env.SHELL first; if the path exists, uses it.

Single-quote escape applied to filePath in bash -c form to handle
paths containing apostrophes safely.

PowerShell uses -File flag (correct .ps1 invocation).
cmd.exe uses direct file (.cmd association is safe — no Git Bash issue).

Closes #384 fully (in addition to commit 9d1f44f).

Test coverage:
- SHELL env override (3 tests)
- buildCommand bash -c source (Windows + Unix variants, 5 tests)
- Single-quote escape edge case
- All previous Windows shell tests still pass

* fix(test): normalize scriptPath separators in cache-heal-self-heal assertion

Same root cause as the #369 test fix: buildHookCommand normalizes
backslashes to forward slashes for cross-platform safety (MSYS/Git Bash
mangling prevention). Test assertion at line 189 compared against the
raw scriptPath from mkdtempSync (backslash-separated on Windows),
causing CI failure on windows-latest while macOS and Ubuntu passed.

Aligns line 189 with line 234 which already had this normalization.

* fix(openclaw): route native tool aliases (#383)

* fix(test): normalize pluginRoot path separators for Windows (#369)

buildNodeCommand() converts backslashes to forward slashes to prevent
MSYS path mangling on Windows. The test assertion was comparing against
raw pluginRoot (backslashes from mkdtempSync) causing CI failure on
windows-latest while macOS and Ubuntu passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: update install stats

* ci: update install stats

* fix(openclaw): route native tool aliases

---------

Co-authored-by: Mert Koseoglu <bm.ksglu@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(hooks): passthrough on `ask` in headless mode (CLAUDE_CODE_HEADLESS) (#380)

* fix(test): normalize pluginRoot path separators for Windows (#369)

buildNodeCommand() converts backslashes to forward slashes to prevent
MSYS path mangling on Windows. The test assertion was comparing against
raw pluginRoot (backslashes from mkdtempSync) causing CI failure on
windows-latest while macOS and Ubuntu passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: update install stats

* ci: update install stats

* fix(hooks): passthrough on `ask` in headless mode

In `claude --print`, the CLI has no TTY to surface a permission prompt.
When routing returns `action: "ask"`, the formatter emits
`permissionDecision: "ask"` and the CLI hangs forever waiting on a user
verdict that can never arrive.

Mirror gemini-cli.mjs: when `CLAUDE_CODE_HEADLESS=1` is set in the
environment, return null (passthrough) on `ask`. Other actions
(deny/modify/context) unchanged. Interactive sessions are unaffected —
without the env var, behavior is identical to before.

Launcher scripts running headless agents must export
`CLAUDE_CODE_HEADLESS=1` to opt in.

* fix(hooks): extend headless passthrough to deny + modify

Without this, v1.0.103's routing.mjs returns action:'modify' for
'dangerous' curl/wget invocations — silently rewriting the command into
an echo that suggests ctx_execute. In TTY sessions that nudge is useful
(the agent reconsiders or asks the user). In headless 'claude --print'
the agent has no UI to reconsider; the rewritten echo runs, produces
zero stdout, and downstream pipelines see a silent failure.

Same shape as the existing 'ask' fix:

  case "deny":
+   if (isHeadless()) return null;
    return { ... };

  case "modify":
+   if (isHeadless()) return null;
    return { ... };

The 'context' case is left as-is (additionalContext is informational,
doesn't block the tool). Two existing tests in formatters.test.ts that
asserted 'still formats deny/modify normally' under
CLAUDE_CODE_HEADLESS=1 are inverted to assert the new passthrough
behavior, matching the existing 'ask' test pattern. 21/21 tests pass.

---------

Co-authored-by: Mert Koseoglu <bm.ksglu@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(memory): adapter-aware persistent memory across all 14 platforms

v1.0.100's "Unified Persistent Memory" feature was Claude-centric.
Auto-memory, prior session, and persist memory all hardcoded ~/.claude/,
breaking 13 of 14 platforms. Plus a worktree filename mismatch broke
Claude Code too on worktree sessions.

Architectural fix: adds 3 methods to HookAdapter interface so every
adapter declares its own conventions:
  - getConfigDir()        — ~/.claude, ~/.codex, ~/.qwen, ~/.gemini, etc.
  - getInstructionFiles() — ['CLAUDE.md'], ['AGENTS.md'], ['QWEN.md'], etc.
  - getMemoryDir()        — ~/.claude/memory, ~/.codex/memories, etc.

BaseAdapter ships sensible defaults derived from sessionDirSegments;
only the 11 non-Claude adapters override (Claude inherits).

Wiring changes:
  - searchAutoMemory() now accepts an adapter, dispatches via methods
  - ctx_search timeline uses _detectedAdapter.getConfigDir()
  - ctx_search timeline SessionDB filename now includes worktree suffix
    (matches what session-snapshot/session-extract write to)
  - extract.ts rule detection covers AGENTS.md, GEMINI.md, QWEN.md,
    KIRO.md, copilot-instructions.md, context-mode.mdc, and any
    *.md inside a memory/memories directory

Bonus fixes (from PR #376):
  - OpenCode/KiloCode cache path: now packages/context-mode@latest/
    layout (silently changed by upstream late 2024 — broke doctor/upgrade)
  - OpenCode SessionStart equivalent via experimental.chat.messages.transform
    — prior-session continuity now works on OpenCode/KiloCode

Tests added (8 new files, 65 new tests, all green):
  - tests/adapters/base-adapter-memory.test.ts        (4)
  - tests/adapters/claude-code-memory.test.ts          (3)
  - tests/adapters/memory-conventions.test.ts         (36)
  - tests/core/auto-memory-adapter.test.ts             (5)
  - tests/core/cache-plugin-root.test.ts               (2)
  - tests/core/server-timeline-adapter.test.ts         (3)
  - tests/opencode-session-start.test.ts               (2)
  - tests/session/extract-rule-detection.test.ts      (10)

Closes architectural root cause of #367 follow-ups.
Supersedes #379 (Codex), #370 (Qwen), #376 (OpenCode/KiloCode portions).

Co-Authored-By: Marcus Neufeldt <MarcusNeufeldt@users.noreply.github.com>
Co-Authored-By: btxbtxbtx <btxbtxbtx@users.noreply.github.com>
Co-Authored-By: Mickey Lazarevic <mikij@users.noreply.github.com>

* fix(test): make Windows-host assertions platform-aware

Three Windows CI failures in two test files, two distinct root causes:

executor.test.ts "buildCommand returns shell command array"
- PR #384 changed Windows+bash to return [bash, -c, "source 'path'"]
  (3 elements) to dodge MSYS path mangling on non-C: drives.
- Test still asserted length === 2 universally. Make it
  platform+shell aware: 3-element bash -c form on Windows+bash,
  2-element direct-exec form everywhere else.

cache-heal-self-heal.test.ts "Unix: rewrites command when node path is stale"
- selfHealCacheHealHook({platform: "linux"}) controls the WRITTEN
  command but ensureShebangAndExecBit's chmodSync is still a host
  syscall — NTFS cannot honor 0o755 exec bit.
- Skip just the mode assertion on Windows host; shebang check stays.

cache-heal-self-heal.test.ts "preserves other hooks unchanged"
- Same root cause as 35b6f90: buildHookCommand normalizes
  backslashes → forward slashes for cross-platform safety. Line 287
  was missed when 35b6f90 patched line 190. Apply identical fix.

* Add Insight directory overrides (#400)

* feat: add insight directory overrides

* build: update bundles for insight overrides

* fix(server): use sync platform detection for pre-adapter session dir

Edge case: when MCP server is called before initialize completes,
_detectedAdapter is null and getSessionDir() returns hardcoded
~/.claude/context-mode/sessions/. For non-Claude platforms this means
the wrong sessions dir until the adapter is detected.

Fix: when adapter is null, call detectPlatform() (sync, env-var-based)
and map to platform-specific session dir segments without adapter
instantiation. Falls back to .claude only if no platform signal found.

Closes the last hardcoded .claude fallback in server.ts.
Completes the multi-platform memory work from 6262c13.

* chore: rebuild bundles with sync platform detection fix

* fix(test): anchor XDG_CONFIG_HOME under fakeHome in memory-conventions

OpenCodeAdapter (and kilo variant) honor XDG_CONFIG_HOME / APPDATA
before falling back to homedir(). GitHub Actions Ubuntu runners can
have XDG_* set to the runner's real /home/runner, which bypasses the
homedir() mock from tests/setup-home.ts and leaks the real path into
test assertions.

Override XDG_CONFIG_HOME / XDG_DATA_HOME / APPDATA / LOCALAPPDATA at
the top of memory-conventions.test.ts (after setup-home runs) so all
adapters stay sandboxed under fakeHome. Reproduced locally with
XDG_CONFIG_HOME=/tmp/fake-xdg before the fix; passes after.

* test(session): regression test for cross-session bleed (#398)

Adds three tests pinning the contract LMS927369's PR #398 fixed:

1. getSessionEvents(db, sessionId) returns ONLY the requested
   session's events — no bleed from concurrent sessions even when
   another session has a more recent session_meta.started_at.
2. getSessionEvents returns [] for unknown sessionId — no fallback
   to global most-recent (which was the original bug's root cause).
3. getLatestSessionEvents still picks globally-most-recent by design,
   pinning the existing semantics so future callers can't be surprised.

All three would silently break if any of the 6 patched SessionStart
adapters regressed back to getLatestSessionEvents(db).

* fix(mcp): resolve ctx_index relative paths from project dir (#365)

fix(mcp): resolve ctx_index relative paths from project dir

Resolves ctx_index relative paths against the project directory (via
getProjectDir() env chain: CLAUDE_PROJECT_DIR / *_PROJECT_DIR /
CONTEXT_MODE_PROJECT_DIR / cwd) instead of MCP server cwd.

Known follow-up gaps tracked separately:
- IDEA_INITIAL_DIRECTORY missing from getProjectDir() cascade (JetBrains)
- FTS5 source label uses raw user-typed path (dedup gap)
- Bundle rebuild
- Negative-path test coverage (traversal / env-unset / label collision)
- getProjectDir() not unified across server.ts (deny-policy at L429)

Co-authored-by: Ousama Ben Younes <ousamabenyounes@users.noreply.github.com>

* test(hooks): add cross-platform regression matrix for MCP readiness (#354)

* test(hooks): add cross-platform regression matrix for MCP readiness

Locks in the directory-scan + PID-liveness contract from #347. The 11
test files updated by #347 changed the sentinel path but never asserted
that isMCPReady() returns true for a sentinel whose PID is outside the
test runner's process tree — the exact condition the PPID-keyed lookup
failed on under WSL2 / `bash -c "node ..."` topologies.

Coverage:
- sentinelPathForPid + deprecated sentinelPath shape
- sentinelDir platform branch (/tmp on Unix, os.tmpdir on win32)
- isMCPReady happy path + resilience to malformed payloads
- Stale-sentinel self-healing (gated to clean envs)
- PPID-independence regression: child PID ∉ runner tree → still true

Pure test-only PR. No production changes.

* test(hooks): apply review cuts (drop deprecated test, collapse it.each, env-var path)

- Drop sentinelPath() deprecated-export test. The JSDoc says it's kept for
  one release cycle; testing what's about to die is a maintenance trap.
- Collapse empty-payload + non-numeric-payload tests into a single it.each.
  Same contract, fewer lines.
- Pass resolved sentinel directory to the regression child via env var
  instead of recomputing the platform branch inline. Keeps mcp-ready.mjs
  as the single source of truth for the path shape.

* test(hooks): merge mcp-ready regression matrix into core-routing.test.ts

Per review feedback: move the three describe blocks (`contract`,
`stale-cleanup self-healing`, `PPID-independence`) from the standalone
tests/hooks/mcp-ready.test.ts into tests/hooks/core-routing.test.ts as
top-level describes after the existing `routePreToolUse` block. Same
test bodies, same assertions; only the host file changed.

The stale-cleanup block adds a local `beforeEach` that unlinks the
file-level `mcpSentinel` so the runner's own live sentinel does not
mask the dead-PID cleanup the test verifies.

---------

Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com>

* fix(cli): use execFile to open URL without shell interpolation

execSync(`open "${url}"`) interpolates the URL through the shell. The
URL is localhost-only today, but the pattern is fragile — a future
remote URL or weak port-validation flips it into shell injection.

Switches darwin/linux/win32 branches to execFile with an arg array
and a try/catch that prints a copyable URL on failure.

* perf: cut per-tool-call latency across all 14 adapters

Five fixes targeting synchronous hot paths fired on every tool call.
Per-session reclaim: ~1.8s macOS / ~0.7-1.5s Linux / ~7.5-12.5s Windows
(Windows wins biggest because fork+exec is heavier).

A1. src/session/db.ts memoize getWorktreeSuffix per (cwd, env override)
    Prior: `git worktree list --porcelain` subprocess fork on every
    ctx_* tool call (~12ms macOS, 50-100ms Windows). Cached: 0.86μs
    warm — 17,000x faster on the hot path.

A2. hooks/session-helpers.mjs 2-level cache for getWorktreeSuffix
    Hooks are fresh node forks per fire — module cache alone won't
    survive across calls. Added cross-process tmpdir marker keyed by
    sha256(cwd) — Windows-safe filename, all-OS tmpdir(). Within a hook
    process, in-memory cache hits 2 of 3 callsites (db/events/cleanup
    paths). Across hook processes, marker file short-circuits the git
    fork. Bench: 41ms cold child vs 28ms warm child = -12ms/fire on
    macOS, ~50-100ms/fire on Windows.

A3. src/server.ts defer persistToolCallCounter via setImmediate
    SQLite open/select/update/close was on the response path. Now runs
    after response returns. Removes 1-3ms from user-perceived latency
    per ctx_* call.

C1. hooks/auto-injection.mjs collapse 4× O(N) Array.filter() into one
    O(N) pass. UserPromptSubmit fires this every prompt; with N up to
    100 events the prior code walked the array 4 times.

C5. src/session/db.ts + hooks/session-loaders.mjs add bulkInsertEvents
    PostToolUse emits 5-15 events per tool call; per-event insertEvent
    ran N transactions = N WAL commits. Bulk path pre-computes hashes
    outside the SQL transaction, then runs all dedup/evict/insert work
    inside one transaction. attributeAndInsertEvents prefers bulk when
    available, falls back to loop for backward compat.

C3. src/search/auto-memory.ts single statSync per candidate file
    Prior code stat'd each candidate twice (size guard, mtime). Reuse
    the first stat for both — one syscall per file instead of two.

Cross-platform: every fix uses platform-agnostic primitives (tmpdir(),
sha256-hashed filenames, setImmediate, in-process module cache).
Tested on macOS locally (2045/2064 vitest pass, tsc --noEmit clean);
CI exercises Linux + Windows. The 5 fixes apply uniformly to all 14
adapters because they all funnel through the same session-helpers /
session-db / MCP server hot paths.

* test(server): regression guard for ctx_fetch_and_index tmp cleanup

ctx_fetch_and_index writes fetched content (including auth headers and
API tokens via subprocess fetch) to os.tmpdir()/ctx-fetch-*.dat before
reading and indexing. On macOS /tmp is world-readable, so leaking even
one file is a P0 issue on shared hosts. The handler currently wraps the
read in try/finally with rmSync(outputPath), but nothing prevents a
future refactor from dropping that block.

Adds tests/core/fetch-cleanup.test.ts with two layers of protection:
  1. Static-source guard — fails if the handler in src/server.ts loses
     the `finally { ... rmSync(outputPath) ... }` block. Verified RED
     by deleting the finally block (matches fail) and GREEN by
     restoring it.
  2. Behavioural tests — replicate the read+cleanup pattern against a
     local HTTP fixture covering success, empty content, error before
     write, and partial-write-then-throw paths. All assert no
     ctx-fetch-*.dat file remains in os.tmpdir() after the call.

No production code change — the fix already landed in 45ecf90; this
commit only locks the invariant in.

* fix(server): include IDEA_INITIAL_DIRECTORY in getProjectDir() chain

JetBrains adapter sets IDEA_INITIAL_DIRECTORY but the server's
getProjectDir() cascade did not read it, so ctx_index relative paths
resolved against the IDE bin dir instead of the project root.

Adds it to the env cascade and pins the regression with a JSON-RPC
spawn test that asserts resolution under IDEA_INITIAL_DIRECTORY only.

* docs: correct repo path, tool names, version, and adapter list

- llms.txt referenced the wrong repo (claude-context-mode) in title
  and 18 raw URLs.
- llms-full.txt documented tools without the ctx_ prefix and was
  pinned to v0.9.22 with a 6-tool count; updates to 11+ tools
  matching the live server registry, drops the stale version pin.
- platform-support.md said "nine platforms"; adds detail sections
  for OpenClaw and Zed and updates the count.
- README "6 sandbox tools" updated to current count.

* fix(server): include URL in ctx_fetch_and_index cache key

getSourceMeta(label) returned the meta from any prior fetch with the
same label, so two distinct URLs sharing a label silently returned
the cached first response instead of fetching the second.

Composes the cache key from label+url so legitimate cache hits still
work but cross-URL label reuse no longer serves stale content.

* fix(codex): default projectDir to cwd when env and input missing

Codex's parser left projectDir undefined when neither input.cwd
nor the platform env var (CODEX_PROJECT_DIR) was set, so
downstream hooks received undefined and broke under worktrees /
non-default cwd. Aligns with cursor/opencode pattern by falling back
to process.cwd().

* fix(gemini-cli): default projectDir to cwd when env and input missing

Gemini CLI's parser left projectDir undefined when neither input.cwd
nor the platform env var (GEMINI_PROJECT_DIR / CLAUDE_PROJECT_DIR)
was set, so downstream hooks received undefined and broke under
worktrees / non-default cwd. Now also accepts cwd from the wire
input and falls back to process.cwd(), aligning with the
cursor/opencode pattern.

* fix(openclaw): default projectDir to cwd when env and input missing

OpenClaw's parser left projectDir undefined when neither input.cwd
nor the platform env var (OPENCLAW_PROJECT_DIR) was set, so
downstream hooks received undefined and broke under worktrees /
non-default cwd. Aligns with cursor/opencode pattern by falling back
to process.cwd().

* fix(zed): default projectDir to cwd when env and input missing

Zed's parser left projectDir undefined when neither input.cwd
nor the platform env var (ZED_PROJECT_DIR) was set, so
downstream hooks received undefined and broke under worktrees /
non-default cwd. Aligns with cursor/opencode pattern by falling back
to process.cwd().

Replaces the throw-on-call defensive parsers with parsers that
return a minimal event using the standard fallback chain. Zed
remains mcp-only (capability flags are still all false), so these
parsers should not be invoked in normal operation — they exist as
safe defaults if a misconfigured caller bypasses the capability check.

* fix(antigravity): default projectDir to cwd when env and input missing

Antigravity's parser left projectDir undefined when neither input.cwd
nor the platform env var (ANTIGRAVITY_PROJECT_DIR) was set, so
downstream hooks received undefined and broke under worktrees /
non-default cwd. Aligns with cursor/opencode pattern by falling back
to process.cwd().

Replaces the throw-on-call defensive parsers with parsers that
return a minimal event using the standard fallback chain.
Antigravity remains mcp-only (capability flags are still all
false), so these parsers should not be invoked in normal operation
- they exist as safe defaults if a misconfigured caller bypasses
the capability check.

* fix(server): unify deny-policy project-dir resolution with getProjectDir()

checkFilePathDenyPolicy used `process.env.CLAUDE_PROJECT_DIR ?? cwd()`
which skips GEMINI_PROJECT_DIR / VSCODE_CWD / OPENCODE_PROJECT_DIR /
PI_PROJECT_DIR / IDEA_INITIAL_DIRECTORY / CONTEXT_MODE_PROJECT_DIR.

Non-Claude adapters either failed open or matched the wrong repo's
deny rules. Routes resolution through the existing getProjectDir()
helper so all 12 adapters apply policy against the correct root.

* fix(server): canonicalize ctx_index source label to resolvedPath

source ?? path used the raw user-typed input, so the same absolute
file indexed via './foo.md', 'foo.md', or 'subdir/../foo.md' produced
three FTS5 rows because dedup keys on sources.label.

Default the label to the resolved absolute path; explicit `source`
still wins. Adds a regression test pinning that two relative spellings
of the same file yield exactly one row.

* test(server): pin negative-path coverage for ctx_index resolution

Adds three regression tests:
- Relative `../` path traversal stays allowed (matches current
  trust-boundary policy; pinned to surface future security changes).
- CLAUDE_PROJECT_DIR unset falls back to spawned-server cwd.
- Strengthens the absolute-path bypass test to assert the source
  label equals the absolute path, so the test fails on baselines
  that skip the resolver.

* refactor(server): unify ad-hoc project-dir resolution on getProjectDir()

PR #365 added the getProjectDir() env cascade but only routed ctx_index
through it; ctx_execute_file's executor captured project root once at
construction (CLAUDE_PROJECT_DIR ?? cwd), so the same relative path
resolved differently across the two tools when only
CONTEXT_MODE_PROJECT_DIR was set.

Switches the executor to lazy resolution via getProjectDir(). Adds a
regression test asserting ctx_execute_file resolves under
CONTEXT_MODE_PROJECT_DIR when CLAUDE_PROJECT_DIR is unset.

* Revert "fix(zed): default projectDir to cwd when env and input missing"

This reverts commit 7cd9535214.

* Revert "fix(antigravity): default projectDir to cwd when env and input missing"

This reverts commit 2f5442f5e9.

* fix(test): isolate Windows test env from start.mjs side effects

- start.mjs: skip normalizeHooksOnStartup under VITEST. server.test.ts spawns
  start.mjs from the repo root; on Windows it was mutating the committed
  .claude-plugin/plugin.json, which then poisoned cli.test.ts:156's
  ${CLAUDE_PLUGIN_ROOT} placeholder assertion.

- memory-conventions: make OpenCode/Kilo getConfigDir/getMemoryDir
  expectations platform-aware. Adapter honors XDG_CONFIG_HOME on POSIX and
  APPDATA on Windows; tests previously asserted ~/.config on all platforms.

* feat(batch_execute): opt-in concurrency for I/O-bound batches

Adds a `concurrency: 1-8` parameter to ctx_batch_execute. Default 1
preserves the existing serial path (shared timeout budget, cascading
skip on timeout). >1 switches to a worker pool with per-command
timeouts and order-preserving output.

Local benchmark: 5× sleep(500ms) → 533ms at concurrency=8 (4.97×
speedup vs 2651ms serial).

Why now: LLM agents fan out multi-source research (gh/curl/git
batches). Sequential I/O is pure wait; concurrency turns it into
overlapped wait without any user-facing API change.

Tool description hardened with positive guidance per
PRD-concurrency-architectural.md §4: ✅ network/I/O batches use 4-8,
❌ CPU-bound (npm test, build, lint) and stateful (ports, locks)
stay at 1.

Architecture:
- runBatchCommands() extracted as pure function with BatchExecutor
  interface — testable in isolation, no MCP/SDK dependency.
- Handler is now a thin wiring layer (executor + sessionStats + store).
- THINK IN CODE directive preserved at full strength in description.

Tests (tests/core/server.test.ts, 12 new):
- Serial: order, cascade-skip, shared-budget exhaustion.
- Parallel: order preservation, in-flight cap, per-command timeout
  isolation, FS bytes callback, cmd-count > concurrency safety.
- Edge: empty array, no-output sentinel, prefix prepending.

Schema/description coverage assertion in batch_execute FS read
tracking suite proves the contract stays documented.

Co-Authored-By: Sebastian Breguel <sebastianbreguel@gmail.com>

* chore(batch_execute): strengthen THINK IN CODE in tool description

THINK IN CODE upgraded from soft guidance to NON-NEGOTIABLE directive,
with explicit clarification of how it relates to concurrency:

  Concurrency parallelizes the FETCH; THINK IN CODE owns the PROCESSING.

Adds the tactical detail (pure JavaScript, Node.js built-ins, try/catch,
null-safe) so LLMs writing the processing command have an unambiguous
contract — same level of specificity already present in ctx_execute and
ctx_execute_file descriptions.

No behavior change. Existing description-coverage assertion still passes.

* fix(test): raise insight-cors beforeAll hookTimeout to 120s

Default vitest hookTimeout (10s) is shorter than the inner 3-attempt ×
30s waitForInsight polling, so any slow Windows runner that takes >10s
to spawn node + open sqlite deterministically fails. Pin to 120s to fit
the worst-case retry budget.

* feat(concurrency): opt-in parallelism for I/O-bound MCP tools

Adds a `concurrency: 1-8` parameter to ctx_batch_execute and
ctx_fetch_and_index, plus a shared `runPool` primitive, observability
extractor, and Parallel I/O guidance across all 14 adapter routing
docs.

What ships
- src/concurrency/runPool.ts (new): generic in-flight-capped worker
  pool returning Promise.allSettled-style results. Single primitive
  used by both batch tools — no copy-pasted worker logic.
- ctx_batch_execute: serial branch unchanged (shared timeout budget,
  cascading skip). Parallel branch routed through runPool. Description
  hardened with PARALLELIZE I/O ✅/❌ guidance and NON-NEGOTIABLE
  THINK IN CODE clause.
- ctx_fetch_and_index: accepts both legacy `{url, source}` (single,
  exact backward-compat wording) and new `{requests: [{url, source}]}`
  (batch). Workers fetch in parallel via runPool; FTS5 writes drain
  serially through indexFetched to avoid SQLite WAL contention.
  Per-URL preview capped at 384 chars in batch mode (~3KB total) so
  context-savings hold under 8-URL fan-outs. composeFetchCacheKey
  wiring preserved across the refactor — same-label-different-URL
  collisions stay fixed (commit 1f1243e regression test enforced).
- effectiveConcurrency = min(N, os.cpus().length) when capByCpuCount
  set. Response surfaces capped count in caveman style.
- mcp_tool_call extractor (src/session/extract.ts) persists tool_input
  for mcp__* events with UTF-8-aware truncation at 2KB. Unlocks
  getMcpToolUsage() analytics — median/max concurrency per batch tool
  visible in ctx_stats.
- 14 adapter routing docs updated with the same Parallel I/O
  paragraph adapted to each host's tool-call prefix style. GitHub
  rate-limit caveat included consistently.

Hardening from 2-round architectural review
- Worker try/catch + Promise.allSettled isolation: one job throw no
  longer strands siblings or leaves undefined output slots.
- Timeout sentinel routes through formatCommandOutput: __CM_FS__
  markers stripped + bytes counted on partial-stdout-on-timeout.
- trackIndexed moved after FTS5 write succeeds (no over-count on
  failed indexes).
- UTF-8-aware truncate (Buffer.byteLength + continuation-byte
  walk-back): multi-byte payloads (CJK, 4-byte symbols) honor the
  byte budget without landing mid-codepoint.
- cpuCountForCap helper deleted: was CommonJS require in an ESM
  file, silently always returning 1. Replaced with top-level
  `cpus` import from node:os.

Tests (per CONTRIBUTING.md no-new-test-files rule, all under
existing files)
- 7 runPool unit tests: order, throw isolation, in-flight cap,
  job-count clamp, os.cpus cap, onSettled callback ordering.
- 13 ctx_fetch_and_index batch source-level tests: schema accepts
  both shapes, serial-write contract holds, backward-compat wording
  preserved, batch preview cap enforced, caveman header formatting,
  composeFetchCacheKey wiring across the refactor.
- 3 P0 hardening tests: throw-isolation, timeout marker stripping,
  5-cmd × 100ms at concurrency=5 < 200ms (CI-checked timing
  regression replacing the deleted bench).
- 4 mcp_tool_call extractor tests including UTF-8 multibyte
  regression.
- 3 getMcpToolUsage analytics tests.

Verification
- 138/138 server.test.ts pass; 309/309 across server + extract +
  analytics on cw/ctx-analytics.
- On next: 318/326 pass. 8 pre-existing unrelated failures
  (ctx_index projectRoot resolution from #365, ctx_execute_file env
  cascade, getSessionDir pre-detection) untouched.
- Typecheck clean.

Co-Authored-By: Sebastian Breguel <sebastianbreguel@gmail.com>

* fix(stats): restore Auto-memory, Opus pricing, and business-value footer

Commit b392c2f rewrote src/session/analytics.ts as part of the
opt-in concurrency feature and inadvertently dropped the user-facing
stats improvements landed in 4742160 (bugs #5/#6/#7/#8): the
Auto-memory preferences-learned line, the "Your AI talks less,
remembers more, costs less" tagline, the Opus pricing breakdown,
the "$X this session / $Y lifetime" footer, and the per-prefix
auto-memory bars. The 4th arg to formatReport also collapsed from
an options object {lifetime, mcpUsage} into bare mcpUsage, breaking
every test that passed lifetime data.

Restores the options-object signature, re-renders all dropped
sections under their original guards, and keeps b392c2f's runPool
and getMcpToolUsage infrastructure intact (no concurrency revert).
Updates the src/server.ts call site to match.

tests/session/stats-output-format.test.ts back to green (12/12 pass);
no other tests regress.

* feat(stats): persist runtime stats + status line bar (#399)

Adds a Claude Code statusLine integration so users see live token
savings at the bottom of their terminal without invoking any MCP
tool.

- src/server.ts: persistStats() writes <sessionDir>/stats-<sessionId>.json
  after every trackResponse / trackIndexed, throttled to 500ms; cleared
  on ctx_purge.
- bin/statusline.mjs: single-file Node script, no extra deps; walks the
  parent process chain via /proc/<pid>/status to find Claude Code; falls
  back to most recent stats-*.json within 30 minutes.
- src/cli.ts: context-mode statusline / statusline-install subcommands
  with safe ~/.claude/settings.json merge + timestamped backup.
- tests/statusline.test.ts: 8 hermetic cases covering idle, render,
  PPID fallback, stale sentinel, NaN guard, corrupt file, --json, error
  exit.
- README.md: status line wiring snippet for the Claude Code section.

Render aligned with the restored ctx_stats business voice (Auto-memory,
Opus pricing, "preserved across compact, restart & upgrade" tagline,
\$X this session / \$Y across sessions footer) so both surfaces speak
the same language.

Co-authored-by: Ousama Ben Younes <ousamabenyounes@users.noreply.github.com>

* fix(adapters/detect): full 14-platform PLATFORM_ENV_VARS audit + opencode-plugin DRY

PR #376 follow-up. mikij flagged that src/opencode-plugin.ts hardcoded a
KILO_PID-only check that violated DRY against PLATFORM_ENV_VARS. Audit of
the canonical list itself surfaced the broader problem: half of the entries
were unverified placeholders, 4 platforms (antigravity, zed, pi, openclaw)
were entirely missing or incorrectly listed, and the plugin paradigm's
fallback to "opencode" was blind (didn't actively check OPENCODE env vars).

What ships
- Re-audited every entry against the platform's own runtime source code:
  - kilo: dropped bare `KILO` (Kilo-Org/kilocode never sets it; only KILO_PID
    is set unconditionally at packages/opencode/src/index.ts:140).
  - jetbrains-copilot: dropped IDEA_HOME and JETBRAINS_CLIENT_ID (no
    source-line evidence in any JetBrains repo). Kept IDEA_INITIAL_DIRECTORY.
  - qwen-code: dropped QWEN_SESSION_ID (0 hits in QwenLM/qwen-code).
  - openclaw: removed entirely from env-var tier (runtime never sets
    OPENCLAW_HOME/OPENCLAW_CLI). Detection falls through to ~/.openclaw/
    config-dir tier, which already worked.

- Added 3 new platforms with verified env vars:
  - antigravity: ANTIGRAVITY_CLI_ALIAS — verified in Google's
    google-gemini/gemini-cli packages/core/src/ide/detect-ide.ts (canonical
    IDE detection map). Listed before vscode-copilot since Antigravity is an
    Electron/VSCode fork.
  - zed: ZED_SESSION_ID + ZED_TERM — verified in zed-industries/zed
    crates/terminal/src/terminal.rs `insert_zed_terminal_env()` and
    cross-confirmed by Google's gemini-cli detect-ide.ts.
  - pi: PI_PROJECT_DIR — confirmed by our own consumers at
    src/pi-extension.ts:154 and src/server.ts:153.

- Reordered fork pairs so collision detection works:
  - kilo before opencode (Kilo sets OPENCODE=1 because it's an OpenCode fork).
  - cursor + antigravity before vscode-copilot (both inherit VSCODE_PID).

- src/opencode-plugin.ts getPlatform() rewritten to iterate
  PLATFORM_ENV_VARS instead of hardcoding KILO_PID. Filters to kilo+opencode
  so a stray CLAUDE_PROJECT_DIR can't leak into the plugin's platform decision.
  Symmetric: actively checks BOTH platform's env vars instead of blind
  fallback. Per-line JSDoc credits PR #376 (mikij).

Tests
- tests/adapters/detect.test.ts: removed 5 broken assertions for unverified
  env vars; added 4 assertions for new platforms (antigravity, zed×2, pi)
  and a fork-collision test (KILO_PID + OPENCODE both set → kilo wins).
- tests/adapters/detect-config-dir.test.ts: rewrote priority chain from
  OPENCLAW/CODEX assertions to fork-collision assertions
  (KILO/OPENCODE, CURSOR/VSCODE, ANTIGRAVITY/VSCODE, CURSOR/CODEX).

Verification
- 451/451 adapter + plugin tests pass on next worktree.
- Typecheck clean.

Co-Authored-By: Mickey Lazarevic <noreply@github.com>

* fix(server): re-apply path-resolution + adapter-aware fixes lost in b392c2f

Commit b392c2f rewrote ~600 lines of src/server.ts as part of the
opt-in concurrency feature and inadvertently reverted PR #365 plus
fixes 1, 2, 4, 10 from the prior fix-army landings (#400 cluster).
Six independent regressions slipped through: ctx_index ignored
CLAUDE_PROJECT_DIR / IDEA_INITIAL_DIRECTORY / source-label
canonicalization, the deny-policy and executor cwd fell back to
ad-hoc CLAUDE_PROJECT_DIR ?? cwd(), getSessionDir lost its
detectPlatform pre-detection branch, and timeline-mode search lost
its worktree suffix, adapter-aware configDir, and adapter
pass-through.

Re-applies all of the above as a single squashed restore:

- isAbsolute import + resolveProjectPath helper.
- IDEA_INITIAL_DIRECTORY in getProjectDir() env cascade.
- ctx_index uses resolveProjectPath; source label canonicalises to
  the resolved absolute path so FTS5 dedup keys stop fragmenting
  across cwds.
- Executor takes a () => getProjectDir() thunk so ctx_execute_file
  picks up the full env cascade lazily, not just the constructor
  snapshot of CLAUDE_PROJECT_DIR.
- checkFilePathDenyPolicy reads getProjectDir() instead of the
  divergent CLAUDE_PROJECT_DIR ?? cwd() pattern.
- getSessionDir consults detectPlatform + getSessionDirSegments
  before the .claude fallback.
- Timeline mode opens SessionDB at hash+worktreeSuffix, derives
  configDir from _detectedAdapter.getConfigDir(), and threads the
  adapter into searchAllSources.

Also relaxes the fetch-cleanup static guard: b392c2f extracted the
fetch path into a runFetchOne helper, so the prior slice from the
registerTool call no longer covered ctx-fetch-*.dat. Asserts the
patterns at the file scope instead.

Local: 5 failing tests on next reduced from 13. The remaining five
are bundle-stale ctx_index spawn cases that pass once CI rebuilds
server.bundle.mjs on main.

* fix(security+release): PR #401 5-mode review follow-up — B3 redaction, SSRF guard, SHELL allowlist + 6 hardening fixes

5-agent review on PR #401 (v1.0.104) flagged P0 security + P1 release-quality
issues. This commit addresses every actionable finding except those requiring
release-process changes (npm version bump, grill-me gate — handled separately
on the release path).

Security (B3, SSRF guard, SHELL allowlist)
------------------------------------------
- src/session/extract.ts: mcp_tool_call extractor redacts secret-bearing keys
  BEFORE serialization. Walk via redactSecrets() with ancestor-set cycle
  detection (path-based, so DAG / shared-ref shapes process every site).
  Keys matching /authorization|token|secret|password|api_key|cookie|signature|
  private_key|client_secret/i are masked to "[REDACTED]". DAG-safe so a
  shared `headers` object referenced by multiple sub-requests gets redacted
  at every site.
- src/server.ts: ssrfGuard for ctx_fetch_and_index. Hard-blocks file://,
  gopher://, javascript:, data: schemes; hard-blocks 169.254.0.0/16
  (link-local incl. AWS/GCP/Azure IMDS 169.254.169.254), IPv6 link-local,
  multicast, reserved. Loopback + RFC1918 ALLOWED by default (developer
  workflow: local dev servers on localhost / internal network) — strict mode
  via CTX_FETCH_STRICT=1 blocks those too. DNS-resolves to defend against
  attacker-controlled DNS records / DNS rebinding. Runs BEFORE cache lookup
  so a previously-poisoned source label can't serve from cache.
- src/runtime.ts: SHELL env var allowlist. Basename must match
  /^(bash|sh|zsh|dash|pwsh|powershell|cmd)(\.exe)?$/i. Cross-OS basename
  split handles both / and \ separators. Defends against profile-script
  compromise redirecting executor to /usr/bin/python or arbitrary binary.

Release quality (P1.1, P1.2, P1.3)
----------------------------------
- src/server.ts P1.1: OPUS_INPUT_PRICE_PER_TOKEN dedup. Removed local
  definition; imports from src/session/analytics.ts (single source of truth).
  Architect + Ops 2-vote convergence.
- src/server.ts P1.2: gracefulShutdown flushes persistStats with throttle
  bypass before exit. Last 0-500ms of bytes_indexed/bytes_returned no longer
  silently lost on SIGTERM/SIGINT.
- src/server.ts + bin/statusline.mjs P1.3: STATS_SCHEMA_VERSION=1 in payload.
  Statusline reads schemaVersion (defaults 0 for legacy bundles), warns to
  stderr when reading future schema, still parses known fields. Eliminates
  silent schema drift (architect review found dollars_saved_lifetime was
  removed without versioning).

Architecture / dev experience
-----------------------------
- src/adapters/* getConfigDir contract: always returns absolute path.
  Pre-fix: cursor/vscode-copilot/jetbrains-copilot/kiro/openclaw returned
  relative segments → server.ts:1587 consumed verbatim → corrupted timeline
  configDir. New contract documented in HookAdapter JSDoc; all adapters
  resolve via path.resolve(projectDir ?? cwd, segment).
- bin/statusline.mjs cross-OS PID resolution (B4): macOS now walks parent
  chain via `ps -o ppid=,comm= -p <pid>` (mirroring Linux /proc walk).
  Windows degrades to ppid with one-shot stderr warning. Fixes session-id
  mismatch where statusline #1 would show stats from session #2 on macOS.
- Deleted PRD-347-ppid-mismatch-wsl2.md + PRD-wsl2-ppid-sentinel.md —
  shipped as docs without implementation per Diagnose review. Implement
  later or remove the orphan.

Test consolidation (CONTRIBUTING.md L275)
-----------------------------------------
- 3 cache-heal test files merged into 1 with shared fixture helper:
  tests/hooks/cache-heal-build-command.test.ts (deleted),
  tests/hooks/cache-heal-stale-node-detection.test.ts (deleted),
  tests/hooks/cache-heal-self-heal.test.ts (4 describe blocks, 24 tests
  preserved, makeTmp/writeJson helpers extracted).

Verification
------------
- All new/modified test files pass:
  - tests/session/session-extract.test.ts: 153/153 (B3 + 2 new redaction tests)
  - tests/runtime.test.ts: 12/12 (SHELL allowlist + 4 new tests)
  - tests/core/server.test.ts SSRF block: 12/12 (classifyIp + ssrfGuard source-grep)
  - tests/statusline.test.ts: 13/13 (B4 cross-OS + schemaVersion handling)
  - tests/hooks/cache-heal-self-heal.test.ts: 24/24 (consolidated)
  - tests/adapters/memory-conventions.test.ts: 62/62 (getConfigDir contract)
- Full vitest run: 2170/2188 pass, 19 skipped, 14 pre-existing failures
  (8 opencode config-paths + 6 ctx_index/ctx_execute_file projectRoot
  resolution — both documented in PRD-concurrency-architectural §8 and
  Diagnose review baseline; both resolve via `npm run build`).
- npx tsc --noEmit clean.

Co-Authored-By: Mickey Lazarevic <noreply@github.com>

* fix(stats): persist dollars_saved_lifetime for statusline (#402)

fix(stats): persist dollars_saved_lifetime so statusline can render the brand-poem triad

The README shipped in 58a60d8 promises:

  context-mode  ●  $0.42 saved this session  ·  $12.30 saved across sessions  ·  87% efficient  ·  23m

bin/statusline.mjs reads `stats.dollars_saved_lifetime ?? 0` and only
renders the "saved across sessions" block when > 0. After the b392c2f
concurrency refactor + e638bd6 analytics restoration, getLifetimeStats
came back, but persistStats() never wired it into the JSON sidecar —
the statusline would always read 0 and the "remembers more" half of
the brand poem (talks-less / remembers / costs-less) would never
render.

Wire `getLifetimeStats({ sessionsDir: getSessionDir() })` into
persistStats() with a 30s cache. The 500ms persist throttle would be
too aggressive for a function that scans every per-project SessionDB
plus the auto-memory dir; the statusline doesn't need second-by-second
lifetime accuracy. Conversion factor (256 tokens/event = ~1KB ÷ 4
bytes/token) is the same one used by analytics.ts renderBottomLine,
extracted to a TOKENS_PER_EVENT constant so it stays in lockstep if
either side moves.

Failures during the disk scan keep the stale cache (or 0) — same
best-effort discipline as the surrounding persistStats() try/catch.

Verification
- npm run typecheck         clean
- npm run build             clean (cli 552kb, server 511kb)
- npx vitest run            74/74 files, 2130/2130 pass
- targeted: tests/statusline.test.ts + lifetime-stats + stats-output-format
  all green (16/16)

Addresses Critical 3 from the PR #399 review:
https://github.com/mksglu/context-mode/pull/399#issuecomment-4364664233

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ray <34021803+meikocho1@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ray <cho_meiko@okuribito-funeral.jp>
Co-authored-by: 津铭 <yongtingzhang@gmail.com>
Co-authored-by: Anton Okhontsev <anton.ohontsev@gmail.com>
Co-authored-by: Marcus Neufeldt <MarcusNeufeldt@users.noreply.github.com>
Co-authored-by: btxbtxbtx <btxbtxbtx@users.noreply.github.com>
Co-authored-by: Mickey Lazarevic <mikij@users.noreply.github.com>
Co-authored-by: VrianCao <45995071+VrianCao@users.noreply.github.com>
Co-authored-by: Tomodad <128800342+Tomodad@users.noreply.github.com>
Co-authored-by: Ousama Ben Younes <ousamabenyounes@users.noreply.github.com>
Co-authored-by: Sebastian Breguel <62109266+sebastianbreguel@users.noreply.github.com>
Co-authored-by: Sebastian Breguel <sebastianbreguel@gmail.com>
Co-authored-by: Mickey Lazarevic <noreply@github.com>
Co-authored-by: Ben Younes <benyounes.ousama@gmail.com>
2026-05-03 00:05:27 +03:00
Mert Köseoğlu e7dbf587b7 feat: unified persistent memory & timeline search (#367)
* feat: unified persistent memory & timeline search (#363)

Two pillars for session continuity after compaction:

Pillar 1 — Unified Timeline Search:
- ctx_search gains sort="timeline" mode searching 3 sources chronologically
- ContentStore (current session) + SessionDB (prior sessions) + auto-memory
- Default sort="relevance" preserves current behavior exactly
- New searchAllSources() in src/search/unified.ts
- SessionDB.searchEvents() with project_dir filter + LIKE escaping
- searchAutoMemory() with adapter-aware dash-separated paths
- FTS5 schema migration: 4 new UNINDEXED columns (source_category, session_id, event_id, timestamp)

Pillar 2 — Auto-Injection on Compaction:
- buildAutoInjection() fires ONLY on SessionStart(source="compact")
- <behavioral_directive> for roles, <rules> for decisions, <active_skills> for skills
- 500 token hard cap with priority-based overflow strategy
- Never fires mid-session — context-mode does not pollute the context it protects

Static Reinforcement (~99 tokens, session start only):
- 12 adapter configs: Session Continuity + MEMORY sections (4 tiers by hook support)
- routing-block.mjs: <session_continuity> tag + tool hierarchy 0. MEMORY
- ctx_search tool description: SESSION STATE reminder

Bug Fixes:
- snapshot.ts: add missing case "role" + buildRolesSection()
- extract.ts: skill priority P3 → P2
- pi-extension.ts: minPriority 2 → 3

12 New Categories:
Phase 1: user-prompt, compaction, rejected-approach, session-resume, constraint, knowledge-reuse
Phase 2: agent-finding, error-resolution, external-ref, blocked-on, iteration-loop, latency

8 review rounds, 16+ engineers per round, TDD-first implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move rejected-approach from pretooluse to posttooluse via marker file

PreToolUse cannot safely load SessionDB (native module loading breaks
hook stdout on CI). Use tmpdir marker file pattern (same as latency)
instead: pretooluse writes marker, posttooluse reads and writes event.

Also implements knowledge-reuse event (search_hit_prior) with sessionId
wiring and 3 new tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: wire searchAllSources into ctx_search handler + skip empty guard for timeline

Two critical wiring bugs found by final review:
1. searchAllSources was imported but never called — sort="timeline" silently
   fell back to ContentStore-only search
2. Empty-index guard blocked timeline mode even when SessionDB had data

Now sort="timeline" creates a read-only SessionDB connection and calls
searchAllSources() for unified 3-source search. Default sort="relevance"
unchanged — calls store.searchWithFallback() directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct tool name prefixes in qwen-code, openclaw, kilo adapter configs

- qwen-code: bare ctx_* → mcp__context-mode__ctx_* (22 refs)
- openclaw: bare ctx_search → context-mode__ctx_search (line 49)
- kilo: bare ctx_search → context-mode_ctx_search (line 49)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review — 6 blockers resolved

1. Timeline sort: ContentStore results get timestamp fallback (new Date())
2. Read path no longer mutates state: knowledge-reuse write removed from
   ctx_search (read tool should not write)
3. configDir: use CLAUDE_CONFIG_DIR env var + absolute path (fixes #290 regression)
4. SessionDB: open once before query loop, close in finally (was per-query)
5. Auto-memory: 1MB file size guard on readFileSync
6. Debug logging: all catch blocks log with DEBUG=context-mode env var

Removes 3 knowledge-reuse tests that validated deleted write-on-read behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: timeline timestamp correctness + output format + CI fix

1. auto-memory: set timestamp from file mtime (was missing entirely)
2. ContentStore: use sessionStartTime instead of new Date() per-result
3. Normalize SQLite datetime format to ISO before sort
4. Output headers show origin + timestamp: [prior-session | 2026-04-27 14:30 | source]
5. pretooluse.mjs: move marker writes BEFORE stdout (fixes macOS CI timeout)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: macOS CI + auto-memory match quality + snippet boundaries

1. macOS CI: test sentinel path uses /tmp (matching sentinelDir()) instead
   of tmpdir() which returns /var/folders/... on macOS — fixes isMCPReady()
   returning false in CI, causing all redirect assertions to fail
2. Auto-memory: raise minimum term length from 2 to 3 chars, use word
   boundary regex instead of String.includes() (prevents "if" matching
   "specific", "definition", etc.)
3. Snippet extraction: walk to nearest paragraph boundary (\n\n) instead
   of cutting mid-word at arbitrary byte offset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: surface real timestamps from FTS5 chunks for correct timeline sort

ContentStore search results now carry actual indexing timestamps instead
of falling back to Date.now(). Changes:

1. store.ts: add chunks.timestamp to all 12 search SQL SELECT statements
2. store.ts: populate timestamp on INSERT with new Date().toISOString()
3. types.ts: add timestamp to SearchResult interface
4. unified.ts: use r.timestamp from ContentStore, fallback sessionStartTime

Timeline sort now produces correct chronological ordering across all 3
sources (ContentStore indexed_at, SessionDB created_at, auto-memory mtime).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove session bundle files from PR

Session bundles (session-db, session-extract, session-snapshot) are
CI-generated artifacts. Only server.bundle.mjs and cli.bundle.mjs
belong in commits. CI will regenerate session bundles on merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove PRD from PR — belongs in issues, not in repo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 09:04:10 +03:00
Marcus 91cd6dfeb2 feat(codex): wire prompt and stop hooks (#360)
Co-authored-by: boederzeng <86715671+boederzeng@users.noreply.github.com>
Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com>
2026-04-27 18:49:09 +03:00
Mert Koseoglu 859b87cac1 fix: replace bare tool shorthands with correct ctx_ prefixed names (#363)
All tool references in descriptions, response messages, and docs now use
registered MCP names (ctx_search, ctx_execute, etc.) instead of bare
shorthands (search, execute) that agents misinterpret as callable functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 18:18:25 +03:00
Mert Koseoglu 7e35caf43e feat: output compression + caveman-style response rules across all 14 adapters
Replace hard "500 words" limit with linguistic compression rules:
- Terse like caveman. Technical substance exact. Only fluff die.
- Drop: articles, filler (just/really/basically), pleasantries, hedging.
- Fragments OK. Short synonyms. Code unchanged.
- Pattern: [thing] [action] [reason]. [next step].
- Auto-expand for: security warnings, irreversible actions, user confusion.

Changes:
- routing-block.mjs: <word_limit> → <communication_style> with soft rules
- 14 adapter config MDs: compressed, 500-word limit removed
- 6 MCP tool descriptions: added response style tip
- README.md: "The Problem" → "How Context Mode Solves It" subheading,
  output compression as 4th pillar, platform count 12→14
- web/index.html: new "Output compression" section with before/after,
  platform count 12→14, JetBrains + Qwen Code tags added
- CLAUDE.md: updated output section
- 3 test files: assertions updated for new routing block text

~65-75% output token reduction with full technical accuracy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 22:59:41 +03:00
Mert Koseoglu 4e9baedc95 docs: add label field guidance to batch_execute across all 12 adapters (#256)
LLMs see batch_execute(commands, queries) in routing block and config
docs but label field was never documented. label serves triple duty:
markdown section header, FTS5 source key, and chunk boundary marker.

Without knowing about label, LLMs send {command, description} which
fails zod validation (label is required).

Fix: document label in routing-block.mjs GATHER section (runtime
injection for all adapters) + all 11 platform config files. No
fallback coercion — teach the LLM, don't silently patch.

Also fixes pre-existing test bug: routing test was comparing against
ROUTING_BLOCK (full) but routePreToolUse sends subagent block
(includeCommands: false).

Closes #256

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:43:16 +03:00
Mert Koseoglu bfd7a95c65 feat: add ctx_purge tool, platform-isolated content DB, fix #227 and #200
BREAKING: ctx_stats no longer accepts reset parameter. /clear and /compact
no longer affect context-mode data. Use ctx_purge for explicit wipe.

- Add ctx_purge tool with confirm gate, honest deletion reporting
- Platform-isolate content DB under ~/<configDir>/context-mode/content/
- Add adapter-based session paths via _detectedAdapter (no hardcoded .claude)
- Add hashProjectDir() for consistent hashing across content/session DBs
- Add CONTEXT_MODE_PROJECT_DIR universal env var in start.mjs
- Add getProjectDir() with verified platform env vars only
- Remove .clear-stats flag mechanism, checkClearStatsFlag, resetSessionStats
- Remove ctx_stats reset parameter (ctx_purge is sole wipe mechanism)
- Fix ctx_purge after --continue (_store=null path deletes from disk)
- Fix legacy shared content DB cleanup on purge
- Fix OPENCLAW_HOME semantic bug (install dir, not project dir)
- Update 11 adapter configs, 4 skills, README, docs, llms.txt
- 85 tests passing, zero type errors

Fixes #227. Closes #200.
2026-04-04 19:17:09 +03:00
Mert Koseoglu 071ded7a88 feat: Think in Code — MANDATORY paradigm across all 12 platforms
Add "Think in Code" manifesto to all instruction files, tool descriptions,
and routing messages. LLMs are now instructed to PROGRAM analysis via
ctx_execute instead of reading raw data into context to process mentally.

Changes:
- ctx_execute, ctx_execute_file, ctx_batch_execute tool descriptions updated
- 4 routing block messages (curl/wget, inline HTTP, build tools, WebFetch)
- 12 platform instruction files with platform-correct tool prefixes
- Root CLAUDE.md manifesto

Rules enforced:
- Pure JavaScript only — no npm dependencies
- Node.js built-ins only (fs, path, child_process)
- Always try/catch, null-safe
- Node.js + Bun compatible
- console.log() only the answer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:16:27 +03:00
Mert Koseoglu 838bfd6749 Revert "fix(codex): remove PreToolUse from Codex hooks — additionalContext unsupported"
This reverts commit 5ade0be640.
2026-04-03 23:06:52 +03:00
Mert Koseoglu 5ade0be640 fix(codex): remove PreToolUse from Codex hooks — additionalContext unsupported
Codex CLI does not support additionalContext in PreToolUse responses,
causing "PreToolUse hook returned unsupported additionalContext" error.

- Remove PreToolUse from configs/codex/hooks.json
- Remove PreToolUse from CodexAdapter.generateHookConfig()
- formatPreToolUseResponse: silently drop "context" decisions (return {})
- Update README + platform-support.md: hooks.json example without PreToolUse
- Routing handled via AGENTS.md instead

Closes #225

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:05:21 +03:00
Mert Koseoglu 725951a24a 1.0.59
fix: add PreToolUse to Codex hooks.json + stop to Cursor hooks.json

README:
- Codex: hooks no longer optional, PreToolUse added for routing enforcement
- Codex: removed from "no hook support" references (lines 747, 830)
- Codex: continuity table updated (PostToolUse/SessionStart: Yes)
- Cursor: stop hook in hooks.json example

Configs:
- configs/codex/hooks.json: created with PreToolUse+PostToolUse+SessionStart
- configs/cursor/hooks.json: added stop hook entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:15:27 +03:00
Mert Koseoglu 2b0a8c2a40 fix: post-merge fixes for PRs #183, #190, #192
PR #183: Restrict sessionKey regex to [a-zA-Z0-9_-]+ and add
startsWith containment check to prevent path traversal in
writeRoutingInstructions workspace derivation.

PR #190: Fix getRuntimeSummary() bun detection — use endsWith("bun")
instead of === "bun" to handle full paths like ~/.bun/bin/bun.

PR #192: Remove trailing whitespace from AGENTS.md blank separator lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:01:33 +03:00
李明皓 3df75cea67 Add Windows notes for script execution and path handling (#192)
Added Windows-specific notes regarding PowerShell cmdlets, path handling, and quoting requirements for scripts.
2026-03-28 21:54:25 +03:00
Mert Koseoglu 4bbbd46a29 fix: platform-specific tool prefixes in configs, Kiro mcp.json rename, Cursor .mdc, README rewrite
## Config tool prefix fixes (evidence-based from official docs)
- configs/opencode/AGENTS.md: mcp__context-mode__ → context-mode_ (OpenCode convention)
- configs/zed/AGENTS.md: mcp__context-mode__ → mcp:context-mode: (Zed convention)
- configs/codex/AGENTS.md: mcp__context-mode__ → bare ctx_ (Codex convention)
- configs/openclaw/AGENTS.md: mcp__context-mode__ → bare ctx_ (OpenClaw convention)
- configs/pi/AGENTS.md: mcp__context-mode__ → bare ctx_ (Pi convention)

## Kiro adapter fix
- Renamed configs/kiro/mcp_config.json → mcp.json (matches official Kiro docs)
- Updated src/adapters/kiro/index.ts: 5 references fixed
- Updated tests/adapters/kiro.test.ts

## Cursor routing rules
- Created configs/cursor/context-mode.mdc with alwaysApply: true
- Uses bare tool names per Cursor convention

## README rewrite (DX Engineer)
- All 12 platform install sections rewritten in consistent book format
- Every section: Prerequisites → Install → Verify → Routing
- Platform-correct tool names in all examples
- No references to auto-created files (disabled)
- Collapsible sections for advanced config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:10:37 +03:00
Mert Koseoglu 4d2c771ee7 feat: ctx_ tool prefix, Bash routing nudge, cross-platform fixes
- Rename all 9 MCP tools with ctx_ prefix to avoid conflicts with other MCP servers
  (ctx_execute, ctx_execute_file, ctx_batch_execute, ctx_index, ctx_search,
  ctx_fetch_and_index, ctx_stats, ctx_doctor, ctx_upgrade)
- Update all references across server, adapters, hooks, configs, skills, tests, docs
- Fix #66: inject BASH_GUIDANCE on every general Bash call (matches Read/Grep pattern)
- Fix Windows: replace new URL().pathname with fileURLToPath in 8 hook files
- Fix Windows: replace hardcoded /tmp with os.tmpdir() in cli.ts
- Fix Windows: replace split("/") with split(/[/\\]/) in session-directive.mjs
- Update Security section with .claude/settings.json placement note
- Add session continuity example to Try It section
- Update BENCHMARK.md tool names with ctx_ prefix
- Remove Contributors/Special Thanks sections from README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:32:12 +03:00
Mert Koseoglu c56afc99e4 feat: global install + CLI hook dispatcher
Replace `node ./node_modules/context-mode/hooks/...` with `context-mode hook <platform> <event>`.
All platforms now use `npm install -g context-mode` as prerequisite.
MCP server configs use `context-mode` directly instead of `npx -y context-mode`.
This ensures ctx-upgrade persists across sessions (no ephemeral npx cache).

- Add `hook` subcommand to CLI with HOOK_MAP dispatcher
- Update all adapter buildHookCommand() to use CLI dispatcher
- Update all adapter configureAllHooks() and generateHookConfig()
- Update all config templates (gemini-cli, vscode-copilot, opencode, codex)
- Update README install instructions for all platforms

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:00:06 +03:00
Mert Koseoglu d6efc67eb1 feat: universal ctx commands via MCP meta-tools
- Add doctor and upgrade meta-tools to server.ts — return shell
  commands + formatting instructions so any platform can execute them
- Add formatting directive to stats tool response ("display as-is")
- Add <ctx_commands> section to routing block for automatic LLM routing
- Add ctx commands table to all 5 platform configs
  (claude-code, gemini-cli, vscode-copilot, opencode, codex)
- Update README with Utility Commands section
- No changes to existing Skills — backward compatible

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:25:08 +03:00
Mert Koseoglu 956a46ab6e feat: multi-platform session continuity hooks + OpenCode plugin
- Add OpenCode TypeScript plugin entry point (src/opencode-plugin.ts)
  with routing enforcement, event capture, and compaction snapshot
- Implement Gemini CLI session hooks (aftertool, precompress, sessionstart)
  with full lifecycle: startup cleanup, event capture, snapshot generation,
  and compaction/resume recovery
- Implement VS Code Copilot session hooks (posttooluse, precompact,
  sessionstart) with sessionId camelCase support and copilot-instructions.md
  rule capture
- Extend hooks/session-helpers.mjs with platform params (GEMINI_OPTS,
  VSCODE_OPTS, getProjectDir) for cross-platform DB/session paths
- Fix Claude Code hook import paths (packages/session/dist → build/session)
- Add 36 new tests (14 OpenCode plugin + 11 Gemini + 11 VS Code hooks)
- Update README platform tables and session continuity notes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 04:34:10 +03:00
Mert Koseoglu 75ae6ae394 feat: multi-platform adapters, session continuity, hook scripts
- Add 4 new platform adapters (Gemini CLI, VS Code Copilot, OpenCode, Codex CLI)
  with hooks.ts + config.ts per adapter, platform detection via env vars
- Add shared hook core (routing.mjs, formatters.mjs, stdin.mjs) and
  platform-specific hook scripts (gemini-cli/, vscode-copilot/)
- Add configs/ directory with per-platform install files (settings.json,
  mcp.json, hooks.json, opencode.json, config.toml, routing instructions)
- Auto-write routing instruction files (CLAUDE.md, GEMINI.md, AGENTS.md,
  copilot-instructions.md) on MCP server startup via start.mjs
- Add Session Continuity section to README with event capture table,
  compaction survival flow, and per-platform support matrix
- Add adapter TDD tests (631 total tests across 8 new test files)
- Fix plugin.json version sync, add typecheck to bundle.yml workflow
- Remove obsolete pretooluse.sh, demo.md, root llms files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 03:16:23 +03:00