mirror of
https://github.com/mksglu/context-mode.git
synced 2026-10-02 04:14:38 +08:00
main
188
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4112e8485b | docs(readme): sync Zed config fix to next (#875) | ||
|
|
e0c3bb2afa |
fix(adapters/omp): self-register MCP server on plugin load (#677) (#872)
The `omp plugin install context-mode` path wires the extension factory so routing hooks fire, but never creates an mcp.json entry — so the 11 ctx_* tools stay unreachable even though curl/wget are hard-blocked. Register the server on plugin load, only when absent (never clobbering a user's existing entry). Spawn via `node <plugin>/server.bundle.mjs` rather than the `context-mode` bin: under the plugin install the package lives in ~/.omp/plugins/node_modules and its bin is not on PATH. Takes effect on the next OMP restart, same as the manual mcp.json workaround the issue documents. |
||
|
|
d1ae562557 |
fix(security): confine ctx_execute_file to the project boundary (#852)
ctx_execute_file fed its `path` straight into resolve(projectRoot, path), so an absolute path (/home/user/secret, /etc/passwd) or a `../` traversal escaped the workspace and read any file on the host. With Claude Code's sandbox enabled the host denied such a read, but the agent retried through the MCP sandbox — and the host's MCP approval prompt cannot inspect the tool's input params, so the escape was invisible to the approver. Mitigation (defense-in-depth): - New pure, no-regex primitive isPathInsideProject() + policy wrapper evaluateProjectContainment() in security.ts: a path resolving outside the project root (absolute escape, `..` traversal, or symlink-canonical escape) is refused. The symlink check mirrors evaluateFilePath(). - ctx_execute_file handler runs checkProjectBoundary() before execution. - Escape hatch reuses the host's existing permissions.allow Read(...) rules (via generalized readToolPermissionPatterns), NOT a bespoke context-mode env that would rot into dead code — an out-of-project grant lives once in the same config Claude Code itself honors. - MCP approval titles for ctx_execute / ctx_execute_file now read as code execution, since refs show the host prompt renders only the tool title + raw args (the title is the one server-controlled signal). Residual: ctx_execute / ctx_batch_execute run arbitrary code and still inherit the process FS; the boundary guard hardens the file-read tool, not a full OS sandbox. Host-level sandboxing remains the primary control. Tests: tests/security/project-boundary-852.test.ts (containment geometry, symlink escape, allow-rule opt-in, server wiring). typecheck green; full suite 4484 passed. Bundles intentionally not committed (CI rebuilds on main). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2608e344bb |
refactor(antigravity-cli): one-command agy plugin install (drop npm wrapper) + doc cleanup (#853)
refactor(antigravity-cli): one-command agy plugin install; drop npm wrapper agy 1.0.7 added GitHub-subpath plugin install (with branch resolution), so the former three-step flow shipped in #787 — `npm install -g` + `git clone` + `npm run install:agy` (scripts/install-antigravity-cli-plugin.mjs) — is dead weight. agy (<=1.0.6) `plugin install` accepted only a local directory, which is why the wrapper existed; that constraint is gone. Install is now one command, no clone, no wrapper: npm install -g context-mode agy plugin install https://github.com/mksglu/context-mode/tree/main/configs/antigravity-cli - remove scripts/install-antigravity-cli-plugin.mjs + the install:agy npm script and its files[] entry - antigravity-cli doctor `fix` strings now point at the one-command install - README: split the conflated "Antigravity" entry into Antigravity IDE vs Antigravity CLI (agy); bring the agy section to the other install guides' level (Prerequisites / Install / MCP-only / Verify / Routing / Full configs); Verify points to the existing "Try It" prompts. Deep mechanics and troubleshooting stay in docs/platform-support.md - docs/platform-support.md: one-command update + a "Verified: agy 1.0.10" note recording the >=1.0.7 install floor; hook contract unchanged through 1.0.10 (config/hooks.json canonical since 1.0.8) - tests: drop the wrapper-shape regression assertions (no leftover trace); bundle-content tests still guard the installable artifact Preserved invariants: the bundle still registers MCP via its native mcp_config.json (command: context-mode, env-pinned CONTEXT_MODE_PLATFORM=antigravity-cli); the dual hooks.json + hooks/hooks.json is kept (agy runtime reads root, validate reads subdir). Verified on agy 1.0.10 (Linux): clean-room single-command install registers MCP + hooks + skill from a zero baseline; tools/list exposes 11 Gemini-safe ctx_* tools (0 const / 0 additionalProperties); `agy -p` smoke returns 12. npm run build + tsc --noEmit + targeted vitest (47) pass. Bundles are CI-managed (bundle.yml) and not included. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a880c4bf24 | docs(readme): link docs/adapters/kimi-code.md from the Kimi Code section | ||
|
|
9f34c6f11b |
Add GitHub Copilot CLI + Antigravity CLI (agy) support (#787)
* feat(adapters): add Antigravity CLI (agy) + GitHub Copilot CLI support
Add two agentic CLI adapters onto next's existing adapter registration —
without the abandoned PR's setup subcommand / consolidated registry.
Antigravity CLI (agy):
- MCP + capture-only PostToolUse hook adapter (agy honors no stdout veto in
auto-run mode; verified against agy 1.0.5). The agy hook payload
{conversationId, toolCall, workspacePaths} is mapped onto the shared
capture pipeline.
- Ships a Claude-layout plugin bundle (configs/antigravity-cli/) installed via
`npm run install:agy` (mirrors install:openclaw), with a version-skew
capture-hook probe in the installer.
GitHub Copilot CLI (1.0.59):
- json-stdio hook adapter with six events: PreToolUse, PostToolUse, PreCompact,
SessionStart, UserPromptSubmit, Stop. Overrides CopilotBaseAdapter to emit the
FLAT {type,command} + top-level "version": 1 hook config Copilot CLI requires.
- MCP install via `copilot mcp add context-mode -- context-mode`.
- Fix a latent Stop-hook bug: a session_end event with no `data` threw inside
insertEvent (createHash(undefined)) and was silently dropped.
Cross-cutting:
- #774: probe agy/copilot config markers before the generic ~/.claude check.
The copilot marker is narrowed to context-mode-written files
(~/.copilot/mcp-config.json | hooks/context-mode.json), not a bare ~/.copilot/
dir, so a co-installed-but-unconfigured Copilot CLI cannot steal detection
from a Claude Code user.
- Dispatcher fails OPEN (exit 0) on a missing hook script: GitHub Copilot CLI
treats an exit-1 PreToolUse hook as DENY, so a version skew (a newer adapter's
hook command on an older global) would otherwise brick the agent.
Fixes #774. Fixes #775.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci: regenerate bundles for antigravity-cli + copilot-cli support
Picks up the new HOOK_MAP entries, client-map keys, validPlatforms,
getSessionDirSegments cases, and the fail-open dispatcher into the
esbuild-generated runtime bundles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(platform-support): sync support docs to 18 platforms + fix stale Kiro classification
Make README.md and docs/platform-support.md internally consistent and aligned
with the adapter source of truth.
Header sync (18 platforms everywhere):
- The Main Comparison Table (was 11 cols), the Capability Matrix (was 11), and
the README Platform Compatibility table (was 17, missing Kimi Code) now list
the SAME 18 platforms in one shared order. Adds the two branch-new platforms
(GitHub Copilot CLI, Antigravity CLI `agy`) plus previously-omitted Qwen Code,
KiloCode, OpenClaw, Zed, Pi as columns. Each cell sourced from the per-platform
detail sections / adapter source and independently verified.
- Fix five ragged rows in the Main Comparison Table (a dropped trailing OMP cell)
and add CLI Hook Dispatcher rows for qwen-code + copilot-cli.
- GitHub Copilot CLI section: normalize the `**Hook Names:**` label and add the
missing `**Output Modification:**` field for json-stdio-family parity.
Fix stale Kiro classification (code is the source of truth):
- The kiro adapter is json-stdio with working preToolUse/postToolUse hooks
(hooks/kiro/{pretooluse,posttooluse}.mjs + a kiro HOOK_MAP entry), yet the docs
called it "MCP-only (Phase 2 — not implemented)" and the README contradicted
itself ("no hook support" in one place, "native preToolUse/postToolUse" in two
others).
- Reclassify Kiro as json-stdio with PreToolUse + PostToolUse + exit-code-2
blocking across the Overview paradigm table, both wide tables, the dispatcher
table, and the Kiro detail section; document that agentSpawn (SessionStart) and
stop are not yet wired, so session restore after compaction is unavailable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(antigravity-cli): drop vestigial .mcp.json dependency that broke fresh clones
The agy plugin-bundle test asserted configs/antigravity-cli/.mcp.json, but
.mcp.json is gitignored repo-wide and was never committed — so the test passed
on the dev machine (file present locally) yet failed on a fresh clone with
ENOENT. Committing the file is the wrong fix: the .gitignore comment documents
that shipping .mcp.json has silently broken fresh installs before (#253/#531).
- The bundle declares MCP the Claude way via .claude-plugin/plugin.json
mcpServers (committed — the mechanism agy reads on `agy plugin install`),
mirrored by the agy-native mcp_config.json (committed). Remove the vestigial
bundle .mcp.json and stop the test + docs from requiring it. Every file the
plugin test reads is now git-tracked, so a fresh clone passes.
- README: Kiro was still grouped under "Non-hook platforms" in the routing-
enforcement note. Kiro has native preToolUse/postToolUse hooks; it needs the
manual KIRO.md copy only because agentSpawn/SessionStart is not yet wired.
Reword to say so.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(adapters): cross-platform agy installer + copilot-cli COPILOT_HOME parity
Windows fix (real): replace the bash-only agy plugin installer with a
cross-platform Node script so `npm run install:agy` runs natively on Windows
(PowerShell/cmd), not just Git Bash/WSL. agy runs on Windows, so its installer
must too — the old `node -e` wrapper hard-exited 1 on win32. openclaw stays
bash-only (it is genuinely POSIX-only). Removes
scripts/install-antigravity-cli-plugin.sh in favor of
scripts/install-antigravity-cli-plugin.mjs (same preflight + version-skew probe).
copilot-cli hardening (COPILOT_HOME edge case only — the default ~/.copilot
install was and remains correct):
- CopilotCliAdapter.getSessionDir() now roots at getConfigDir() (COPILOT_HOME-
aware), mirroring codex/kimi, so the TS server reads sessions from the same
place the hook runtime (COPILOT_OPTS configDirEnv: COPILOT_HOME) writes them.
Previously a relocated COPILOT_HOME split hook writes ($COPILOT_HOME/...) from
server reads (~/.copilot/...), making sessions appear empty.
- detect.ts copilot-cli marker honors COPILOT_HOME, not just ~/.copilot.
No change to the default (COPILOT_HOME-unset) behavior; a regression test pins
both the ~/.copilot default and the COPILOT_HOME-rooted path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci: regenerate bundles for copilot-cli COPILOT_HOME parity
Picks up CopilotCliAdapter.getSessionDir() and the COPILOT_HOME-aware detect.ts
marker into the esbuild runtime bundles.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(antigravity-cli): installer registers the MCP server (agy plugin install skips it)
`npm run install:agy` ran only `agy plugin install`, which — verified against
agy 1.0.5 — processes a bundle's skills + hooks but logs "mcpServers : skipped
(not found)" and registers NO MCP server. agy reads a plugin's MCP only from a
bundle `.mcp.json` (intentionally not shipped — gitignored repo-wide after
#253/#531) and has no `agy mcp add` command, so context-mode's MCP server was
never registered: users had to add it to ~/.gemini/config/mcp_config.json by hand
(reported on Windows; reproduced on Linux: `mcpServers : skipped (not found)`).
The installer now also writes context-mode into agy's GLOBAL MCP profile
~/.gemini/config/mcp_config.json (idempotent JSON merge, preserves other servers,
tolerates a malformed file) — the file agy actually loads and `context-mode
doctor` checks. Verified end-to-end on agy 1.0.5: `npm run install:agy` →
mcp_config.json gains context-mode → `agy -p "... ctx_execute ... 7 + 5"` → 12.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(server): emit Gemini-safe tool schemas so agy/Gemini CLI expose ctx_* tools
Antigravity CLI (agy) and Gemini CLI use Gemini's function-calling API, which
rejects JSON Schema `const` and `additionalProperties`. When a tool's parameter
schema contains either, the host SILENTLY DROPS that tool from the model's
function list — so agy never sees the ctx_* tools and works around them by
hand-rolling the MCP protocol through its Bash tool (verified on Windows: agy
wrote scratch/call_ctx_stats.js + list_mcp_tools.js MCP clients instead of
calling the tools natively). That defeats the point of context-mode — bash
output floods the context window instead of staying in the sandbox.
context-mode builds schemas with Zod, which emits `const` (from coerce/preprocess
constructs) and `additionalProperties`, with no Gemini sanitization. Wrap the
SDK's tools/list handler to rewrite the EMITTED schema:
- `const: X` -> `enum: [X]` (an identical single-value constraint)
- drop `additionalProperties` (advisory-only; every ctx_* handler parses args
with Zod, which strips unknown keys server-side regardless)
Both transforms are behavior-preserving for every other client (Claude Code,
Copilot, Cursor): const and a one-value enum are equivalent, and no model sends
undeclared properties — only the wire schema changes, never validation or how a
tool is called. Best-effort: if the MCP SDK internals shift, the original handler
is left untouched (no regression). Verified on the real tools/list: all 11 ctx_*
tools now emit 0 `const` / 0 `additionalProperties`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci: regenerate bundles for Gemini-safe tool schema sanitizer
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(antigravity-cli): clear agy's stale MCP tool-schema cache on install
agy caches each MCP server's tool schemas under
~/.gemini/antigravity-cli/mcp/<server>/ and does NOT refresh them on reconnect
(verified on agy 1.0.6 against a live Windows install). A cache captured by a
context-mode older than the Gemini-safe-schema fix (
|
||
|
|
8db9102b57 |
chore(insight): drop pricing + platform URL, remove stale root docs
Keep context-mode.com/insight as the single source of truth for the ctx_insight tool/CLI/skill/README copy — remove the platform.context-mode.com sign-in line and the $20/seat pricing, which can change. Also delete stale root docs: release-notes-v1.0.148.md, llms-full.txt, llms.txt. |
||
|
|
11b8b58701 |
feat(insight): pivot ctx_insight from local dashboard to hosted product
Insight is now a hosted B2B product at context-mode.com/insight (sign-in + org analytics at platform.context-mode.com). The local build-and-serve dashboard is removed; ctx_insight (MCP tool + CLI) now opens the hosted URL in the default browser via the shared openBrowserSync helper. - server.ts: replace the ~250-line build/spawn/port handler with a browser-open handler; drop _insightChild tracking, ChildProcess/execSync imports, and the shutdown kill hook. Keep openBrowserSync/killProcessOnPort helpers and the ctx_upgrade legacy insight-cache cleanup (#469). - cli.ts: 'context-mode insight' opens the hosted URL. - openclaw: update ctx_insight stub description. - Remove the insight/ dashboard app and its package.json files entries. - Tests: drop the port-schema, containment, and CORS suites for the removed local server; keep helper, #469, and #697 description tests. - Docs: README + ctx-insight SKILL point at the hosted dashboard. |
||
|
|
9f63a0b2c1 |
fix(hooks): capture Stop turn ends (#773)
fix(hooks): capture Claude stop turn ends |
||
|
|
1b4f2be855 | feat(adapter): add Kimi Code CLI hook integration (#729) | ||
|
|
b51873fdc0 | feat(cli): add indexing and search commands (#721) | ||
|
|
a90d399c6a |
fix(codex): set platform env for plugin MCP server (#722)
fix(codex): set platform env for plugin MCP |
||
|
|
9e29a940a1 |
fix(server): comprehensive ctx_* tool description audit + WebFetch refusal (substitutes #654) (#683)
* fix(server): replace "blocked" wording in WebFetch refusal with imperative retry hint (substitutes #654)
Three redirect messages in hooks/core/routing.mjs (curl/wget, Inline HTTP,
WebFetch) reframed from the negation-heavy "blocked" voice to imperative-
positive "redirected (NOT a network restriction)" — and crucially append
"— retry if it fails with a transient DNS error" so the next action is
explicit across all model tiers.
PR #654 (contributor) correctly identified Opus 4.6's "blocked → capitulate
to training" failure mode under the EAI_AGAIN cascade. Our internal A/B
audit (Probe 3, 6 trials Haiku) confirmed the fix on Opus but uncovered
a 2/6 Haiku regression — the parenthetical "(NOT a network restriction)"
landed as information without a paired action, and 2 trials concluded
"since the redirect isn't a restriction either, I can just use training
data." Audit recommended appending the imperative retry clause — this
substitute ships exactly that.
Sibling-tool consistency on ctx_fetch_and_index:
- ssrfGuard pre-flight DNS path: classify EAI_AGAIN / ETIMEDOUT /
ETIMEOUT / ENETUNREACH / EPERM as transient and append the same
retry hint. Non-transient codes (ENOTFOUND) stay silent — retry
won't help on a genuinely bad domain.
- Subprocess fetch stderr path: closes the contributor's flagged
"Known follow-up" (batch wrapper bypassed the single-URL hint).
Same code regex on result.stderr — same retry hint surfaces in the
common multi-URL batch case the original PR couldn't reach.
Tests updated, no new test files (CONTRIBUTING L275):
- tests/hooks/core-routing.test.ts: assert "redirected" + retry hint,
explicit negative-assert .not.toContain("blocked") regression guard.
- tests/hooks/cursor-hooks.test.ts, tool-naming.test.ts: wording sync.
- tests/hooks/integration.test.ts: curl-warning regex relaxed to
survive both old "Do NOT use curl" and new "Do NOT retry with curl".
Bundles untouched — CI rebuilds on main push (project_ci_bundles).
Targeted: npx vitest run tests/hooks/ → 438/438 pass.
TypeScript: npx tsc --noEmit clean.
Closes work on #654 (PR closed in favor of this direct-to-next commit).
Audit doc: .cw/ctx-analytics/TOOL-DESCRIPTIONS-AUDIT.md §6.1
Substitute log: .cw/ctx-analytics/PR-654-SUBSTITUTE-LOG.md
* docs(adr): tool description style (ADR-0002) + routing deny reasons (ADR-0003)
ADR-0002 formalizes the structure every ctx_* tool description must
follow (1-line role / WHEN: / WHEN NOT: / RETURNS: / EXAMPLE:), the
forbidden-token list (MANDATORY:, BLOCKED, PREFER X OVER Y, Do NOT,
Never use, SESSION STATE, emoji bullets), and the MUST/SHOULD/MAY
hierarchy reserved for post-call obligations only. Grounded in 38
trials x 6 probes A/B evidence: heavy framing helps ctx_purge on Haiku
(5/5 vs 3/5 parameter fidelity) but hurts ctx_execute selection — one
size does not fit all, so rewrites are probe-gated.
ADR-0003 splits routing deny reasons into CASE A (redirect — supported
via alternative tool) and CASE B (true policy restriction). PR #654's
finding: the bare word "blocked" in WebFetch's CASE A denial was
misread by Opus 4.6 as a network restriction, triggering training-data
capitulation. CASE A MUST use "redirected", state "this is NOT a
network/security restriction", and end with a transient-error retry
hint. CASE B keeps "denied"/"blocked by security policy".
PR #683 (substitutes #654).
* fix(server): apply ADR-0002 voice to 6 ctx_* tool descriptions + contract test
Comprehensive audit of all 11 ctx_* MCP tool descriptions (see
TOOL-DESCRIPTIONS-AUDIT.md). Six tools rewritten per ADR-0002 verbatim
templates; the remaining 5 are unchanged (3 minimal-description
exemptions, 1 MUST-allowed post-call obligation, 1 deferred to a
probe-gated follow-up PR).
HIGH severity (audit §3): voice consistency on the ctx_execute family.
- ctx_execute (src/server.ts:1419): drop "MANDATORY:" opener,
"PREFER THIS OVER BASH", "THINK IN CODE" voice-of-trainer paragraph,
"Do NOT read raw data". Replace with role definition + WHEN: /
WHEN NOT: / RETURNS: / EXAMPLE: sections. ~1200 -> ~700 chars.
- ctx_execute_file (src/server.ts:1755): same shape; drop "PREFER
THIS OVER Read/cat" and "Don't read files into context to analyze
mentally". Probe 2 evidence: disambiguation was already strong;
this is a voice-consistency pass.
- ctx_batch_execute (src/server.ts:3109): drop "THIS IS THE PRIMARY
TOOL", "THINK IN CODE — NON-NEGOTIABLE", and the emoji-bulleted
PARALLELIZE I/O block. Replace with WHEN: / CONCURRENCY: prose.
~1700 -> ~900 chars.
MEDIUM severity (audit §3):
- ctx_search (src/server.ts:2072): drop the SESSION STATE clause
(it is a routing-block.mjs concern; semantic-equivalence proof
in GRILL-Q1-VERDICT.md Round 5). Add explicit WHEN: structure
and a one-line EXAMPLE with batched queries.
- ctx_index (src/server.ts:1900): rewrite "Do NOT use for: log
files..." as a positive WHEN NOT: clause pointing at
ctx_execute_file. Keep the existing WHEN TO USE: header
(transitional alias permitted by ADR-0002).
- ctx_fetch_and_index (src/server.ts:2865): replace
"PARALLELIZE I/O" banner + ✅/❌ emoji bullets with a positive
CONCURRENCY: prose block. ✅/❌ tokenize inconsistently across
Llama/Gemini and act as negative-example leakage (rubric #4 +
Probe 3 evidence).
Regression guard (audit §10.1, folded into existing test file per
CONTRIBUTING.md L282 "Do NOT create new test files"):
- tests/core/server.test.ts: new describe block "tool description
style contract (#683 ADR-0002)" parses every
server.registerTool() block and asserts:
* MUST NOT contain forbidden tokens (MANDATORY:, BLOCKED,
PREFER X OVER Y, Do NOT read/use/pull, Never use,
SESSION STATE, ✅, ❌)
* MUST contain a WHEN: section (WHEN TO USE: accepted)
Exemptions: ctx_stats/ctx_doctor/ctx_insight (minimal by
design), ctx_upgrade (MUST is appropriate for post-call
obligation), ctx_purge (deferred entirely — see below).
- Updated two existing tests that asserted the old wording:
concurrency-field guidance now checks prose form;
PARALLELIZE I/O test now checks CONCURRENCY: section.
Explicitly deferred — ctx_purge:
Probe 4 (5 trials x 2 variants, Haiku) showed the proposed soft
rewrite REGRESSES parameter fidelity 5/5 -> 3/5. Counter-intuitive:
the heavy negative framing (DESTRUCTIVE, REFUSAL RULES, NEVER call
with bare {confirm:true}) actually anchors small models to the
required scope discipline. A follow-up PR must run a tri-LLM probe
(Haiku/Sonnet/Opus) and gate merge on that probe before changing
this tool. Documented inline in tests/core/server.test.ts via
EXEMPT_FROM_FORBIDDEN_TOKENS with rationale.
Verification:
- npx tsc --noEmit: clean
- tests/core/server.test.ts: 361/364 pass (3 pre-existing failures
unrelated to this PR — confirmed via git stash diff)
- tests/hooks/: 438/439 pass (1 skipped, unchanged)
- tests/adapters/: 787/787 pass
PR #683 (substitutes #654). See docs/adr/0002 and docs/adr/0003.
* fix(hooks): apply ADR-0002 + ADR-0003 contract to routing-block.mjs + lock with regression test
Extends PR #683 to the highest-blast-radius prompt surface in the project:
hooks/routing-block.mjs ships into the system prompt of every session, while
src/server.ts tool descriptions only fire at tool-selection time. The original
PR #683 scope cleaned up the per-tool surface and the routing.mjs deny reasons
but missed the system-prompt surface itself.
Three forbidden-token violations rewritten per ADR-0002 rubric #2 (affirmative >
negative) + #9 (cross-LLM Constitutional AI safety bias):
- <forbidden_actions> XML container -> <when_not_to_use>; the container name
itself is a Constitutional AI trigger on Anthropic-tier models.
- "NEVER use ctx_execute ... for file writes" -> descriptive form
"File writes use the native Write or Edit tool -- ctx_execute,
ctx_execute_file, and Bash subprocesses do not persist edits to the host
filesystem." Same operational intent, no forbidding voice.
- "Write artifacts ... NEVER inline" -> "Write artifacts ... to files. Return
only: file path + 1-line description."
Semantic-equivalence verified by enumerating all 16 directives in the current
block and mapping each to its rewrite (0 orphans, 0 additions). Net character
delta: -76 chars. RFC 2119 MUST kept in <priority_instructions> per the
ADR-0002 post-call-obligation carve-out.
Adds a sibling contract describe block to tests/core/server.test.ts:
"hook routing prompt-surface contract (#683 ADR-0002 + ADR-0003)". Folded
into the same file per CONTRIBUTING.md L282 (no new test files). Scans:
- hooks/routing-block.mjs and hooks/core/routing.mjs for forbidden tokens
(<forbidden_actions>, NEVER, FORBIDDEN, "NO X for Y" bullets).
- Every "redirected"-bearing template literal in routing.mjs for ADR-0003
CASE A compliance: MUST open with "redirected", MUST NOT contain bare
uppercase BLOCKED, MUST name at least one ctx_* alternative tool.
15 assertions total. CASE B strings (Blocked by security policy: ...)
correctly excluded by the extractor. This is the contract test ADR-0003
Consequences L79-82 invited as follow-up.
Three tests/hooks/core-routing.test.ts assertions and one tests/core/
server.test.ts hook-injection assertion updated to match the new positive
wording (same semantic coverage, new container name).
Full regression sweep: 841 passing / 1 skipped / 3 pre-existing storage-
roots failures (verified by git stash on the branch HEAD; out of scope per
PR #683 body).
* fix(server): apply ADR-0002 canonical structure to ctx_purge + 4 ctx_* tools (PR #683 WS2/WS3)
WS2 — ctx_purge rewrite (audit §6.5, Probe 4 evidence preserved):
- Replace negative flat framing (DESTRUCTIVE/REFUSAL RULES/NEVER) with the
canonical WHEN/WHEN NOT/SCOPES/CONTRACT/RETURNS/EXAMPLE structure.
- Preserve all four refusal rules verbatim under CONTRACT (confirm:false,
sessionId+scope ambiguity, scope:'session' without sessionId, deprecated
bare {confirm:true}) so Probe 4 parameter-fidelity discipline holds on
Haiku (5/5 baseline must not regress).
- Keep DESTRUCTIVE headline as accurate user-facing signaling (distinct
from the cross-LLM-bias negative framing the ADR-0002 rubric forbids).
- Add two EXAMPLE lines for the two valid input shapes (per-session +
per-project) so the LLM has explicit parameter templates.
- Add WHEN NOT clause covering the ambiguous-scope handler ("User says
'reset'/'clear'/'wipe' without naming a scope -> ask first").
WS3 — corpus-wide canonical structure pass:
- ctx_index: add EXAMPLE: line (was missing); fold the path-hash sentence
into the RETURNS block so the canonical four-section shape holds.
- ctx_search: drop the non-canonical TIPS: header (fold into RETURNS
prose); add explicit WHEN NOT clauses (empty-index redirect, single
one-off question -> ctx_execute).
- ctx_fetch_and_index: drop the non-canonical CONCURRENCY: header (fold
the I/O-bound split into the WHEN clause; fold the SQLite single-writer
note into RETURNS); add WHEN NOT clauses (local content -> ctx_index,
SPA-rendered content -> headless browser).
- ctx_batch_execute: drop the non-canonical CONCURRENCY: header (fold the
I/O-bound guidance into WHEN; fold the CPU-bound + stateful guidance
into WHEN NOT).
Section order on all six routing-target tools is now strictly
WHEN -> WHEN NOT -> RETURNS -> EXAMPLE per ADR-0002 §Canonical structure.
Bullets are markdown '- ' only. ctx_stats/ctx_doctor/ctx_upgrade/ctx_insight
remain minimal one-line diagnostic descriptions (exempt).
Empirical reference: TOOL-DESCRIPTIONS-AUDIT.md §6.1 (ctx_purge Probe 4),
audit §3 row-by-row standardization verdicts.
* test(server): lock canonical-structure contract + amend ADR-0002 (PR #683 WS3)
ADR-0002 amendment (docs/adr/0002-tool-description-style.md):
- Add ### Canonical structure (locked rubric — PR #683 WS3) subsection
with seven numbered rules (section order, bullet uniformity, header
casing, indent, blank-line spacing, single canonical EXAMPLE per tool,
per-tool carve-out allow-list).
- Add ### Cross-LLM rationale subsection citing the tokenizer-uniformity
argument across Claude / GPT / Gemini / Llama as the empirical basis
for the UPPERCASE+colon header shape.
- Update ### Exemptions and ## Consequences to reflect that ctx_purge is
no longer deferred — the WS2 rewrite ships with audit-approved
DESTRUCTIVE/SCOPES/CONTRACT carve-outs allow-listed in the contract
test, while still meeting the canonical four-section shape.
Contract test extensions (tests/core/server.test.ts):
- Remove ctx_purge from EXEMPT_FROM_FORBIDDEN_TOKENS and EXEMPT_FROM_WHEN
(the WS2 rewrite passes the canonical structure with the carve-outs).
- Add ALLOWED_EXTRA_SECTIONS map carving out DESTRUCTIVE/SCOPES/CONTRACT
on ctx_purge only, with inline rationale citing Probe 4.
- Add four new per-tool assertions (run on every non-exempt ctx_* tool):
1. MUST contain RETURNS: and EXAMPLE: (mandatory presence).
2. Section order WHEN -> WHEN NOT -> RETURNS -> EXAMPLE (strictly
increasing flat.indexOf() positions for canonical sections).
3. UPPERCASE+colon headers must be in the canonical set OR the
per-tool carve-out list (rejects off-spec sections like CONCURRENCY:
and TIPS:).
4. Bullets must be markdown '- ' only (rejects 1./1-/* /•).
- Add flattenDescription() helper that collapses the literal '\n' escapes
and joins the "+ \n " concat continuation so the assertions run against
the shape the host LLM actually sees at tool-selection time.
Two stale-test updates (folded CONCURRENCY: into WHEN: prose):
- "tool description documents the concurrency field with positive
guidance" — expect 'parallelize I/O-bound calls' + 'concurrency 4-8'
+ 'CPU-bound or stateful' + 'keep concurrency at 1' (inline now).
- "PARALLELIZE I/O guidance + locked requests:[] schema in description"
— expect 'requests: [{url' + 'concurrency 4-8' + 'FTS5 indexing then
serializes writes' (inline now).
CONTRIBUTING.md L282 compliance: all assertions folded into the existing
tests/core/server.test.ts file; no new test files.
Result: tests/core/server.test.ts goes from 88 to 124 contract assertions
across 7 non-exempt ctx_* tools; all 124 pass. Three pre-existing baseline
failures (ctx_index storage-error + 2 ctx_doctor settings.json) are
environment-specific and not introduced by this PR.
Empirical reference: PR-683-FINALIZE-LOG.md (WS1 verdict table, WS2 probe
design, WS3 before/after section structure).
* fix: skip context-mode redirect echoes in isToolError + rename forbidden_actions test anchor
PR #683 CI failed across all 3 OS on two tests, both downstream of this
PR's own intentional changes:
1. tests/session/continuity.test.ts:79 'outputs additionalContext with
XML routing block' — Expected <forbidden_actions> tag.
The PR renamed <forbidden_actions> → <when_not_to_use> in hooks/
routing-block.mjs (ADR-0002, affirmative framing — describe when NOT
to reach for a tool instead of declaring it forbidden). The
continuity test still asserted on the old name. Update the assertion
to match the new tag + cross-reference ADR-0002 in the failure
message so a future maintainer who runs `npm test` sees the rename
instead of a bare diff.
2. tests/opencode-plugin.test.ts:1241 'blocked tool command is replaced
before execution' — expected snapshot=="", got <session_resume
events="1"> containing a fake <errors count="1"> with our own echo
text.
The PR rewrote the curl/wget/inline-HTTP/WebFetch redirect echo
from "context-mode: curl/wget blocked. …" to user-friendlier
"context-mode: curl/wget redirected … retry if it fails with a
transient DNS error. …". The new copy legitimately mentions failure
modes ("fails", "transient DNS error"), but `isToolError` at
src/session/extract.ts:63 keyword-matches /FAIL/i and `failed/i`
(case-insensitive, no word boundary), so "fails" inside "if it
fails" triggered a substring match → our OWN guidance echo was
captured as a session error → next chat would show a fake error in
<session_resume>.
Fix: gate isToolError on the unique `context-mode:` prefix. The
check is defensive at the source — any future copy change to the
guidance text cannot reintroduce the bug. Match BOTH sides because
real shell runs report `response = "context-mode: …"` (the echo
stdout), while the OpenCode plugin test path captures `response =
'echo "context-mode: …"'` (the raw command itself, never executed).
Verified locally on Node 20:
npx vitest run tests/session/continuity.test.ts -t "outputs additionalContext"
→ 1 passed
npx vitest run tests/opencode-plugin.test.ts -t "blocked tool command"
→ 1 passed
npx vitest run tests/session/ (all 28 files)
→ 594 passed | 4 skipped, no regressions
* fix(routing): drop negation framing from CASE A deny reasons (#683)
All four redirect-style deny reasons in hooks/core/routing.mjs (curl/wget,
inline HTTP, build tools, WebFetch) rewritten to fully positive imperative
voice per ADR-0002 + ADR-0003.
- Removed "(context-window optimization, NOT a network restriction)"
- Removed "Do NOT retry with curl/wget|Bash|WebFetch"
- Replaced "retry if it fails" hedge with imperative "Retry the same call
on a transient DNS error (EAI_AGAIN, ETIMEDOUT, ENETUNREACH)"
Cross-LLM rationale: negation framing primes LLM attention on the
forbidden item (ironic process theory). Positive routing intent +
explicit capability affirmation + imperative next-action work uniformly
across Claude/GPT/Gemini/Llama.
ADR-0003 amended with §Amendment noting the empirical rationale.
Contract tests in tests/core/server.test.ts gain two guards (PR #683
follow-up) that fail loud if "NOT a network" or "Do NOT retry" reappear.
* test(hooks): update WebFetch + curl deny assertions for affirmative voice (#683)
|
||
|
+2 |
3d8db08bd7 |
feat: add runtime storage override (#617)
* feat: add runtime storage override Co-authored-by: Codex <noreply@openai.com> * test: update storage path source assertions Co-authored-by: Codex <noreply@openai.com> * test: normalize storage path expectations Co-authored-by: Codex <noreply@openai.com> * fix runtime storage override resolution Consolidate storage overrides on CONTEXT_MODE_DIR, share resolver behavior across server, hooks, and statusline, and update docs/tests around the Codex Desktop failure contract. Co-authored-by: Codex <noreply@openai.com> * fix windows storage override tests Make storage-path resolver expectations platform-aware and keep statusline multi-adapter tests on adapter-default discovery instead of the root override path. Co-authored-by: Codex <noreply@openai.com> * address runtime storage review Compose tool registration wrappers, memoize storage writability checks, report storage roots in ctx_doctor, keep legacy statusline session-dir compatibility, and fold resolver tests into existing server coverage. Co-authored-by: Codex <noreply@openai.com> * fix windows storage path test Use an absolute temp-directory fixture instead of a POSIX-rooted default path so resolver tests assert storage behavior consistently across platforms. Co-authored-by: Codex <noreply@openai.com> * fold storage resolver into session db Move CONTEXT_MODE_DIR resolver exports into the existing session DB module so hooks and statusline can use the existing session-db bundle. Drop the new storage-paths source and hook bundle entries while preserving shared storage behavior. Co-authored-by: Codex <noreply@openai.com> * consolidate default storage session roots Add the shared default session-dir helper to the existing session DB bundle boundary, route server/hooks/statusline through it, and document why storage resolution lives there. Expand storage resolver tests for the shared helper. Co-authored-by: Codex <noreply@openai.com> * fix codex default session guard Update the source assertion for the shared default-session-dir helper so the CI guard checks CODEX_HOME via configDirEnvForSessionSegments instead of the removed direct Codex config import. Co-authored-by: Codex <noreply@openai.com> * Fix Claude plugin skills path and pack integrity guard (#661) * ci: update server.bundle.mjs, cli.bundle.mjs, session hook & security bundles * ci: update install stats * ci: update install stats * ci: update install stats * Fix Claude plugin skills manifest path --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix(pi): prevent mixed-width TUI over-width crashes (CJK/Korean/emoji) (#676) * fix(pi): CJK wide-character width-aware truncation in PiTextComponent (#665) PiTextComponent/truncateAnsiLine counted every JS character as width 1, but CJK ideographs occupy 2 terminal columns. This produced lines whose actual visibleWidth exceeded the requested width, triggering a pi-tui crash: 'visible width: 162 > terminal width: 147'. Root cause: truncateAnsiLine() iterated chars with visible++ (always 1) instead of accounting for east-asian-width W/F codepoints. Fix: - Add charWidth() helper that returns 2 for CJK/Hangul/fullwidth ranges. - Change truncateAnsiLine to use charWidth and check visible + w > maxWidth (not >=), so a 2-wide char still fits when exactly 2 columns remain. - Export PiTextComponent and truncateAnsiLine for testability. Tests (Slice 10 in pi-mcp-bridge.test.ts): - Pure CJK text respects requested width. - Mixed ASCII + CJK is correctly truncated. - ANSI escape sequences are preserved and not counted toward width. - The real crash line from pi-crash.log fits within terminal width 147. - Edge case: maxWidth 0 or negative returns empty string. Fixes #665 * test(asymmetric-drift): make npm pack dry-run windows-safe --------- Co-authored-by: baifan <ubuntu@BaiFanPC.localdomain> * fix(server): add z.preprocess coercions to ctx_fetch_and_index force and requests params (#679) fix(server): add z.preprocess coercions to ctx_fetch_and_index params The OpenCode/Kilo in-process native plugin bridge stringifies primitive types. Other tools already use z.preprocess(coerceBoolean/coerceJsonArray) to handle this, but ctx_fetch_and_index was missing these wrappers on its force and requests parameters. Added schema-level test verifying preprocess wrappers are present. Updated existing schema tests to match new structure. * feat(fetch): add per-call cache ttl (#666) * feat(fetch): add per-call cache ttl * test: run npm pack dry-run on Windows * fix(batch_execute): preserve heredoc commands (#657) Stops appending `2>&1` to user command strings in `runBatchCommands()` — that mutation broke heredoc terminators (`NODE` → `NODE 2>&1`). Now executes commands as-written and merges executor-captured stdout+stderr via new `combineExecOutput()` helper. Symmetric across serial + parallel paths. Adds serial + parallel regression tests for heredoc commands with stderr. Updates the nodeOptsPrefix edge-case test to reflect the new behavior. Bundles taken from `next` (CI-authoritative); regenerate on next main push. Fixes #656. Co-Authored-By: Noctivoro <nick@movermarketing.ai> * fix(server): surrogate-safe preview truncation in ctx_fetch_and_index (#659) (#660) The fetch-preview path at src/server.ts truncated `f.markdown` with `String.prototype.slice(0, FETCH_PREVIEW_LIMIT)` where the limit (3072) is a UTF-16 code-unit count. When the cut fell between the two halves of an astral-plane character (e.g. 🟡 = U+1F7E1 = 🟡), the high surrogate remained and the low surrogate was dropped. JSON.stringify then emitted the orphan as a literal `\uD83D` escape in the tool_result body, causing RFC 8259-strict consumers (the host LLM API) to reject the next request with `400 ... no low surrogate in string`. Sessions could not recover without removing the bad message from the transcript. Adds a new `charSafePrefix(str, maxChars)` export to `src/truncate.ts` mirroring the existing internal `byteSafePrefix` semantics: cap by UTF-16 code units, back off one unit if the cut would split a surrogate pair. Wires it into the fetch-preview construction. Tests cover the helper directly plus a regression that walks the exact preview-construction pattern with an emoji at the LIMIT boundary and asserts the resulting JSON contains no orphan high surrogate and round-trips through a strict parser. Other `.slice(0, N)` sites in src/ (small label/error/timestamp truncations) are out of scope for this PR — they have low bounds and their inputs are unlikely to contain emoji at the boundary. Happy to extend coverage in a follow-up if the maintainer wants to remove the class of bug entirely. Fixes #659 * fix(auto-memory): scope memory dir by projectDir to stop cross-project leak (#663) (#664) * fix(auto-memory): scope memory dir by projectDir to stop cross-project leak (#663) getMemoryDir() ignored projectDir, so every adapter (except OpenClaw, whose configDir IS the project root) returned a path shared by every project on the machine. Two terminals open in different repos read each other's .md memory files via searchAutoMemory(), then those notes contaminated ctx_search timeline results. Fix: HookAdapter.getMemoryDir now accepts an optional projectDir. When supplied, the path is scoped via hashProjectDirCanonical(projectDir) under the existing base. searchAutoMemory passes projectDir through; the adapterless legacy fallback applies the same hash directly so the contract holds at both call sites. Reuses the same canonicalization as ContentStore and searchEvents, so all three project-scoped surfaces share one identity. Backwards compat: getMemoryDir() without projectDir keeps returning the unscoped path so external adapter consumers don't break. Tests: leak canary (write under projectA scope, search from projectB, assert 0 hits) + positive control + #663 negative test pinning that the old unscoped path is no longer surfaced. * test(windows): fix two flaky Windows-only CI failures - asymmetric-drift-assert: spawnSync("npm", ...) needs shell:true on Windows because npm resolves to npm.cmd. Without it Node returned status=null/stderr=null/stdout=null and the assertion failed with no diagnostic. Also surface r.error in the assertion message. - server.test.ts "python: cap works with python scripts": replace the 10k-iteration Python loop with a single 100KB write. The cap still triggers (and stderr still gets "output capped"), but the test no longer races the 10s timeout on slow Windows CI VMs. --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Baijack-star <71923891+Baijack-star@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: ByF <222546298+ByronFinn@users.noreply.github.com> Co-authored-by: baifan <ubuntu@BaiFanPC.localdomain> Co-authored-by: LeoNardo <58056860+LeoNardo-LB@users.noreply.github.com> Co-authored-by: NgoQuocViet2001 <123613986+NgoQuocViet2001@users.noreply.github.com> Co-authored-by: /noctivoro-x <nick@movermarketing.ai> Co-authored-by: ccheng555 <ccheng5@gmail.com> Co-authored-by: Seba Breguel <62109266+sebastianbreguel@users.noreply.github.com> Co-authored-by: Mert Koseoglu <bm.ksglu@gmail.com> |
||
|
|
3e51e53ba1 |
feat(opencode): streamline installation process and improve CLI upgrade (#650)
* feat(opencode): streamline installation process and improve CLI upgrade Remove redundant global npm install step from installation instructions for both OpenCode and KiloCode integrations. Refactor CLI upgrade command to omit global package updates for these platforms. * refactor: extract opencode/kilo platform check into helper Use helper in getPluginRoot and upgrade to gate global npm install Add test for gate placement in upgrade --------- Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com> |
||
|
|
ccd5911e2c |
docs(readme): remove dead Lifecycle env-var section + complete redaction list
Two adjacent README claims drift away from the code on origin/next: 1. The "Lifecycle environment variables" section documents `CONTEXT_MODE_STARTUP_SWEEP` as a default-on runtime knob. The env var is not read anywhere in src/, hooks/, scripts/, or tests/ on the current tree — the underlying `startupSiblingSweep()` call site was reverted in #602 ("revert: lifecycle idle-shutdown") along with the idle-shutdown class it was paired with. The same #602 commit body explicitly states: "Neither env var is read anywhere in src/, tests/, or scripts/ after the revert — leaving the README section in place would leak dead configuration knobs to users." #602 did delete the section; commit |
||
|
|
0c838a3dea |
docs(readme): clarify plugin+legacy-mcp coexistence in OpenCode/KiloCode sections
Adds symptom→cause→fix guidance for the v1.0.137-139 silent-suppression edge case (#623). Mentions v1.0.140+ stderr diagnostic so users hitting 'zero ctx_* tools' can find their way to the upgrade command. |
||
|
|
eb1a629ed8 | docs(opencode): note plugin-native upgrade cleanup (#619) | ||
|
|
09efefdc4a |
feat(opencode): register ctx tools natively via plugin (#574) (#597)
Move OpenCode/Kilo from plugin+MCP dual registration to plugin-native ctx_* tools. The plugin now imports the shared server tool registry without starting stdio, exposes all 11 ctx_* tools via the OpenCode/Kilo tool map, and uses AsyncLocalStorage to pass project/session context into existing handlers without a process.env race. Upgrade safety for existing users: - configureAllHooks removes only legacy mcp.context-mode while preserving other MCP servers. - doctor warns when a legacy mcp.context-mode block remains and points to context-mode upgrade. - stale legacy OpenCode/Kilo MCP children suppress ctx_* registration and become no-op rather than exposing duplicate tools. Safety cleanup: - Remove CONTEXT_MODE_IDLE_TIMEOUT_MS entirely; plugin-native tools remove the need for timer-driven MCP death, and timer shutdown was unsafe for hosts that keep registered tool handles. - Guard process-wide exception handlers so importing server.js for native tools does not alter OpenCode/Kilo host crash semantics. - Scope CONTEXT_MODE_EMBEDDED_PLUGIN_TOOLS to the dynamic import and restore it so child commands do not inherit the internal guard. Tests cover native tool registration, native ctx_stats execution, host side-effect leakage, native session attribution, legacy MCP config cleanup, doctor warning, and stale MCP no-op predicate. Refs: #574, #565, #592 Co-authored-by: Ousama Ben Younes <benyounes.ousama@gmail.com> |
||
|
|
a888cac640 |
revert: lifecycle idle-shutdown (#568 + #595) — restore tools on 12 hosts (#602)
* Revert "fix(lifecycle): make MCP idle shutdown opt-in outside OpenCode/Kilo (#592) (#595)" This reverts commit |
||
|
|
572d451f8d |
fix(codex): harden plugin hook routing (#575)
* fix(claude-config): static-import detect to fix Node 22.5 require(esm) gate
CI run 25877550371 failed on all 3 OS with 12 silent-empty assertion
errors in tests/security.test.ts > cross-adapter deny-policy parity:
every per-adapter test returned `policies = []` instead of the seeded
deny pattern. Local Node 20.19.2 + Node 22.22.2 both pass the same
tests; only CI's Node 22.5 was broken.
Root cause: `resolveAdapterGlobalSettingsPaths` lazy-loaded
`../adapters/detect.js` via
createRequire(import.meta.url)("../adapters/detect.js")
Both files compile to ESM under `"type": "module"`. `require()` of an
ESM module is FLAG-GATED on Node 22.x before 22.12 — it needs
`--experimental-require-module`. CI's actions/setup-node@v4 install of
Node 22.5 does not pass that flag by default. The require throws
ERR_REQUIRE_ESM, the surrounding try/catch eats it silently, the
function returns `[claudeGlobalPath]` only, and the test's seeded
`<fakeHome>/.cursor/settings.json` is never read → empty policies →
assertion fails on every adapter (cursor, codex, qwen-code, gemini-cli,
jetbrains-copilot, vscode-copilot) × 2 patterns = 12 fails per OS.
Why Node 20 and 22.22.2 passed locally: 20.x has require(esm) under the
same flag but vitest's test runner sets it via NODE_OPTIONS in its node
import; 22.12+ has it default-on. Node 22.5 specifically lives in the
flag-gated window with no implicit enabler — which is exactly the
version CI hopped to in
|
||
|
|
5bcff7eda9 |
fix(lifecycle): make MCP idle shutdown opt-in outside OpenCode/Kilo (#592) (#595)
Issue #592 confirms the #568 idle self-shutdown default is unsafe for Claude Code/Codex-style MCP hosts: after context-mode exits cleanly on idle, the host can keep registered ctx_* tool handles but mark the MCP server disconnected, leaving tools stale until the user reconnects or restarts. #583 fixed Pi by making its bridge respawn, but Claude Code and Codex host MCP clients are outside this repo and cannot be patched from lifecycle.ts. Change the policy from global default-on to host opt-in: - idleTimeoutForEnv({}) now returns 0 (disabled), not 900000. - malformed/negative CONTEXT_MODE_IDLE_TIMEOUT_MS also falls back to 0, the safe default. - positive env values still opt in exactly as before. - OpenCode and KiloCode configs explicitly set CONTEXT_MODE_IDLE_TIMEOUT_MS=900000 because those hosts were the original #565 accumulation case (one MCP child per session/subagent). - README lifecycle docs now describe the conservative default and why Claude Code/Codex/editor MCP clients should not idle-exit unless users explicitly opt in. Regression tests: - lifecycle.test.ts pins missing/malformed env => 0 and explicit env => honored. - opencode-idle-config.test.ts pins OpenCode and KiloCode shipped configs as explicit 15 min opt-ins, preventing accidental reintroduction of a global default or removal of the targeted host opt-in. Verification: - npx tsc --noEmit: PASS - npm run build: PASS - npx vitest run tests/lifecycle.test.ts tests/adapters/opencode-idle-config.test.ts: PASS (26/26) - npx vitest run: same 3 pre-existing environment-dependent failures on this workstation (VSCODE_PID inheritance, JetBrains IDEA_INITIAL_DIRECTORY), unchanged from upstream/next baseline. Refs: #592, #568, #565, #583 Co-authored-by: Ubuntu <omer@Omer.dn3uxh3znnmu5eefnjnut0i1af.tlvx.internal.cloudapp.net> |
||
|
|
7ae9ee7fc3 |
feat: Make context-mode output collapsible (#594)
* ci: update install stats * ci: update install stats * feat: Make context-mode output collapsible * docs: Document collapsible feature for Pi --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com> |
||
|
|
752d5236ec |
fix(routing): re-fire external-MCP nudge periodically, not once per session (#593)
#529 added a one-shot PreToolUse guidance for external MCP tools (slack/jira/notion/gdrive…) telling the agent to wrap large payloads in ctx_execute. In MCP-heavy sessions (#567 follow-up reports 55 Jira calls, context growing 24K → 75K tokens) the single nudge is dropped by context compaction and later tool responses flood the conversation unchecked. Replace `guidanceOnce("external-mcp", …)` with a new `guidancePeriodic` helper that fires on calls 1, N+1, 2·N+1, … with N defaulting to 10. Tunable via `CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY` (range [1, 100], invalid values fall back to 10). Bash/Read/Grep nudges keep their one-shot behavior — they advise on tool *category* usage, not per-call flood pressure. Counter is in-memory + file-backed (`<guidanceDir>/<type>.count`) so it stays coherent across hook process invocations within the same session. On any IO/parse failure we fire (lose a counter rather than silently drop the advisory). Test plan - npx vitest run tests/hooks/core-routing.test.ts → 80 passed - npm run typecheck → clean - New tests cover: default cadence over 22 calls, env-tuned cadence, invalid env value fallback, resetGuidanceThrottle clears the counter. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
61e680f1f3 |
fix(v1.0.132): stats measurement + #563/#564/#567/#569 + PR follow-ups
- stats: populate bytes_avoided for external_ref via ctx_fetch_and_index preamble; indexer chunks now FK-attributed (chunks.session_id/event_id) at all 7 callers in server.ts - #563: drop .refine() from ctx_purge schema (MCP SDK normalizeObjectSchema requires .shape); ambiguity check moved to handler; class-wide CI guard added for all 11 tools - #564: engines.node>=22.5 + scripts/postinstall.mjs hard-fail on Linux+Node<22.5+no-Bun + ctx_doctor RED FAIL + README/docs sync to canonical 22.5 floor - #567: vscode-copilot + jetbrains-copilot mcp.json npx-y -> global context-mode (npx-y was scaffold residue from Mar 2026, ghost-installs bypass user's npm i -g causing better-sqlite3 ABI mismatch) - #569: anti-pattern docs centralized to anti-patterns.md §8 + SKILL.md ref (capture-vs-filter principle, no tool enumeration) - #571 follow-up: vswhere timeout 5s->15s, year regex caps at currentYear+5 - #568 follow-up: documented CONTEXT_MODE_IDLE_TIMEOUT_MS + CONTEXT_MODE_STARTUP_SWEEP env vars; realpath guard in lifecycle-e2e-real-binary.test.ts |
||
|
|
2607f21de2 |
docs(csharp): bump language count 11 to 12 across docs
PR #546 added C# (dotnet-script) as the 12th supported runtime, but the documentation across README, CONTRIBUTING, BENCHMARK, and llms-full kept counting 11 languages and missing C# from the language enumerations and runtime tables. Bring docs in line with the executor's actual capability. |
||
|
|
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" |
||
|
|
ac0c761149 |
feat(kiro): route external MCP tools through PreToolUse (closes #529 partial — kiro)
Extends PR #532's external-MCP routing protection to Kiro. Adds `@(?!context-mode/)` to the preToolUse matcher (Kiro's MCP wire shape is `@<server>/<tool>`, with own tools at `@context-mode/ctx_*` — verified via hooks/core/tool-naming.mjs and src/adapters/kiro/hooks.ts:47-49). The routing.mjs `isExternalMcpTool` extension landed with the cursor slice already covers the `@<server>/<tool>` shape, so this slice only needs the matcher-list addition + config-file mirroring. - src/adapters/kiro/hooks.ts: export EXTERNAL_MCP_MATCHER_PATTERN, append to PRE_TOOL_USE_MATCHERS - configs/kiro/agent.json: mirror runtime matcher - README.md: keep docs in sync - tests/adapters/kiro-external-mcp-routing.test.ts: 6-test vertical slice (matcher regex, adapter config, routing.mjs integration for `@context-mode/ctx_*` vs `@other/foo`) |
||
|
|
742456552f |
feat(qwen-code): route external MCP tools through PreToolUse (closes #529 partial — qwen-code)
Extends PR #532's external-MCP routing protection to Qwen Code. Adds the new `hooks.ts` module exporting `EXTERNAL_MCP_MATCHER_PATTERN = "mcp__(?!.*context-mode)"` and appends it to both `generateHookConfig` and the `configureAllHooks` upgrade path so slack / telegram / gdrive / notion-style MCPs trigger the context-guidance nudge before their large payloads flood context. Verified Qwen's MCP wire shape: as a Gemini fork (packages/core/src/tools/tool-names.ts) it uses `mcp__<server>__<tool>`. Own context-mode MCP surfaces as both `mcp__plugin_context-mode_context-mode__*` (Claude marketplace shim) and `mcp__context-mode__*` (Qwen canonical via tool-naming.mjs). The negative lookahead excludes both. - src/adapters/qwen-code/hooks.ts: NEW — export EXTERNAL_MCP_MATCHER_PATTERN - src/adapters/qwen-code/index.ts: append matcher in both code paths - README.md: keep docs in sync - tests/adapters/qwen-code-external-mcp-routing.test.ts: 3-test vertical slice |
||
|
|
3900db8c4a |
feat(gemini-cli): route external MCP tools through PreToolUse (closes #529 partial — gemini-cli)
Extends PR #532's external-MCP routing protection to Gemini CLI. Adds `mcp__(?!.*context-mode)` to the BeforeTool matcher so slack / telegram / gdrive / notion-style MCPs trigger the context-guidance nudge before their large payloads flood the model's context window. Verified Gemini's MCP wire shape (`mcp__<server>__<tool>`) via hooks/core/tool-naming.mjs (`mcp__context-mode__<tool>` for own MCP). The negative lookahead excludes any `mcp__` server segment containing `context-mode` so both the canonical own-MCP and the Claude shim (`mcp__plugin_context-mode_*`) are skipped. Also broadens the explicit own-MCP matcher to include the canonical `mcp__context-mode` prefix (previously only `mcp__plugin_context-mode` was listed — a latent Gemini-routing gap). - src/adapters/gemini-cli/hooks.ts: export EXTERNAL_MCP_MATCHER_PATTERN - src/adapters/gemini-cli/index.ts: append + broaden own-MCP coverage - README.md: keep docs in sync - tests/adapters/gemini-cli-external-mcp-routing.test.ts: 3-test vertical slice |
||
|
|
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 |
||
|
|
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. |
||
|
|
b465acd834 |
docs(omp): drop hardcoded version from install guide + prune redundant manifest field
The previous manual install path pasted a literal `"version": "1.0.111"`
into a JSON snippet for omp-plugins.lock.json. That number drifts
silently on every release — anyone reading the README a week from
now would copy a stale version into their lock file.
Verified upstream that the snippet was unnecessary in the first
place. The plugin loader at refs/platforms/oh-my-pi/packages/
coding-agent/src/extensibility/plugins/loader.ts:89-94 only consults
the lock file when a plugin is explicitly disabled:
const runtimeState = runtimeConfig.plugins[name];
if (runtimeState && !runtimeState.enabled) continue;
Plugins missing from the lock file load with default-enabled state.
So the manual install collapses to two commands: `cd ~/.omp/plugins`
+ `bun add context-mode`, then restart. No JSON to edit, no version
to pin.
Same logic eliminates the `omp.version` field we had been carrying in
the root package.json. The upstream loader stamps
`manifest.version = pluginPkg.version` from the top-level
package.json:version on every load (loader.ts:87), so duplicating it
inside the omp block adds a drift surface and zero signal. The
matching `pi` block follows the same convention, so consistent.
Drops the corresponding omp.version sync code from
scripts/version-sync.mjs — it can no longer drift if the field
doesn't exist.
|
||
|
|
a783d2b334 |
docs(omp): rewrite install steps as concrete commands + JSON, drop bullet-style explanation
Previous version mixed user-runnable steps with explanatory bullets
("OMP runs bun install...", "OMP reads package.json...") that read
like an internals tour, not an install guide. Replaced with three
discrete numbered paths:
1. Plugin path (recommended) — `omp plugin install context-mode`
+ `omp plugin list` / `omp plugin doctor` verification.
2. Manual plugin path — for cases where `omp plugin install` is
unavailable (older OMP, restricted env, dev workflow). Direct
`bun add context-mode` inside ~/.omp/plugins/ + a lockfile JSON
snippet for omp-plugins.lock.json that flips enabled: true. Both
mechanics verified against upstream manager.ts:158 (bun install
into getPluginsDir()) and types.ts:141 (PluginRuntimeConfig shape).
3. MCP-only path — unchanged, retained as fallback for users who
don't want hooks.
Routing paragraph rewritten to describe what each of the four
`pi.on(...)` handlers actually does, with the upstream
hooks/types.ts:566 link for the block contract.
|
||
|
|
2ddae394c4 |
feat(omp): plugin path with native hook enforcement (HookAPI tool_call/tool_result/session_start/session_before_compact)
Promotes OMP from MCP-only delivery to a proper plugin. `omp plugin
install context-mode` now wires programmatic enforcement equivalent to
Claude Code's PreToolUse/PostToolUse/PreCompact/SessionStart pipeline.
Verified end-to-end against the upstream OMP source cloned to
refs/platforms/oh-my-pi @ v3.20.1 (no LLM trust, every claim
file:line cited):
- Manifest format: `omp` or `pi` field on root package.json
Source: refs/.../extensibility/plugins/loader.ts:75
`const manifest = pluginPkg.omp || pluginPkg.pi;`
+ line 82: `manifest.version = pluginPkg.version;` (loader stamps
version from top-level pkg.version on load — explicit
`omp.version` is belt-and-suspenders, kept synced by
scripts/version-sync.mjs).
- Install command: `omp plugin install <pkg>` runs
`bun install <pkg>` inside ~/.omp/plugins per
refs/.../extensibility/plugins/manager.ts:158, then reads
`~/.omp/plugins/node_modules/<pkg>/package.json` for the manifest.
- HookFactory contract: `(pi: HookAPI) => void` per
refs/.../extensibility/hooks/types.ts:809.
- Block return shape: `{ block?: boolean; reason?: string }` per
refs/.../extensibility/hooks/types.ts:566.
- Event payloads:
- ToolCallEvent (refs/.../hooks/types.ts:448): {toolName, toolCallId, input}
- ToolResultEvent (refs/.../hooks/types.ts:461 onward): {toolName, toolCallId, input, content[], isError}
- Example reference: refs/.../examples/hooks/permission-gate.ts.
What the plugin actually does:
- tool_call: hard-blocks bash containing curl/wget/inline-fetch
(`requests.get`, `http.get`, `Invoke-WebRequest`, etc.) — same
pattern set as the Pi extension.
- tool_result: feeds OMP-shaped events through the existing
extractEvents pipeline → SessionDB at ~/.omp/context-mode/.
- session_start: derives a stable 16-hex session id from
sessionManager.getSessionFile() (or wall-clock fallback), runs
7-day cleanup.
- session_before_compact: persists a buildResumeSnapshot output via
upsertResume + increments compact_count for resume-on-restart.
Reference parity:
- Mirrors src/adapters/pi/extension.ts shape closely. OMP differs in
two ways that justify a dedicated file:
1. Storage at ~/.omp/context-mode/ via OMPAdapter (not ~/.pi/)
2. OMP has native MCP via mcp.json — the Pi extension's
mcp-bridge.ts is dead weight under OMP and is intentionally
omitted here.
- Mirrors src/adapters/openclaw/plugin.ts integration shape (root
package.json field → built JS entry).
Smoke test (run locally before commit):
- pkg.omp.hooks resolves to build/adapters/omp/plugin.js ✓
- default export is a function ✓
- 4 handlers register: session_start, tool_call, tool_result,
session_before_compact ✓
- tool_call({toolName: 'bash', input: {command: 'curl ...'}}) →
{block: true, reason: '...'} ✓
Tests: tests/adapters/omp-plugin.test.ts adds 17 cases across 4 TDD
slices (routing, extraction, session lifecycle, resume snapshot). All
green. Full vitest run: 2642 passed, 20 skipped, 0 failed.
scripts/version-sync.mjs now also stamps package.json:omp.version
when running on `npm version` lifecycle so OMP manifest version
never drifts from top-level pkg.version (verified by simulating a
stale 0.0.0 value and watching it correct to current).
README updated:
- OMP install section reordered: plugin path is now primary, with
upstream file:line citations for the loader and block contract;
MCP-only path retained as the alternative.
- Hook coverage table (lines ~1024-1031): OMP rows promoted from
"--" to ✓ (via tool_call event), etc.
- Platform compatibility table: OMP PreToolUse/PostToolUse/
SessionStart/PreCompact/CanBlockTools all marked Plugin.
- Routing-enforcement note: OMP moved from non-hook list to
hook-capable list.
- All "OMP MCP-only / no hook integration" prose paragraphs
rewritten.
|
||
|
|
a8a3e6c36a | docs(platforms): bump 14 → 15 across README + web — count OMP officially | ||
|
|
755697c7ca |
fix(omp): correct env var, MCP filename, and instruction file per upstream evidence
The OMP adapter and README were written from secondary sources and
diverged from the actual oh-my-pi runtime. Verified against
can1357/oh-my-pi v3.20.1 by cloning the repo into refs/platforms/
and reading source line-by-line:
- Env var: OMP_PROCESSING_AGENT_DIR → PI_CODING_AGENT_DIR
Source: packages/utils/src/dirs.ts:193
`let dirs = new DirResolver(process.env.PI_CODING_AGENT_DIR);`
No `OMP_*` runtime env exists; `OMP_*` keys are only mirrored
to `PI_*` from .env files (packages/utils/src/env.ts:38-41),
not from process.env.
- MCP config filename: mcp_config.json → mcp.json
Source: packages/utils/src/dirs.ts:455-459 (getMCPConfigPath)
docs/mcp-config.md:17-18 ("User: ~/.omp/agent/mcp.json")
Both user-scope (~/.omp/agent/mcp.json) and project-scope
(.omp/mcp.json) are documented loading paths.
- Instruction file: PI.md → SYSTEM.md (+ AGENTS.md)
Source: packages/coding-agent/src/main.ts:394-402 reads
.omp/SYSTEM.md (project) and ~/.omp/agent/SYSTEM.md
(global).
packages/coding-agent/src/discovery/agents-md.ts:25-29
also auto-discovers AGENTS.md walking up from cwd.
Zero `PI.md` matches in the upstream repo — the previous
naming was speculative.
- Hook surface: "no hook support" framing → MCP-only delivery
Source: packages/coding-agent/src/extensibility/hooks/types.ts:695
(`export interface HookAPI`)
packages/coding-agent/examples/hooks/auto-commit-on-exit.ts
(`pi.on("session_shutdown", ...)`)
OMP DOES expose pre/post tool-call hooks. Our adapter still
delivers via MCP only — but as our scoping choice, not OMP's
limitation. Wiring OMP-native hooks is future work.
Also creates the missing `configs/omp/` shipped artifacts:
- configs/omp/SYSTEM.md (routing rules; the file the README's
`cp` step now actually has to copy from)
- configs/omp/mcp.json (MCP config snippet, parity with antigravity)
README.md OMP section rewritten to match other adapters' format —
no link, no "isolated storage under" prose in the title; install
flow is JSON config + SYSTEM.md copy + restart.
Tests updated and passing (77 OMP + detect tests, full suite
baseline preserved at 13 pre-existing flakes, 0 net new).
|
||
|
|
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> |
||
|
|
7b86ee5a20 |
feat(cursor): add Marketplace plugin packaging (#489)
* feat(cursor): add Marketplace plugin packaging
Mirror the Claude Code plugin layout for Cursor's plugin marketplace:
- .cursor-plugin/plugin.json: manifest pointing at ./configs/cursor/context-mode.mdc, ./skills/, ./hooks/cursor/hooks.json, and an MCP server entry running 'npx -y context-mode'.
- hooks/cursor/hooks.json: registers preToolUse, postToolUse, sessionStart, afterAgentResponse, and stop, all dispatched through 'npx -y context-mode hook cursor <event>' so users do not need a local clone.
- src/adapters/cursor/index.ts: doctor now detects plugin installs under ~/.cursor/plugins/{local,cache} and warns when both the plugin and a native .cursor/hooks.json register context-mode hooks.
- scripts/version-sync.mjs: keeps .cursor-plugin/plugin.json in lockstep with package.json.
- README.md, docs/platform-support.md: document the Marketplace install path alongside the existing manual install.
Refs #485
* feat(cursor): add plugin README + drop non-schema displayName field
- Add .cursor-plugin/README.md so the Marketplace tile has a dedicated landing page (project root README is unchanged).
- Remove 'displayName' from .cursor-plugin/plugin.json: the field is not in Cursor's plugin manifest schema (https://cursor.com/docs/reference/plugins) and would be flagged by the validator.
Validated all manifest keys against the official schema; no other extra fields. Cursor adapter test suite: 50/50 pass.
* feat(cursor): add Marketplace logo
Adds .cursor-plugin/assets/logo.png and references it via the manifest 'logo' field. Cursor resolves relative paths to raw.githubusercontent.com URLs at the commit SHA, so the Marketplace tile renders the snowflake icon directly from the repo.
* docs(cursor): add local-install quickstart for testers
Document the robocopy/symlink workflow so reviewers (and early adopters) can try the plugin from the repo before Marketplace acceptance. Calls out the Windows symlink limitation explicitly so testers do not waste time debugging mklink.
* docs(cursor): mark Marketplace plugin as work-in-progress until review
Per maintainer feedback: until Cursor's review team lists the plugin, the README needs an explicit 'work in progress' notice plus copy-pasteable local-install commands for both Windows (robocopy) and macOS/Linux (ln -s). Calls out the Windows symlink limitation directly so testers do not waste time debugging mklink.
Refs #485, #489
---------
Co-authored-by: Maxwell_sun <Maxwell_sun@noreply.gitcode.com>
Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com>
|
||
|
|
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> |
||
|
|
84b8d15c0f |
feat(adapters): add OMP (Oh My Pi) platform adapter (#473) (#480)
* feat(adapters): add OMP (Oh My Pi) platform adapter (#473) Issue #473 reported that switching from Pi to OMP left context-mode data under ~/.claude/ instead of an OMP-rooted directory. Audit shows session DB / stats / content paths already follow the adapter's sessionDir, but OMP had no adapter — detection fell through to Pi or to the default Claude fallback, so storage rooted at ~/.claude/. Adds a dedicated OMPAdapter (mcp-only paradigm, modeled on AntigravityAdapter) with sessionDirSegments=[".omp"], wires OMP_PROCESSING_AGENT_DIR detection BEFORE pi in PLATFORM_ENV_VARS, adds the same precedence to the ~/.omp/ vs ~/.pi/ config-dir tier, and threads "omp" through PlatformId, getSessionDirSegments, the CONTEXT_MODE_PLATFORM allowlist, and getAdapter(). Honors OMP_PROCESSING_AGENT_DIR for the MCP settings root (defaults to ~/.omp/agent), so OMP installs that relocate their agent dir keep working. No new flag, no env-var override layer — purely closes the adapter gap that produced the misrouted storage. Note: PiAdapter still does not exist; pi falls back to the ClaudeCodeAdapter via the default branch in getAdapter(). That is a separate pre-existing bug — out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(adapters): document OMP adapter in README/platform-support/CONTRIBUTING (#473) Addresses maintainer review on #480: any contribution that adds a new adapter must update the user-facing docs in the same PR so the support matrix, install instructions, and architecture surface stay in sync. - README.md: new <details> install block for OMP after Pi; OMP column added to the 5-hook session continuity table and the Platform Compatibility table; per-platform notes paragraph and the routing-instructions caveat both mention OMP. - docs/platform-support.md: bumped "twelve" → "thirteen" platforms; added OMP to the MCP-only paradigm row, the Main Comparison Table, and the Capability Matrix; new Platform Details section for OMP covering hook support (none), path resolution, detection priority, and the issue-473 motivation. - CONTRIBUTING.md: added omp/ to the src/adapters/ directory tree. Code paths unchanged. Full vitest suite still green (2450 passed, 25 skipped); typecheck clean; pre-existing kiro-hooks worker termination flake is unrelated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: retrigger flaky macOS better-sqlite3 dlopen --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c3d0467923 |
fix(#413): /resume snapshot fallback in SessionStart hook
Per CC docs, --continue, --resume, and /resume all fire SessionStart with source="resume" + the ACTIVE session_id. For /resume the active id is a *fresh* uuid for the resumed conversation, so live-events lookup misses even when prior context exists. The resume branch now falls back to db.claimLatestUnconsumedResume(currentSessionId) — same pattern OpenCode and OpenClaw plugins already use (opencode-plugin.ts:454). Live-events path keeps precedence; snapshot only surfaces when no live rows match the incoming session_id (the /resume case, or when the prior session was previously compacted). Tests: - "falls back to snapshot when resumed session has no live events" - "prefers live events over snapshot when both exist (--continue stays correct)" README: - Session Continuity intro now lists --continue, --resume, AND /resume - Adds one-paragraph note on snapshot fallback for non-latest /resume picks - Claude Code platform line updated to match |
||
|
|
5432575ae3 |
fix(install): self-heal missing better-sqlite3 binding on Windows (#408) (#410)
Closes #408. 3-layer self-heal (prebuild-install via process.execPath → npm install fallback → actionable stderr). Wired into postinstall + ensure-deps + cli doctor hint. Co-authored-by: ousamabenyounes <ousama.benyounes@gmail.com> |
||
|
|
7e64e4fadb |
docs: update README with OpenCode session continuity improvements (#409)
* docs: update README with OpenCode session continuity improvements - Update OpenCode adapter to enable sessionStart and canInjectSessionContext hooks - Clarify SessionStart handling via experimental.chat.system.transform surrogate - Update AGENTS.md usage instructions for both OpenCode and KiloCode - Add new directories to .gitignore for development tools * test: update OpenCode adapter test expectations for session capabilities - Change sessionStart and canInjectSessionContext expectations from false to true - Update test descriptions to reflect new expected behavior - Ensure KiloCode adapter test maintains consistency with updated expectations --------- Co-authored-by: Mert Köseoğlu <bm.ksglu@gmail.com> |
||
|
|
d9a537fac3 |
docs(readme): inline concurrency annotation, drop verbose Parallel I/O section
Per user feedback: code blocks were too heavy for the Tools table area.
Compressed into one-liners next to the relevant tool rows:
- ctx_batch_execute: "Opt-in concurrency: 1-8 for I/O-bound batches."
- ctx_fetch_and_index: "Pass requests:[{url,source}, ...] + concurrency: 1-8 for parallel multi-URL."
The verbose Parallel I/O subsection with 30+ lines of example code is gone.
Schema details remain documented in src/server.ts JSDoc on each tool's
description string (which is the canonical source for LLM-facing usage hints).
|
||
|
|
5850e3bcb5 |
fix(release): pack bin/ + document concurrency + CTX_FETCH_STRICT for v1.0.104
DX validation post-tag found:
1. bin/ missing from package.json files array — npm tarball did not include
bin/statusline.mjs, breaking the v1.0.104 marquee statusline feature for
fresh installs (cli.bundle.mjs forwards to context-mode statusline which
resolves to a non-packaged path).
2. README missing concurrency feature documentation (added in
|
||
|
+7 |
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 |
||
|
|
36fefe206c |
feat(insight): session analytics — 90 metrics, 37 insights, 4 composite scores
Insight Dashboard: - New /api/category-analytics endpoint with 8 sections (categories, errorIntelligence, delegation, governance, gitProductivity, contextHealth, fileIntelligence, compositeScores) - 37 insight patterns (5 KILLER, 19 STRONG, 4 NICE) with severity sort + "Show all" toggle - 4 composite scores (0-100): Productivity, Quality, Delegation, Context Health - 5-min TTL response cache prevents double DB opens - insufficientData guard (50 events / 3 sessions minimum) Windows Hook Fixes (#369, #371, #372): - fix: use process.execPath instead of bare 'node' for hook commands (#369) - fix: forward-slash paths + quoting prevents MSYS path mangling on non-C: drives (#372) - fix: ensure-deps no longer skips better-sqlite3 install on Node v24 (#371) Only the SIGSEGV-prone probe is skipped; prebuild-install always runs - buildNodeCommand() utility in src/adapters/types.ts — cross-platform, MSYS-safe - All 8 hook-building adapters updated README: - Updated "What gets captured" section: 14 → 23 event categories - Updated Session Guide: 5 new sections - Updated ctx_insight description: 90 metrics, 37 patterns, 4 composite scores Closes #369, Closes #371, Closes #372 |
||
|
|
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> |
||
|
|
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> |