Skip to content

Commit 862e570

Browse files
committed
feat(adaptation): enhance platform detection and cache path handling for OpenCode/KiloCode
1 parent 0501887 commit 862e570

8 files changed

Lines changed: 235 additions & 200 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ context-mode-guidance-*/
2020
.claude-worktrees/
2121
.vibetree/
2222
.cw/
23+
# CocoIndex Code (ccc)
24+
/.cocoindex_code/
25+
/.kilo/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ Session continuity requires 4 hooks working together:
919919
| **SessionStart** | Restores state after compaction or resume | Yes | Yes | Yes | Yes | -- | -- | -- | Plugin | Yes | -- | -- | -- | ✓ (via session_start event) |
920920
| | **Session completeness** | **Full** | **High** | **High** | **High** | **Partial** | **High** | **High** | **High** | **Partial** | **--** | **Partial** | **--** | **High** |
921921

922-
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, **VS Code Copilot**, and **JetBrains Copilot**. **OpenCode** provides **high** session continuity: it captures tool events and injects compaction snapshots via the plugin, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume restore is not supported. **KiloCode** shares the same plugin architecture as OpenCode via the OpenCodeAdapter, so its continuity level depends on KiloCode's SessionStart support. **Cursor** captures tool events via `preToolUse`/`postToolUse`, but `sessionStart` is currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so session restore after compaction is not available yet. **OpenClaw** uses native gateway plugin hooks (`api.on()`) for full session continuity. **Pi Coding Agent** provides high session continuity via extension hooks (`tool_call`, `tool_result`, `session_start`, `session_before_compact`). **Codex CLI** provides partial hook-based session tracking through PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, and Stop; MCP tools work. **Antigravity**, **Kiro**, and **Zed** have no hook support in the current release, so session tracking is not available.
922+
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, **VS Code Copilot**, and **JetBrains Copilot**. **OpenCode** provides **high** session continuity: it captures tool events and injects compaction snapshots via the plugin, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume restore is not supported. There is a plugin available that enforces model to use context-mode on session start. More details on their [home page](https://github.com/AbdelrhmanUZaki/opencode-enforce-context-mode). **KiloCode** shares the same plugin architecture as OpenCode via the OpenCodeAdapter, so its continuity level depends on KiloCode's SessionStart support. **Cursor** captures tool events via `preToolUse`/`postToolUse`, but `sessionStart` is currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so session restore after compaction is not available yet. **OpenClaw** uses native gateway plugin hooks (`api.on()`) for full session continuity. **Pi Coding Agent** provides high session continuity via extension hooks (`tool_call`, `tool_result`, `session_start`, `session_before_compact`). **Codex CLI** provides partial hook-based session tracking through PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, and Stop; MCP tools work. **Antigravity**, **Kiro**, and **Zed** have no hook support in the current release, so session tracking is not available.
923923

924924
<details>
925925
<summary><strong>What gets captured</strong></summary>

cli.bundle.mjs

Lines changed: 115 additions & 112 deletions
Large diffs are not rendered by default.

server.bundle.mjs

Lines changed: 72 additions & 72 deletions
Large diffs are not rendered by default.

src/adapters/detect.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const PLATFORM_ENV_VARS = [
3434
["claude-code", ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]],
3535
["gemini-cli", ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]],
3636
["openclaw", ["OPENCLAW_HOME", "OPENCLAW_CLI"]],
37-
["kilo", ["KILO", "KILO_PID"]],
37+
["kilo", ["KILO", "KILO_PID", "KILOCODE_VERSION"]],
3838
["opencode", ["OPENCODE", "OPENCODE_PID"]],
3939
["codex", ["CODEX_CI", "CODEX_THREAD_ID"]],
4040
["cursor", ["CURSOR_TRACE_ID", "CURSOR_CLI"]],
@@ -53,6 +53,7 @@ export function detectPlatform(clientInfo?: { name: string; version?: string }):
5353
// ── Highest priority: MCP clientInfo ──────────────────
5454
if (clientInfo?.name) {
5555
const platform = CLIENT_NAME_TO_PLATFORM[clientInfo.name];
56+
5657
if (platform) {
5758
return {
5859
platform,

src/adapters/opencode/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,13 @@ export class OpenCodeAdapter extends BaseAdapter implements HookAdapter {
426426
}
427427

428428
getInstalledVersion(): string {
429-
// Check ~/.cache/opencode/node_modules/ for context-mode
430429
try {
431430
const pkgPath = resolve(
432431
homedir(),
433432
".cache",
434433
this.platform,
434+
"packages",
435+
"context-mode@latest",
435436
"node_modules",
436437
"context-mode",
437438
"package.json",

src/cli.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const args = process.argv.slice(2);
116116
if (args[0] === "doctor") {
117117
doctor().then((code) => process.exit(code));
118118
} else if (args[0] === "upgrade") {
119-
upgrade();
119+
upgrade(args[1]);
120120
} else if (args[0] === "hook") {
121121
hookDispatch(args[1], args[2]);
122122
} else if (args[0] === "insight") {
@@ -173,10 +173,10 @@ function defaultPluginRoot(): string {
173173
function cachePluginRoot(platform: string): string {
174174
if (process.platform === "win32") {
175175
const localApp = process.env.LOCALAPPDATA;
176-
if (localApp) return resolve(localApp, platform, "node_modules", "context-mode");
177-
return resolve(homedir(), "AppData", "Local", platform, "node_modules", "context-mode");
176+
if (localApp) return resolve(localApp, platform, "packages", "context-mode@latest", "node_modules", "context-mode");
177+
return resolve(homedir(), "AppData", "Local", platform, "packages", "context-mode@latest", "node_modules", "context-mode");
178178
}
179-
return resolve(homedir(), ".cache", platform, "node_modules", "context-mode");
179+
return resolve(homedir(), ".cache", platform, "packages", "context-mode@latest", "node_modules", "context-mode");
180180
}
181181

182182
function getPluginRoot(): string {
@@ -570,9 +570,19 @@ async function insight(port: number) {
570570
* Upgrade — adapter-aware hook configuration
571571
* ------------------------------------------------------- */
572572

573-
async function upgrade() {
573+
async function upgrade(source = 'remote') {
574574
if (process.stdout.isTTY) console.clear();
575575

576+
// local mode is available only from context-mode git clone otherwise ignore local mode
577+
try {
578+
const gitCheck = execFileSync("git", ["remote", "get-url", "origin"], { stdio: "pipe" }).toString().trim();
579+
if (source !== "remote" && gitCheck !== "https://github.com/mksglu/context-mode.git") {
580+
source = "remote"
581+
}
582+
} catch {
583+
source = "remote"
584+
}
585+
576586
// Detect platform
577587
const detection = detectPlatform();
578588
const adapter = await getAdapter(detection.platform);
@@ -588,25 +598,34 @@ async function upgrade() {
588598
const s = p.spinner();
589599

590600
// Step 1: Pull latest from GitHub
591-
p.log.step("Pulling latest from GitHub...");
601+
if (source==="remote") {
602+
p.log.step("Pulling latest from GitHub...");
603+
} else {
604+
p.log.step("Copying local branch...");
605+
}
592606
const localVersion = getLocalVersion();
593607
const tmpDir = join(tmpdir(), `context-mode-upgrade-${Date.now()}`);
594608

595609
s.start("Cloning mksglu/context-mode");
596610
try {
597-
execFileSync(
598-
"git", ["clone", "--depth", "1", "https://github.com/mksglu/context-mode.git", tmpDir],
599-
{ stdio: "pipe", timeout: 30000 },
600-
);
601-
s.stop("Downloaded");
611+
if (source === 'remote'){
612+
execFileSync(
613+
"git", ["clone", "--depth", "1", "https://github.com/mksglu/context-mode.git", tmpDir],
614+
{ stdio: "pipe", timeout: 30000 },
615+
);
616+
s.stop("Downloaded");
617+
} else {
618+
cpSync(".", tmpDir, { recursive: true });
619+
s.stop("Copied");
620+
}
602621

603622
const srcDir = tmpDir;
604623
const newPkg = JSON.parse(
605624
readFileSync(resolve(srcDir, "package.json"), "utf-8"),
606625
);
607626
const newVersion = newPkg.version ?? "unknown";
608627

609-
if (newVersion === localVersion) {
628+
if (newVersion === localVersion && source === "remote") {
610629
p.log.success(color.green("Already on latest") + ` — v${localVersion}`);
611630
rmSync(tmpDir, { recursive: true, force: true });
612631
return;

src/opencode-plugin.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type { HookInput } from "./session/extract.js";
2929
import { buildResumeSnapshot } from "./session/snapshot.js";
3030
import type { SessionEvent } from "./types.js";
3131
import { AdapterPlatformType, OpenCodeAdapter } from "./adapters/opencode/index.js";
32+
import { PLATFORM_ENV_VARS } from "./adapters/detect.js";
3233

3334
// ── Types ─────────────────────────────────────────────────
3435

@@ -78,7 +79,14 @@ interface CompactingHookOutput {
7879

7980
// ── Helpers ───────────────────────────────────────────────
8081
function getPlatform(): AdapterPlatformType {
81-
return process.env.KILO_PID ? "kilo" : "opencode";
82+
const [platform, vars] = PLATFORM_ENV_VARS.filter(p => p[0]==="kilo")[0];
83+
for (const _var of vars){
84+
if (process.env[_var]) {
85+
return platform;
86+
}
87+
}
88+
89+
return "opencode";
8290
}
8391

8492
// ── Plugin Factory ────────────────────────────────────────

0 commit comments

Comments
 (0)