mirror of
https://github.com/mksglu/context-mode.git
synced 2026-09-19 03:27:16 +08:00
next
26 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
926481e8c0 | fix(opencode): convert Zod v3 schemas to v4 for in-process plugin host (#734) | ||
|
|
45100c75be |
fix(opencode): convert zod v3 schemas to v4 for kilo platform compatibility (#632)
* fix(opencode): convert zod v3 schemas to v4 for kilo platform compatibility KiloCode's runtime bundles Zod v4 internally, but plugin tool definitions may contain Zod v3 schemas. This causes crashes with "undefined is not an object" when accessing ._zod.def. Added a utility that converts Zod v3 schema shapes to v4 equivalents specifically when platform === "kilo". OpenCode uses Zod 3 natively and receives the original shapes unchanged. The conversion handles all common Zod types including objects, arrays, unions, optionals, nullables, defaults, and effects. Includes comprehensive test coverage for all supported types. * fix(opencode): remove package.json version reading at module load - dead code |
||
|
|
7c82220023 |
fix(opencode): coerce stringified primitives on native plugin path (#627)
v1.0.139 (#621) added inputSchema.parse(args) on the OpenCode native plugin bridge so z.preprocess / z.default would fire. That fixed ctx_batch_execute.commands and ctx_search.queries (both wrapped in preprocessors) but exposed three primitive-type rejections that the MCP stdio transport used to absorb: • ctx_search.limit was z.number() — rejects "4" with "Expected number, received string" • ctx_search.queries with a *bare* string (not JSON-stringified array) survived coerceJsonArray unchanged and hit z.array(z.string()) • ctx_execute.background / ctx_purge.confirm were z.boolean() — reject "false" with "Expected boolean, received string" Several LLM providers' tool-call JSON stringifies primitives, and OpenCode's plugin host passes them through verbatim. With #621's parse step in place the bridge surfaces these as "Invalid arguments for <tool>" instead of coercing transparently. Fix: • Widen coerceJsonArray to lift a bare non-empty string into [val] (empty strings still bubble to Zod's .min(1) so we don't mask real input errors). • Add coerceBoolean preprocessor — maps only literal "true"/"false" to booleans; passes everything else through. z.coerce.boolean() is unsafe here because Boolean("false") is true. • Apply coerceBoolean to ctx_execute.background and ctx_purge.confirm. • Switch ctx_search.limit to z.coerce.number() — Number coerce is safe (Number("4") === 4) and mirrors what ctx_batch_execute's timeout/concurrency and ctx_fetch_and_index's concurrency already do. TDD slices in tests/opencode-plugin.test.ts (#621 describe extended, no new test files per CONTRIBUTING L275): • ctx_search accepts stringified limit (exact reporter case) • ctx_search lifts bare-string queries into single-element array • ctx_execute accepts stringified background boolean • ctx_purge accepts stringified confirm boolean Full suite: 3362 / 3362 pass (145 files, 28 unrelated skips). refs/platforms/opencode/packages/opencode/src/tool/registry.ts:145 confirms OpenCode does not coerce or decode plugin tool args — the plugin owns its parsing contract. |
||
|
|
a7c74303df |
fix(opencode): run zod schema parse on native plugin tool args (#621)
OpenCode's plugin tool registry (refs/platforms/opencode/packages/ opencode/src/tool/registry.ts:127) uses the Zod schema only as a boolean type guard via .safeParse(u).success — it passes RAW args to def.execute(). Our ctx_batch_execute / ctx_search handlers rely on z.preprocess(coerceCommandsArray | coerceJsonArray, …) to coerce JSON-string args back into arrays and to fill defaults. PR #574 / #597 wired ctx_* tools natively via the plugin tool map and called registered.handler(args ?? {}) directly, bypassing the MCP SDK's safeParseAsync wrapper. Result: when the LLM delivered commands as a JSON-stringified array or omitted them entirely, the handler crashed with "commands.map is not a function" instead of either coercing the value or producing an actionable validation error. Fix: run inputSchema.parse(args) inside buildNativeTools before invoking the handler — same contract as the MCP framework (server/mcp.js safeParseAsync line 174). Validation failures now surface as "Invalid arguments for <tool>: <zod error>" rather than opaque TypeErrors downstream. TDD slices (tests/opencode-plugin.test.ts): - baseline well-formed args still work - JSON-stringified commands array is coerced - bare-string commands are lifted to {label,command} - missing commands raises a clear "Invalid arguments" error - JSON-stringified queries on ctx_search are coerced |
||
|
|
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> |
||
|
|
d923716d00 |
refactor(adapter/C2): drop getSessionDBPath/EventsPath from Adapter interface
The two methods were SHALLOW pure derivatives of `getSessionDir() +
projectDir` (interface complexity == implementation complexity). The Codex
override added no behavior — its `normalizeWorktreePath` pre-pass was a
no-op because `resolveSessionDbPath` and `hashProjectDirCanonical` already
normalize internally. The events.md override included a worktree suffix
that nothing in production consumed (server.ts and hooks build their own
events.md path inline).
Apply the deletion test: removing them from BaseAdapter / Adapter / Codex
collapses to ONE site of computation (`resolveSessionDbPath` in
src/session/db.ts). Complexity does NOT reappear in N callers — only one
production caller existed (opencode/plugin.ts), updated to call the helper
directly. Adapter authors can no longer cargo-cult overrides; the methods
do not exist on the interface.
Migration shape (preserved by tests):
before: new SessionDB({ dbPath: adapter.getSessionDBPath(projectDir) })
after: new SessionDB({ dbPath: resolveSessionDbPath({
projectDir, sessionsDir: adapter.getSessionDir(),
}) })
Test counts: 2627→2630 passed (+2 new C2 narrowing pins, +1 less flake);
19 baseline failures unchanged (security policy + statusline cross-OS,
unrelated to this change).
Cross-OS validation: `tests/hooks/precompact-snapshot-event.test.ts:81-84`
documents the slash-folding pattern that resolveSessionDbPath performs
internally; refs/platforms/codex/codex-rs/core/src/config/mod.rs:622-627
confirms upstream Codex separates platform dir state (`codex_home`) from
per-project file paths — the separation we are formalizing.
Bundles regenerated via `npm run build`. hooks/*.bundle.mjs unchanged
(they don't import adapter classes).
|
||
|
|
5a6fe4bd3b | feat(adapters): add PiAdapter to fix getAdapter('pi') fallthrough (#473 follow-up) | ||
|
|
dedb433da8 | fix(opencode): restore AGENTS.md capture for snapshot/auto-memory parity (#487 follow-up) | ||
|
|
087bfa9c38 | fix(opencode): wrap debug logger awaits in safeLog to preserve turn (#448 follow-up) | ||
|
|
9b5c3b9d47 | fix(opencode): replace overlapping quorum markers with distinct tokens (#487 follow-up) | ||
|
|
961a749c25 |
docs: update grep documentation and remove routing markers (#499)
* docs: update grep documentation and remove routing markers Update grep guidance across configuration files to warn about context flooding. Remove XML comment markers from OpenCode plugin routing logic to streamline system message injection. * test: remove context-mode marker comment from OpenCode plugin test Remove XML comment markers from snapshot assertions in OpenCode plugin test to streamline snapshot validation. Update test description to focus on snapshot emission rather than marker comments. |
||
|
|
5aade76749 |
fix(opencode): remove duplicate routing block injection and AGENTS.md capture (#487)
- Remove redundant routing block injection when system prompt already contains context-mode instructions - Remove AGENTS.md/CLAUDE.md rule capture functionality as it's handled by the host - Add quorum-based routing detection to prevent duplicate instruction injection - Update plugin documentation to reflect simplified hook behavior |
||
|
|
c805410878 |
fix(pi): bridge MCP tools into pi.registerTool() so the LLM can call them (#426) (#472)
* fix(pi): bridge MCP tools into pi.registerTool() so the LLM can call them (#426) Pi 0.73.x has no native MCP support — its README is explicit: > No MCP. Build CLI tools with READMEs (see Skills), or build an > extension that adds MCP support. Without a bridge inside the context-mode Pi extension, the routing block tells the LLM to call `ctx_execute` / `ctx_search` / etc. but those tools never enter Pi's tool list and the LLM cannot reach them. The reporter measured 18 sessions over 2 days: ~2,500 tokens of system-prompt overhead per window, 0 actual ctx_* calls, 447 events recorded but never retrieved. Net ROI on Pi was negative. This adds a stdio JSON-RPC client (`MCPStdioClient`) plus a thin bootstrap (`bootstrapMCPTools`) that: - spawns `server.bundle.mjs` as a long-lived MCP child, - performs the standard MCP handshake (initialize → notifications/initialized), - lists tools once via `tools/list`, and - registers each tool through `pi.registerTool({ name, label, description, parameters, execute })` so the LLM sees the canonical bare names (matching what hooks/core/tool-naming.mjs emits for Pi). Each Pi `execute()` callback forwards into the MCP child via `tools/call`. Errors are translated to `throw` (Pi's contract for "tool failed") so the LLM sees the MCP server's diagnostic text. Lifecycle: - Bridge bootstrap is fire-and-forget at extension load — the rest of the extension (session capture, hooks, slash commands) is not blocked by spawn / handshake latency. - `session_shutdown` terminates the child via SIGTERM. - A missing `server.bundle.mjs` or any spawn / handshake error is surfaced once on stderr, then the extension keeps running with only the existing hooks + commands. Defense-in-depth so the bridge can never break Pi sessions for users with broken installs. ## Why a JSON Schema parameters object instead of TypeBox MCP `tools/list` returns JSON Schema. Pi's parameter validator accepts JSON Schema directly (TypeBox just produces JSON Schema with extra Symbol metadata for type inference). Passing the schema through unchanged avoids a runtime translation pass and keeps the bridge a true thin layer over the MCP protocol — what works in Claude Code, Gemini CLI, and the other adapters now also works in Pi. ## No new runtime dependencies Pure `node:child_process` + `node:path`. The `@earendil-works/pi-*` packages are NOT pulled in as build deps — `pi` is typed structurally as `any` (matching the existing src/pi-extension.ts style) and the bridge only touches the documented `pi.registerTool()` shape. ## Tests Added two new `describe` blocks in `tests/pi-extension.test.ts`: 1. `MCPStdioClient` (5 tests) — wire-protocol contract pinned with fake stdio servers: id-matched responses, concurrent in-flight requests with out-of-order delivery, child-exit cancellation, timeout, non-JSON noise tolerance. 2. `bootstrapMCPTools` (2 integration tests) — spawn the real `start.mjs` MCP server, assert that the canonical ctx_* set (`ctx_execute`, `ctx_execute_file`, `ctx_search`, `ctx_index`, `ctx_batch_execute`, `ctx_fetch_and_index`, `ctx_doctor`, `ctx_stats`, `ctx_purge`) is registered, and round-trip `ctx_index` through `tools/call` to confirm execute() forwards args and returns text. ## Test plan - [x] `npm run build` - [x] `npm run typecheck` clean - [x] `npm test` — 73 files, 2406 pass / 25 skipped / 0 fail - [x] `npx vitest run tests/pi-extension.test.ts` — 44 pass (37 pre-existing + 7 new for the bridge) - [x] see-real-bug repro: `pi.registerTool` count = 0 in installed binary (`/home/$USER/.nvm/.../context-mode/build/pi-extension.js`) on `next` @ |
||
|
|
51fd51e45b |
fix(opencode-plugin):add structured logging, remove per-session dedup sets, tighten plugin types (#448)
* fix(opencode-plugin):add structured logging, remove per-session dedup sets, tighten plugin types - Replace loose PluginContext with typed client/log interfaces matching the OpenCode plugin contract (client.app.log) - Add OPENCODE_DEBUG-gated logger() at key injection points: routing block, compaction snapshot, auto-injection block, and resume snapshot - Remove routingInjected/resumeInjected per-session Sets — routing block now injects on every chat.system.transform turn for reliability; resume snapshot dedup is handled by DB claim consumption - Add `id` field to default export for plugin identification - Defensive fallback to process.cwd() when ctx.directory is undefined - Add verbatim-display instruction to ctx_commands in routing block - Update tests to match new behavior: routing block re-injects every turn, resume snapshot gated by DB consumption only * docs(hooks): remove verbose command output instruction |
||
|
|
3ef7b6ebb4 |
feat(plugin): add context injection support for additionalContext (#422)
Previously, context actions were no-ops in the OpenCode plugin. Now, when a context action includes additionalContext, it gets injected into the output args for OpenCode to consume. Adds test case to verify guidance injection for allowed commands. |
||
|
|
ff0a9a2665 |
fix(14-adapter parity): v1.0.107 — Mickey ROUTING_BLOCK + 30+ verified gaps
Comprehensive 14-adapter parity audit + line-by-line clone verification
(no agent fabrication: code-driven `grep` + websearch + `gh code search`
where source closed). 9 fabrications caught and retracted; 22 verified
P0 gaps closed across 13 adapters.
═══ Mickey-class routing block (ana şikayet) ═══
Mickey @mikij retest of v1.0.106 — "routing block is not injected and
everything rely on AGENTS.md which btw is sadly unreliable."
VERIFIED via grep: `ROUTING_BLOCK | createRoutingBlock` returned 0 hits
in src/opencode-plugin.ts. Real gap.
Fixed in 3 adapters (same pattern, evidence-based):
• OpenCode (src/opencode-plugin.ts:430-446) — `experimental.chat
.system.transform` now splices `createRoutingBlock(createToolNamer
("opencode"))` at index 1 with visible marker
`<!-- context-mode v1.0.107: routing block injected (sessionID=...) -->`.
Separate `routingInjected: Set<string>` (NOT same as resumeInjected) so
routing fires even without prior snapshot. splice(1,0) preserves
OpenCode's `system[0]` cache-fold header per
agentisd/refs/opencode/packages/opencode/src/session/llm.ts:117-128.
• OpenClaw (src/openclaw-plugin.ts:638-647) — `before_prompt_build`
handler replaced static `readFileSync(configs/openclaw/AGENTS.md)`
with dynamic `createRoutingBlock(createToolNamer("openclaw"))`. Same
visible marker pattern.
• Pi (src/pi-extension.ts:321-330) — `before_agent_start` handler
prepends routing block via dynamic-import `getRoutingBlock(pluginRoot)`.
Same visible marker.
Verification path: `OPENCODE_DEBUG=1 opencode` → `grep
"<!-- context-mode v1.0.107: routing block" ~/.cache/opencode/log/*.log`.
═══ Per-adapter P0 fixes (verified by Phase 7 line-by-line review) ═══
OpenCode (4 P0 — Mickey-class):
OC-1 ROUTING_BLOCK injection (above)
OC-2 chat.message hook wired (was missing — SDK has it at
agentisd/refs/opencode/packages/plugin/src/index.ts:233);
captures user prompts + extractUserEvents + CCv2 inline
<system-reminder> filter
OC-3 buildAutoInjection (P1/P2/P3/P4 priority bands, 500-token cap)
in experimental.session.compacting (was raw buildResumeSnapshot)
OC-4 captureAgentsMd() in tool.execute.after — captures AGENTS.md /
CLAUDE.md / CONTEXT.md as rule_content events
(FILES order verified against agentisd/refs/opencode/packages
/opencode/src/session/instruction.ts)
OpenClaw (6 critical):
OClaw-1 NEW src/openclaw/mcp-tools.ts — registers 11 ctx_* MCP tools
via `api.registerTool` (mirrors swarmvault standard MCP at
refs/plugin-examples/openclaw/swarmvault/packages/engine
/src/mcp.ts:46-51). Routing block was telling agents to
use tools that DIDN'T EXIST in OpenClaw sessions
OClaw-2 dynamic createRoutingBlock (above)
OClaw-3 isSystemReminderMessage 4-prefix filter in
before_model_resolve (Mickey-class false-positive guard)
OClaw-4 session_end handler finalizes resume snapshot
(per refs/platforms/openclaw/docs/plugins/hooks.md:110)
OClaw-5 subagent_spawning handler injects routing block via
inputOverride.prompt
OClaw-6 closure flag at lines 239,483,564,568 — F6 retraction
confirmed: per-register() singleton scope, NOT a bug
Pi (1 P0 + 3 P1):
Pi-1 routing block injection (above)
Pi-2 before_provider_response handler (was unregistered — captures
{model, provider, latencyMs, tokens})
Pi-3 active_memory always-built (removed compact_count===0 early-return)
Pi-4 buildAutoInjection 500-token cap
Qwen Code (4 silent bugs — wire identical at PAYLOAD level only):
Qwen-1 NEW hooks/core/platform-detect.mjs — env-var detection mirrors
PLATFORM_ENV_VARS in src/adapters/detect.ts (single source of
truth). Replaces hardcoded createToolNamer("claude-code") in
hooks/sessionstart.mjs:22 → Qwen agents now see correct tool
names instead of CC-prefix namespace
Qwen-2 configureAllHooks 2 → 5 hooks (was dropping PostToolUse,
PreCompact, UserPromptSubmit)
Qwen-3 hooks/core/routing.mjs TOOL_ALIASES extended with Qwen native
(write_file, edit, glob, todo_write, ask_user_question,
list_directory, save_memory, skill, exit_plan_mode) +
OpenClaw native (exec/read/grep/search) + Codex native
(shell, local_shell, etc) — curl/wget/build redirects now
work on all platforms
Qwen-4 src/session/extract.ts TOOL_NAME_NORMALIZE shim normalizes
platform-native tool names → CC equivalents at extractEvents
entry → no more silently-dropped events on Qwen
Kiro (3 P0):
Kiro-1 NEW hooks/kiro/userpromptsubmit.mjs — was declared in HOOK_TYPES
but no script existed
Kiro-2 NEW hooks/kiro/agentspawn.mjs — Kiro's SessionStart-equivalent.
Capability flipped to true, parser added, routing block injected
via additionalContext
VSCode Copilot (1 P0 + path bug):
A1 hooks.ts:98 path bug — was resolving to Claude-Code generic
`hooks/pretooluse.mjs` instead of `hooks/vscode-copilot/...`.
Plus created missing hooks/vscode-copilot/sessionstart.mjs (mirror
of jetbrains-copilot pattern, captures
.github/copilot-instructions.md as rule_content events)
Z13 HOOK_TYPES STOP/SUBAGENT_START/SUBAGENT_STOP orphan removed
(declared but never had scripts). copilot-base made these
optional; jetbrains still declares them but it's also out of
scope (no scripts shipped either)
Cursor (3 P0):
C-1 sessionStart capability flag flipped true (script + dispatcher
already existed, capability said false)
C-2 NEW hooks/cursor/afteragentresponse.mjs (74 lines, mirrors
stop.mjs); was registered in HOOK_TYPES with no script (dangling)
C-3 Task-tool deferral comment (closed-source unknown, no probe data)
Gemini CLI (1 P1):
G-1 NEW hooks/gemini-cli/beforeagent.mjs — Gemini's UserPromptSubmit
analog per refs/platforms/gemini-cli/packages/core/src/hooks
/types.ts:580-602 + hookRunner.ts:215-228 (additionalContext
appended to prompt)
Antigravity (1 P0):
A-1 configs/antigravity/GEMINI.md prepended `trigger: always_on`
frontmatter — confirmed via refs/plugin-examples/antigravity
/antigravity-kit/.agent/rules/GEMINI.md:1
Zed (1 LOW):
Z-1 configs/zed/AGENTS.md added "NEVER use ctx_execute/shell to
create files" clause (was missing per CC routing-block.mjs:46-50)
Kilo (1 LOW):
K-1 src/adapters/opencode/index.ts paths() includes .kilocode/kilo.json
and .kilocode/kilo.jsonc (Kilo runtime accepts these per
refs/platforms/kilo/packages/.../config.ts:50,408)
═══ 9 FABRICATIONS retracted (kanıtla) ═══
F1 V1: "Codex hooks not registered, 10-line fix needed" → already
at src/adapters/codex/index.ts:244-276, all 5 registered
F2 V1+V2: "GEMINI_PROJECT_DIR invented by us" → REVERSED. hookRunner.ts:355
SETS it (plus GEMINI_SESSION_ID, GEMINI_CWD, GEMINI_PLANS_DIR,
CLAUDE_PROJECT_DIR alias). Detection KEPT.
F3 V2: "Add KIRO_API_REGION to detection" → CONFIRMED FABRICATION.
Only in kiro-gateway test fixtures, NOT IDE source. KEPT detect.ts:76.
F4 V1: "VSCode `system` priority injection exists" → wrong, only User/
Assistant in chat API. systemMessage field is UI warning-only.
F5 V1: "mcp-jetbrains exposes IDE injection" → pure stdio↔HTTP proxy
F6 V1: "OpenClaw closure-flag bad per community" → unverifiable
(sample size 0). Retraction-of-retraction: actually fine
per-register() singleton, kept as-is.
F7 V1: "swarmvault is OpenClaw-specific MCP" → standard
@modelcontextprotocol/sdk only
F8 V1: "pi/awesome-agent-skills shows routing patterns" → empty repo
(4 scaffold files only)
F9 V1: "Zed has zero chat-influence APIs" → REVERSED. slash_command +
context_server_command traits CONFIRMED at extension.wit:151-160,
wasm_host.rs:306-340/351-386. (CC10 Zed extension viable —
deferred to v1.0.108+, requires wasm32 build)
+ V2 "Cursor MDC alwaysApply" — Cursor-Rules-Awesome only manages
legacy .cursorrules (no frontmatter). Dropped.
+ Initial v1.0.107 "10-file generic SDD scaffold deploy" (cc-sdd
mirror — product/structure/tech + steering-custom/api-standards/
auth/database/deployment/error-handling/security/testing) →
Mert review caught: those are end-user project templates, NOT
adapter wiring. Reverted; we ship single configs/kiro/KIRO.md
and let users opt in by copying manually.
═══ Tests ═══
All in EXISTING files (CONTRIBUTING L275). Cumulative test counts
post-Phase-8:
• 32/32 OpenCode (8 new, 8 updated)
• 95/95 OpenClaw (12 new)
• 36/36 Pi (7 new)
• 17 new Qwen + 165 regression
• 49/49 Kiro (after steering-scaffold revert: 1 assertion adjusted)
• 32/32 Cursor (2 new)
• 67/67 VSCode + Gemini (4 new)
• 333+ adapter-specific tests pass total
• Full suite: 2268 pass / 9 fail (pre-existing baseline: opencode-
adapter tsx subprocess infra, .cw worktree node_modules/tsx
missing — verified pre-existing via stash)
• TypeScript: clean
═══ Validation discipline ═══
3-tier verification per gap:
1. Code-driven `grep` against fresh `--depth=1` clones in refs/platforms/
2. Re-grep against community plugin examples in refs/plugin-examples/
3. WebSearch + `gh code search` for closed-source platforms (Cursor,
JetBrains AI Assistant, Antigravity, Kiro IDE)
Rejected approach: agent narrative claims (V1+V2 audits had 9
fabrications detected via this discipline). Phase 7 used 1 main +
13 parallel adapter agents reading line-by-line; Phase 8 used 9
parallel staff-engineer agents writing TDD red→green per slice;
2 architect grills caught remaining issues including Q1 platform-
detect.mjs PLATFORM_ENV_VARS misalignment (now mirrors src/adapters
/detect.ts:33-77 as single source of truth).
═══ NOT shipped (out-of-scope reverts during review) ═══
• 10-file Kiro generic SDD steering scaffold (Mert review: ~~cc-sdd
project templates, not adapter wiring~~)
• PRD-*.md files (16 dev artifacts, untracked)
• refs/ clones (~3GB, untracked)
• Companion VSIX extension for VSCode (CC12) — defer
• Zed wasm32 extension (CC10) — defer
• JetBrains companion plugin via postStartupActivity — defer
• Qwen 7 enhancement events (Stop/SubagentStart/Stop/StopFailure/
SessionEnd/PostToolUseFailure/PostCompact) — defer
|
||
|
|
131079db22 |
fix(opencode-plugin+db+server): v1.0.106 — Mickey resume injection follow-up + version-check TTL
Mickey @mikij retest of v1.0.105 (PR #376 follow-up): > "source has changed but snapshot is undefined still and I does > not do nothing yet as I can see. resumeInjected.add(sessionId); > is executed no matter session injected something or not. What I > need to do to get this snapshot active? At least I can't find > use case for it" Three findings, all addressed here: 1. `resumeInjected.add(sessionId)` ordering bug (Mickey's correct observation) v1.0.105 marked the session as "tried" BEFORE checking whether the DB had a snapshot to claim. A new session that polled too early — before any prior session had compacted — was permanently locked out of retroactive injection within the same plugin process. Fix: src/opencode-plugin.ts moves `resumeInjected.add(sessionId)` to AFTER a successful splice. No row → no mark → next chat turn retries. 2. Self-injection bug (would have shipped if we'd applied #1 naively) With #1 alone, a session that compacts mid-flight (Session B turn 5 produces B's own snapshot row) would claim its OWN snapshot back into its OWN system prompt on turn 6 — wasted tokens AND consumed the snapshot meant for the next fresh session. Fix: src/session/db.ts `claimLatestUnconsumedResume(currentSessionId)` now takes a `currentSessionId` parameter. SQL adds `AND session_id != ?` to the inner SELECT, so the current session can never claim its own row. Passing an empty string disables the guard (legacy/test paths). 3. Visible signal — Mickey's "I can't find use case for it" Cross-session resume was working but invisible. No log line, no UI surface, no way for the user to confirm it fired. We now prepend a plain XML comment to the snapshot so OPENCODE_DEBUG=1 (and any provider that echoes the system prompt) shows: <!-- context-mode v1.0.106: resumed prior session abc12345 (N events, M chars) --> <session_resume>...</session_resume> The marker is harmless to the model and proves the feature is live. Bonus: `_latestVersion` TTL refresh src/server.ts now schedules a 1-hour `setInterval` to re-fetch `npm view context-mode/latest`. The first call still happens at startup; the interval keeps long-running sessions (24h+ MCP servers) aware of new releases instead of caching the value from process boot. `.unref()` ensures graceful shutdown isn't blocked by the pending interval. Discovered when v1.0.105 release notification didn't reach a 14h-old session. Tests (all in EXISTING files per CONTRIBUTING.md L275): • tests/session/session-db.test.ts — 2 new self-exclusion slices: - excludes the current session's own row (no self-injection) - returns another session's row when current also has one • tests/opencode-plugin.test.ts — 4 new behavior tests: - does NOT inject snapshot back into producing session (self-injection guard) - retries on next turn when no row exists (no premature gate) - snapshot from B is consumed by C, not by B itself - emits a visible context-mode marker comment Existing 79+24 tests all updated to the new claim signature (empty-string sentinel where no current session is meaningful). Validation: • TypeScript: clean • 440/440 tests pass across session-db + opencode-plugin + server + executor + routing • 3 parallel sub-agents (diagnose / grill-with-docs / architect) converged on RESHAPE before this change, SHIP after the self-exclusion + visible marker landed • Verified against agentisd/refs/opencode source: chat.system.transform fires per-turn from `LLM.process` (session/llm.ts:103-122), system enters as 1-entry joined string, our splice(1, 0, marker+snapshot) preserves cache fold Docs: docs/platform-support.md OpenCode entry now lists the 4th hook (experimental.chat.system.transform), explains the cross-session resume workflow, and tells users how to verify via OPENCODE_DEBUG=1. Mickey's repro after this lands: 1. cd ~/proj && opencode → do enough work to trigger auto-compact (or run /compact) 2. exit, restart `opencode` in same dir 3. send first message → grep log for `<!-- context-mode v1.0.106:` |
||
|
|
eea5877bd3 |
fix(opencode-plugin+executor+routing): Mickey 3-issue + Issue #406 timeout policy
Mickey @mikij (PR #376 follow-up — github.com/mksglu/context-mode/pull/376): 1. db.getResume() returned null on every chat turn Root cause: src/opencode-plugin.ts pivoted on the wrong hook — `experimental.chat.messages.transform` ships with input `{}` (no sessionID) and output `{info,parts}[]`. The prior `output.messages .unshift({role,content})` was the wrong shape and silently dropped by OpenCode. Verified against agentisd/refs/opencode/packages/plugin/src/index.ts:281-295 and packages/opencode/src/session/llm.ts:117-128. Fix: pivot to `experimental.chat.system.transform` (input `{sessionID?, model}`, output `system: string[]`). Insert at index 1 (`splice(1, 0, snapshot)`) — NOT `unshift` — to preserve OpenCode's `system[0]` cache-fold header invariant; otherwise the provider prompt cache invalidates on every resume injection. 2. `sessionStartInjected = true` survived only first session per process Root cause: closure-scope boolean + a process-global `randomUUID()` binding. Multi-session reuse (which OpenCode/Kilo do — the plugin factory runs once and serves many sessions) tripped the flag and never reset it; the fabricated UUID also never matched any prior resume row. Fix: drop `randomUUID()` entirely; plumb `input.sessionID` from each hook (system.transform, tool.execute.after, session.compacting); gate injection with `Set<string>` keyed by sessionID. 3. Z.ai GLM 4.7 → "client is not defined" on ctx_doctor Root cause: Z.ai's MCP renderer mounts a custom React component for GFM task-list syntax (`- [x]`, `- [ ]`, `- [-]`) that depends on a missing `client` context. Fix: refactor ctx_doctor output to plain-text `[OK]/[FAIL]/[WARN]` prefixes and drop the `## ` h2 — renderer-safe across all MCP clients. Skill instructions updated to match. Cross-session resume claim is now race-safe (architect ask): src/session/db.ts gains `claimLatestUnconsumedResume()` — atomic `UPDATE session_resume SET consumed=1 WHERE id=(SELECT id FROM session_resume WHERE consumed=0 ORDER BY created_at DESC, id DESC LIMIT 1) RETURNING session_id, snapshot`. Two concurrent processes for the same project cannot inject the same snapshot (verified by test). Issue #406 @wax911 (github.com/mksglu/context-mode/issues/406): Long-running build commands (Gradle/Maven/SBT) were terminated by the Zod `.default(30000)` ms timeout in ctx_execute / ctx_execute_file / `.default(60000)` in ctx_batch_execute whenever the caller forgot to pass an explicit value. Server-side timeout policy is the wrong layer — every MCP host already enforces its own RPC timeout — and stacking env-var defaults on top would create dead config. Fix: - Drop the Zod `.default(...)` from all three tools' timeout schemas; keep `.optional()`. When the caller omits `timeout`, no server-side timer fires (the host's RPC timeout governs). - PolyglotExecutor: when `timeout === undefined`, do not set the setTimeout timer at all. Long-running processes complete naturally. - runBatchCommands: `BatchRunOptions.timeout` is `number | undefined`; serial path lifts the shared budget, parallel path passes `undefined` to the executor (no per-cmd kill). - hooks/core/routing.mjs: word-boundary-anchored regex now also redirects `sbt` / `./sbt` to ctx_execute (was only gradle/mvn). Guard against false positives like `gradle-wrapper-config`. Tests (all in EXISTING files per CONTRIBUTING.md L275): • tests/session/session-db.test.ts — 4 atomic-claim slices (returns null on empty / on consumed-only, returns latest unconsumed and marks consumed atomically, two parallel claims return distinct rows) • tests/opencode-plugin.test.ts — 5 system.transform behavior tests (factory exposes new hook + omits old, no-sessionID no-op, no-resume no-op, prepends snapshot first call, preserves system[0] header for cache fold, does NOT re-inject same sessionID) • tests/core/server.test.ts — Z.ai renderer-safe regression + 2 #406 serial-batch undefined-timeout tests • tests/executor.test.ts — 2 #406 no-timeout tests (JS + shell) • tests/hooks/core-routing.test.ts — 3 sbt/word-boundary tests • Removed: tests/opencode-session-start.test.ts (orphan + asserted the now-removed messages.transform hook — coverage moved into tests/opencode-plugin.test.ts) Bundles rebuilt: server.bundle.mjs, cli.bundle.mjs, hooks/session-db.bundle.mjs, hooks/session-extract.bundle.mjs Reviewed by 8 parallel sub-agent passes (3 diagnose + 3 grill + 2 architect+validation). Verdicts converged on SHIP after the cache- fold splice fix. 14 adapter × 3 OS coverage: • 10 hook-paradigm adapters unaffected (chat.system.transform is OpenCode SDK plugin paradigm only) • SQLite 3.53 bundled with better-sqlite3 — `UPDATE … RETURNING` works on Windows • openclaw + pi unchanged — they receive a real session_start event so the Mickey #2 closure-flag pattern doesn't apply |
||
|
|
35e264caa4 |
fix(hooks): replace PPID sentinel with directory-scan for MCP readiness (#347)
Claude Code spawns hooks via `bash -c "node ..."` on WSL2/Linux. The intermediate shell makes process.ppid point to a transient bash PID, not Claude Code. The PPID-keyed sentinel is never found, causing all MCP redirects to be bypassed — context window floods. Fix: hooks now scan /tmp for `context-mode-mcp-ready-*` files and probe each PID with kill(pid,0). Server writes sentinel with process.pid (not ppid). Hardcoded /tmp on Unix avoids TMPDIR mismatch. Changes: - hooks/core/mcp-ready.mjs: glob scan + PID liveness + stale cleanup - src/server.ts: sentinel uses process.pid, /tmp on Unix - 11 test files: updated sentinel path pattern Closes #347 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0f80c851e8 |
test: add MCP sentinel setup to all routing test files (#230)
mcpRedirect() returns null when sentinel absent, breaking 32 tests across 8 files that expect deny/modify behavior. Added sentinel write/cleanup in beforeEach/afterEach for all affected test suites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
3893fd5272 |
fix: update opencode-plugin test assertions for reference-based snapshots
Tests at lines 218 and 276 expected full file paths (/src/index.ts, /app/main.ts) in snapshot output, but v1.0.68 reference-based format uses filename-only display (index.ts, main.ts). Updated assertions. Fixes CI failure on ubuntu/windows/macos. |
||
|
|
715d6f45f9 |
fix(opencode): remove stale AGENTS.md auto-write fallback
* ci: update server.bundle.mjs, cli.bundle.mjs & session hook bundles * ci: update install stats * fix(opencode): stop auto-writing AGENTS.md * fix(opencode): remove stale routing fallback * docs: clarify OpenCode continuity limits * chore: retrigger flaky macOS CI * test(ci): use forks pool on macOS --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
b77736a561 |
test: isolate HOME during vitest runs (#180)
* test: isolate HOME during vitest runs * test: assert fake HOME isolation * test: avoid teardown races in fake HOME setup * test: scope fake HOME isolation to homedir suites * docs(test): explain fake HOME scoping * test: isolate opencode plugin HOME writes * chore: retrigger macOS CI |
||
|
|
815de2b56d |
fix: marketplace sync, OpenCode hooks, routing guard, OpenClaw env vars (#156 #158 #159 #160)
## Issue #158 — start.mjs unconditionally writes CLAUDE.md - Added hook-capable platform guard: skip routing file write when CLAUDE_SESSION_ID, GEMINI_PROJECT_DIR, OPENCODE, OPENCLAW_HOME, or OPENCLAW_CLI env vars are present (sessionStart hook handles it) ## Issue #159 — OpenCode hook interface mismatch - Fixed opencode-plugin.ts: two-param (input, output) hook signature matching upstream @opencode-ai/plugin API - Field mapping: tool_name→input.tool, tool_input→output.args, tool_output→output.output, is_error removed - Arg mutation now correctly targets output.args (read by OpenCode) - Updated adapter index.ts and all tests ## Issue #160 — Marketplace sync: cli.bundle.mjs missing - ctx_doctor: runs diagnostics in-process (no CLI dependency) - ctx_upgrade: temp .mjs file fallback when CLI missing (cross-platform) - start.mjs: CLI self-heal shim (cli.bundle.mjs from build/cli.js) - session-loaders.mjs: bundle-first with build/session fallback - Skills: prefer MCP tool, Bash as fallback ## Additional fixes - Replaced fabricated OPENCLAW_PROJECT_DIR with real OPENCLAW_CLI env var (verified against openclaw/openclaw source: src/infra/openclaw-exec-env.ts) - postinstall.mjs: improved nvm4w detection, isSafeWindowsPath guard - Removed dead basename import from session-loaders.mjs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a01106eda2 |
Feat: "sessionStart: true" platforms autocreate instructions on windows (#87)
* feat(vscode-copilot): auto-create or merge copilot-instructions.md on session start
- Create .github/copilot-instructions.md if it doesn't exist
- Append routing rules if file exists but lacks context-mode instructions
- Skip silently if routing rules already present (idempotent)
- Create .github/ directory if needed (mkdirSync recursive)
- Outer try/catch ensures missing configs never block session start
- Update README to document all three cases for Step 4
* feat(adapters): auto-write routing instructions on Gemini/OpenCode startup
* docs(pr): explain run_in_terminal alias and PR purpose
* Revert "docs(pr): explain run_in_terminal alias and PR purpose"
This reverts commit
|
||
|
|
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> |