context-mode 1.0.111 → 1.0.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/index.ts +3 -2
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +152 -34
- package/bin/statusline.mjs +144 -127
- package/build/adapters/base.d.ts +8 -5
- package/build/adapters/base.js +8 -18
- package/build/adapters/claude-code/index.d.ts +24 -3
- package/build/adapters/claude-code/index.js +44 -11
- package/build/adapters/codex/hooks.d.ts +10 -5
- package/build/adapters/codex/hooks.js +10 -5
- package/build/adapters/codex/index.d.ts +17 -5
- package/build/adapters/codex/index.js +337 -37
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/cursor/index.d.ts +6 -0
- package/build/adapters/cursor/index.js +83 -2
- package/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +29 -6
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +75 -0
- package/build/adapters/omp/plugin.js +220 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +198 -0
- package/build/adapters/openclaw/plugin.d.ts +130 -0
- package/build/adapters/openclaw/plugin.js +629 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/plugin.d.ts +145 -0
- package/build/adapters/opencode/plugin.js +457 -0
- package/build/adapters/pi/extension.d.ts +26 -0
- package/build/adapters/pi/extension.js +552 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +113 -0
- package/build/adapters/pi/mcp-bridge.js +251 -0
- package/build/adapters/types.d.ts +11 -6
- package/build/cli.js +186 -170
- package/build/db-base.d.ts +15 -2
- package/build/db-base.js +50 -5
- package/build/executor.d.ts +2 -0
- package/build/executor.js +15 -2
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.js +64 -5
- package/build/search/auto-memory.js +6 -4
- package/build/security.js +30 -10
- package/build/server.d.ts +23 -1
- package/build/server.js +662 -182
- package/build/session/analytics.d.ts +404 -1
- package/build/session/analytics.js +1347 -42
- package/build/session/db.d.ts +114 -5
- package/build/session/db.js +275 -27
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +1 -0
- package/build/session/extract.js +79 -12
- package/build/session/purge.d.ts +111 -0
- package/build/session/purge.js +138 -0
- package/build/store.d.ts +7 -0
- package/build/store.js +69 -6
- package/build/util/claude-config.d.ts +26 -0
- package/build/util/claude-config.js +91 -0
- package/build/util/hook-config.d.ts +4 -0
- package/build/util/hook-config.js +39 -0
- package/build/util/project-dir.d.ts +49 -0
- package/build/util/project-dir.js +67 -0
- package/cli.bundle.mjs +411 -208
- package/configs/antigravity/GEMINI.md +0 -3
- package/configs/claude-code/CLAUDE.md +1 -4
- package/configs/codex/AGENTS.md +1 -4
- package/configs/codex/config.toml +3 -0
- package/configs/codex/hooks.json +8 -0
- package/configs/cursor/context-mode.mdc +0 -3
- package/configs/gemini-cli/GEMINI.md +0 -3
- package/configs/jetbrains-copilot/copilot-instructions.md +0 -3
- package/configs/kilo/AGENTS.md +0 -3
- package/configs/kiro/KIRO.md +0 -3
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +0 -3
- package/configs/opencode/AGENTS.md +0 -3
- package/configs/pi/AGENTS.md +0 -3
- package/configs/qwen-code/QWEN.md +1 -4
- package/configs/vscode-copilot/copilot-instructions.md +0 -3
- package/configs/zed/AGENTS.md +0 -3
- package/hooks/codex/posttooluse.mjs +9 -2
- package/hooks/codex/precompact.mjs +69 -0
- package/hooks/codex/sessionstart.mjs +13 -9
- package/hooks/codex/stop.mjs +1 -2
- package/hooks/codex/userpromptsubmit.mjs +1 -2
- package/hooks/core/routing.mjs +237 -18
- package/hooks/cursor/afteragentresponse.mjs +1 -1
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +1 -1
- package/hooks/cursor/sessionstart.mjs +5 -5
- package/hooks/cursor/stop.mjs +1 -1
- package/hooks/ensure-deps.mjs +12 -13
- package/hooks/gemini-cli/aftertool.mjs +1 -1
- package/hooks/gemini-cli/beforeagent.mjs +1 -1
- package/hooks/gemini-cli/precompress.mjs +3 -2
- package/hooks/gemini-cli/sessionstart.mjs +9 -9
- package/hooks/jetbrains-copilot/posttooluse.mjs +1 -1
- package/hooks/jetbrains-copilot/precompact.mjs +3 -2
- package/hooks/jetbrains-copilot/sessionstart.mjs +9 -9
- package/hooks/kiro/agentspawn.mjs +5 -5
- package/hooks/kiro/posttooluse.mjs +2 -2
- package/hooks/kiro/userpromptsubmit.mjs +1 -1
- package/hooks/posttooluse.mjs +45 -0
- package/hooks/precompact.mjs +17 -0
- package/hooks/pretooluse.mjs +23 -0
- package/hooks/routing-block.mjs +0 -12
- package/hooks/run-hook.mjs +16 -3
- package/hooks/session-db.bundle.mjs +27 -18
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +101 -64
- package/hooks/sessionstart.mjs +51 -2
- package/hooks/vscode-copilot/posttooluse.mjs +1 -1
- package/hooks/vscode-copilot/precompact.mjs +3 -2
- package/hooks/vscode-copilot/sessionstart.mjs +9 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +14 -8
- package/server.bundle.mjs +349 -147
- package/start.mjs +16 -4
- package/skills/UPSTREAM-CREDITS.md +0 -51
- package/skills/context-mode-ops/SKILL.md +0 -299
- package/skills/context-mode-ops/agent-teams.md +0 -198
- package/skills/context-mode-ops/communication.md +0 -224
- package/skills/context-mode-ops/marketing.md +0 -124
- package/skills/context-mode-ops/release.md +0 -214
- package/skills/context-mode-ops/review-pr.md +0 -269
- package/skills/context-mode-ops/tdd.md +0 -329
- package/skills/context-mode-ops/triage-issue.md +0 -266
- package/skills/context-mode-ops/validation.md +0 -307
- package/skills/diagnose/SKILL.md +0 -122
- package/skills/diagnose/scripts/hitl-loop.template.sh +0 -41
- package/skills/grill-me/SKILL.md +0 -15
- package/skills/grill-with-docs/ADR-FORMAT.md +0 -47
- package/skills/grill-with-docs/CONTEXT-FORMAT.md +0 -77
- package/skills/grill-with-docs/SKILL.md +0 -93
- package/skills/improve-codebase-architecture/DEEPENING.md +0 -37
- package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +0 -44
- package/skills/improve-codebase-architecture/LANGUAGE.md +0 -53
- package/skills/improve-codebase-architecture/SKILL.md +0 -76
- package/skills/tdd/SKILL.md +0 -114
- package/skills/tdd/deep-modules.md +0 -33
- package/skills/tdd/interface-design.md +0 -31
- package/skills/tdd/mocking.md +0 -59
- package/skills/tdd/refactoring.md +0 -10
- package/skills/tdd/tests.md +0 -61
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Native Cursor hooks use lower-camel hook names and flat command entries in
|
|
5
5
|
* `.cursor/hooks.json` / `~/.cursor/hooks.json`.
|
|
6
6
|
*/
|
|
7
|
-
import { readFileSync, writeFileSync, mkdirSync, accessSync, chmodSync, constants, existsSync, } from "node:fs";
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync, accessSync, chmodSync, constants, existsSync, readdirSync, } from "node:fs";
|
|
8
8
|
import { execSync } from "node:child_process";
|
|
9
9
|
import { resolve, join } from "node:path";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { BaseAdapter } from "../base.js";
|
|
12
|
+
import { resolveClaudeConfigDir } from "../../util/claude-config.js";
|
|
12
13
|
import { HOOK_TYPES as CURSOR_HOOK_NAMES, HOOK_SCRIPTS as CURSOR_HOOK_SCRIPTS, PRE_TOOL_USE_MATCHER_PATTERN, REQUIRED_HOOKS, OPTIONAL_HOOKS, isContextModeHook, buildHookCommand, } from "./hooks.js";
|
|
13
14
|
const CURSOR_ENTERPRISE_HOOKS_PATH = "/Library/Application Support/Cursor/hooks.json";
|
|
14
15
|
export class CursorAdapter extends BaseAdapter {
|
|
@@ -263,8 +264,72 @@ export class CursorAdapter extends BaseAdapter {
|
|
|
263
264
|
message: "Claude-compatible hooks detected; native Cursor hooks are the supported configuration",
|
|
264
265
|
});
|
|
265
266
|
}
|
|
267
|
+
const pluginInstalls = this.detectPluginInstalls();
|
|
268
|
+
if (pluginInstalls.length > 0) {
|
|
269
|
+
const nativeHasContextMode = loaded
|
|
270
|
+
? Object.entries(loaded.config.hooks ?? {}).some(([type, entries]) => Array.isArray(entries) && entries.some((entry) => isContextModeHook(entry, type)))
|
|
271
|
+
: false;
|
|
272
|
+
if (nativeHasContextMode && loaded) {
|
|
273
|
+
results.push({
|
|
274
|
+
check: "Plugin/native hook duplication",
|
|
275
|
+
status: "warn",
|
|
276
|
+
message: `context-mode plugin detected at ${pluginInstalls[0]} alongside native hooks in ${loaded.path} — ` +
|
|
277
|
+
`each event will fire twice. Remove one configuration to avoid duplicate routing.`,
|
|
278
|
+
fix: "Remove the native .cursor/hooks.json entries OR uninstall the plugin",
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
results.push({
|
|
283
|
+
check: "Plugin install",
|
|
284
|
+
status: "pass",
|
|
285
|
+
message: `context-mode plugin installed at ${pluginInstalls[0]}`,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
266
289
|
return results;
|
|
267
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Detects context-mode plugin installations under Cursor's plugin directories.
|
|
293
|
+
* Returns absolute paths to any `.cursor-plugin/plugin.json` files whose
|
|
294
|
+
* `name` matches `context-mode`.
|
|
295
|
+
*/
|
|
296
|
+
detectPluginInstalls() {
|
|
297
|
+
const roots = [
|
|
298
|
+
join(homedir(), ".cursor", "plugins", "local"),
|
|
299
|
+
join(homedir(), ".cursor", "plugins", "cache"),
|
|
300
|
+
];
|
|
301
|
+
const found = [];
|
|
302
|
+
for (const root of roots) {
|
|
303
|
+
try {
|
|
304
|
+
accessSync(root, constants.F_OK);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
// Plugins live one directory deep: <root>/<name>/.cursor-plugin/plugin.json
|
|
310
|
+
let entries = [];
|
|
311
|
+
try {
|
|
312
|
+
entries = readdirSync(root);
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
for (const name of entries) {
|
|
318
|
+
const manifestPath = join(root, name, ".cursor-plugin", "plugin.json");
|
|
319
|
+
try {
|
|
320
|
+
const raw = readFileSync(manifestPath, "utf-8");
|
|
321
|
+
const parsed = JSON.parse(raw);
|
|
322
|
+
if (parsed?.name === "context-mode") {
|
|
323
|
+
found.push(manifestPath);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return found;
|
|
332
|
+
}
|
|
268
333
|
checkPluginRegistration() {
|
|
269
334
|
const mcpPaths = [resolve(".cursor", "mcp.json"), join(homedir(), ".cursor", "mcp.json")];
|
|
270
335
|
for (const configPath of mcpPaths) {
|
|
@@ -294,6 +359,19 @@ export class CursorAdapter extends BaseAdapter {
|
|
|
294
359
|
continue;
|
|
295
360
|
}
|
|
296
361
|
}
|
|
362
|
+
// #489 round-3 — pure plugin install (Marketplace) bundles MCP registration
|
|
363
|
+
// inside the plugin package. No native mcp.json exists, but the plugin
|
|
364
|
+
// manifest under ~/.cursor/plugins/{local,cache}/<name>/.cursor-plugin/plugin.json
|
|
365
|
+
// is enough to consider context-mode registered. Without this, doctor
|
|
366
|
+
// self-contradicts: `Plugin install: pass` alongside `MCP registration: warn`.
|
|
367
|
+
const pluginInstalls = this.detectPluginInstalls();
|
|
368
|
+
if (pluginInstalls.length > 0) {
|
|
369
|
+
return {
|
|
370
|
+
check: "MCP registration",
|
|
371
|
+
status: "pass",
|
|
372
|
+
message: `context-mode registered via plugin manifest at ${pluginInstalls[0]}`,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
297
375
|
return {
|
|
298
376
|
check: "MCP registration",
|
|
299
377
|
status: "warn",
|
|
@@ -412,10 +490,13 @@ export class CursorAdapter extends BaseAdapter {
|
|
|
412
490
|
return null;
|
|
413
491
|
}
|
|
414
492
|
hasClaudeCompatibilityHooks() {
|
|
493
|
+
// Issue #460 round-3: probe the resolved CC config dir (honors
|
|
494
|
+
// $CLAUDE_CONFIG_DIR) instead of the literal ~/.claude so users
|
|
495
|
+
// who relocated their CC config still trigger the compat path.
|
|
415
496
|
const compatPaths = [
|
|
416
497
|
resolve(".claude", "settings.json"),
|
|
417
498
|
resolve(".claude", "settings.local.json"),
|
|
418
|
-
join(
|
|
499
|
+
join(resolveClaudeConfigDir(), "settings.json"),
|
|
419
500
|
];
|
|
420
501
|
return compatPaths.some((configPath) => existsSync(configPath));
|
|
421
502
|
}
|
|
@@ -23,7 +23,7 @@ import type { PlatformId, DetectionSignal, HookAdapter } from "./types.js";
|
|
|
23
23
|
* Single source of truth — consumed by detectPlatform() below and by
|
|
24
24
|
* tests that need to clear platform-related env vars deterministically.
|
|
25
25
|
*/
|
|
26
|
-
export declare const PLATFORM_ENV_VARS: readonly [readonly ["claude-code", readonly ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]], readonly ["antigravity", readonly ["ANTIGRAVITY_CLI_ALIAS"]], readonly ["cursor", readonly ["CURSOR_TRACE_ID", "CURSOR_CLI"]], readonly ["kilo", readonly ["KILO_PID"]], readonly ["opencode", readonly ["OPENCODE", "OPENCODE_PID"]], readonly ["zed", readonly ["ZED_SESSION_ID", "ZED_TERM"]], readonly ["codex", readonly ["CODEX_THREAD_ID", "CODEX_CI"]], readonly ["gemini-cli", readonly ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]], readonly ["vscode-copilot", readonly ["VSCODE_PID", "VSCODE_CWD"]], readonly ["jetbrains-copilot", readonly ["IDEA_INITIAL_DIRECTORY"]], readonly ["qwen-code", readonly ["QWEN_PROJECT_DIR"]], readonly ["pi", readonly ["PI_PROJECT_DIR"]]];
|
|
26
|
+
export declare const PLATFORM_ENV_VARS: readonly [readonly ["claude-code", readonly ["CLAUDE_PROJECT_DIR", "CLAUDE_SESSION_ID"]], readonly ["antigravity", readonly ["ANTIGRAVITY_CLI_ALIAS"]], readonly ["cursor", readonly ["CURSOR_TRACE_ID", "CURSOR_CLI"]], readonly ["kilo", readonly ["KILO", "KILO_PID"]], readonly ["opencode", readonly ["OPENCODE", "OPENCODE_PID"]], readonly ["zed", readonly ["ZED_SESSION_ID", "ZED_TERM"]], readonly ["codex", readonly ["CODEX_THREAD_ID", "CODEX_CI"]], readonly ["gemini-cli", readonly ["GEMINI_PROJECT_DIR", "GEMINI_CLI"]], readonly ["vscode-copilot", readonly ["VSCODE_PID", "VSCODE_CWD"]], readonly ["jetbrains-copilot", readonly ["IDEA_INITIAL_DIRECTORY"]], readonly ["qwen-code", readonly ["QWEN_PROJECT_DIR"]], readonly ["omp", readonly ["PI_CODING_AGENT_DIR"]], readonly ["pi", readonly ["PI_PROJECT_DIR"]]];
|
|
27
27
|
/**
|
|
28
28
|
* Sync map from platform identifier → home-relative path segments where that
|
|
29
29
|
* platform stores its config. Mirrors the `super([...])` argument passed by
|
package/build/adapters/detect.js
CHANGED
|
@@ -39,10 +39,9 @@ export const PLATFORM_ENV_VARS = [
|
|
|
39
39
|
// 800+ hits in major OSS detection libs (Vercel Next.js, Bun, Google
|
|
40
40
|
// gemini-cli, Nx, CrewAI).
|
|
41
41
|
["cursor", ["CURSOR_TRACE_ID", "CURSOR_CLI"]],
|
|
42
|
-
// kilo (OpenCode fork) — Kilo-Org/kilocode packages/opencode/src/index.ts:
|
|
43
|
-
// sets `process.env.KILO_PID = String(process.pid)`.
|
|
44
|
-
|
|
45
|
-
["kilo", ["KILO_PID"]],
|
|
42
|
+
// kilo (OpenCode fork) — Kilo-Org/kilocode packages/opencode/src/index.ts:138 + 139
|
|
43
|
+
// sets `process.env.KILO = 1` + `process.env.KILO_PID = String(process.pid)`.
|
|
44
|
+
["kilo", ["KILO", "KILO_PID"]],
|
|
46
45
|
// opencode — sst/opencode packages/opencode/src/index.ts:108-109 sets
|
|
47
46
|
// OPENCODE=1 + OPENCODE_PID=<pid> on every CLI invocation.
|
|
48
47
|
["opencode", ["OPENCODE", "OPENCODE_PID"]],
|
|
@@ -64,7 +63,11 @@ export const PLATFORM_ENV_VARS = [
|
|
|
64
63
|
// qwen-code — QWEN_PROJECT_DIR per QwenLM/qwen-code docs/users/features/hooks.md.
|
|
65
64
|
// (QWEN_SESSION_ID removed — 0 hits in qwen-code repository.)
|
|
66
65
|
["qwen-code", ["QWEN_PROJECT_DIR"]],
|
|
67
|
-
//
|
|
66
|
+
// omp (can1357/oh-my-pi). PI_CODING_AGENT_DIR is the upstream
|
|
67
|
+
// agent-dir override per `packages/utils/src/dirs.ts:193`. Listed
|
|
68
|
+
// BEFORE pi so OMP is not misclassified as Pi when both are installed.
|
|
69
|
+
["omp", ["PI_CODING_AGENT_DIR"]],
|
|
70
|
+
// pi — PI_PROJECT_DIR consumed by src/adapters/pi/extension.ts:154 + src/server.ts:153
|
|
68
71
|
// — implies the Pi runtime sets it before invoking the extension.
|
|
69
72
|
["pi", ["PI_PROJECT_DIR"]],
|
|
70
73
|
// openclaw — removed (runtime never sets OPENCLAW_HOME or OPENCLAW_CLI;
|
|
@@ -92,6 +95,7 @@ export function getSessionDirSegments(platform) {
|
|
|
92
95
|
case "vscode-copilot": return [".vscode"];
|
|
93
96
|
case "kiro": return [".kiro"];
|
|
94
97
|
case "pi": return [".pi"];
|
|
98
|
+
case "omp": return [".omp"];
|
|
95
99
|
case "qwen-code": return [".qwen"];
|
|
96
100
|
case "kilo": return [".config", "kilo"];
|
|
97
101
|
case "opencode": return [".config", "opencode"];
|
|
@@ -131,7 +135,7 @@ export function detectPlatform(clientInfo) {
|
|
|
131
135
|
if (platformOverride) {
|
|
132
136
|
const validPlatforms = [
|
|
133
137
|
"claude-code", "gemini-cli", "kilo", "opencode", "codex",
|
|
134
|
-
"vscode-copilot", "jetbrains-copilot", "cursor", "antigravity", "kiro", "pi", "zed", "qwen-code",
|
|
138
|
+
"vscode-copilot", "jetbrains-copilot", "cursor", "antigravity", "kiro", "pi", "omp", "zed", "qwen-code",
|
|
135
139
|
];
|
|
136
140
|
if (validPlatforms.includes(platformOverride)) {
|
|
137
141
|
return {
|
|
@@ -188,6 +192,14 @@ export function detectPlatform(clientInfo) {
|
|
|
188
192
|
reason: "~/.kiro/ directory exists",
|
|
189
193
|
};
|
|
190
194
|
}
|
|
195
|
+
// OMP listed BEFORE pi: shared ~/.pi history with OMP-only ~/.omp/ marker.
|
|
196
|
+
if (existsSync(resolve(home, ".omp"))) {
|
|
197
|
+
return {
|
|
198
|
+
platform: "omp",
|
|
199
|
+
confidence: "medium",
|
|
200
|
+
reason: "~/.omp/ directory exists",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
191
203
|
if (existsSync(resolve(home, ".pi"))) {
|
|
192
204
|
return {
|
|
193
205
|
platform: "pi",
|
|
@@ -300,6 +312,17 @@ export async function getAdapter(platform) {
|
|
|
300
312
|
const { QwenCodeAdapter } = await import("./qwen-code/index.js");
|
|
301
313
|
return new QwenCodeAdapter();
|
|
302
314
|
}
|
|
315
|
+
case "omp": {
|
|
316
|
+
const { OMPAdapter } = await import("./omp/index.js");
|
|
317
|
+
return new OMPAdapter();
|
|
318
|
+
}
|
|
319
|
+
case "pi": {
|
|
320
|
+
// Issue #473 follow-up: without this case, getAdapter("pi") fell
|
|
321
|
+
// through to ClaudeCodeAdapter and Pi sessions wrote into
|
|
322
|
+
// ~/.claude/context-mode/. PiAdapter pins storage to ~/.pi/.
|
|
323
|
+
const { PiAdapter } = await import("./pi/index.js");
|
|
324
|
+
return new PiAdapter();
|
|
325
|
+
}
|
|
303
326
|
default: {
|
|
304
327
|
// Unsupported platform — fall back to Claude Code adapter
|
|
305
328
|
// (MCP server works everywhere, hooks may not)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/omp — Oh My Pi (OMP) platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* OMP integration facts (verified against can1357/oh-my-pi @ v3.20.1):
|
|
5
|
+
* - MCP config: `~/.omp/agent/mcp.json` (global) or `<project>/.omp/mcp.json`
|
|
6
|
+
* (project), per `packages/utils/src/dirs.ts` `getMCPConfigPath()` and
|
|
7
|
+
* `docs/mcp-config.md` "Preferred config locations".
|
|
8
|
+
* - Agent-dir env override: `PI_CODING_AGENT_DIR` — `packages/utils/src/dirs.ts`:
|
|
9
|
+
* `let dirs = new DirResolver(process.env.PI_CODING_AGENT_DIR);`
|
|
10
|
+
* (No `OMP_*` runtime env exists; `.env`-file `OMP_*` keys are mirrored to
|
|
11
|
+
* `PI_*` BEFORE process.env is read.)
|
|
12
|
+
* - System-prompt file: `SYSTEM.md` (project `.omp/SYSTEM.md` precedence,
|
|
13
|
+
* global `~/.omp/agent/SYSTEM.md` fallback). NOT `PI.md` — no `PI.md`
|
|
14
|
+
* loader exists upstream. OMP also auto-discovers `AGENTS.md` via
|
|
15
|
+
* `packages/coding-agent/src/discovery/agents-md.ts`.
|
|
16
|
+
* - Hook surface: OMP DOES expose pre/post tool-call hooks
|
|
17
|
+
* (`~/.omp/agent/hooks/{pre,post}/*.ts`, `omp.on("tool_call", ...)`).
|
|
18
|
+
* This adapter ships MCP-only delivery for now; wiring native OMP
|
|
19
|
+
* hooks is future work tracked separately.
|
|
20
|
+
*
|
|
21
|
+
* Why a dedicated adapter rather than reusing pi:
|
|
22
|
+
* OMP and Pi share a runtime surface but different storage roots
|
|
23
|
+
* (`~/.omp/agent/` vs `~/.pi/`). Without an OMP adapter, OMP users
|
|
24
|
+
* running through a Claude-installed harness silently land their
|
|
25
|
+
* context-mode data under `~/.claude/context-mode/` (issue #473).
|
|
26
|
+
*/
|
|
27
|
+
import { BaseAdapter } from "../base.js";
|
|
28
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
29
|
+
export declare class OMPAdapter extends BaseAdapter implements HookAdapter {
|
|
30
|
+
constructor();
|
|
31
|
+
readonly name = "OMP";
|
|
32
|
+
readonly paradigm: HookParadigm;
|
|
33
|
+
readonly capabilities: PlatformCapabilities;
|
|
34
|
+
parsePreToolUseInput(_raw: unknown): PreToolUseEvent;
|
|
35
|
+
parsePostToolUseInput(_raw: unknown): PostToolUseEvent;
|
|
36
|
+
parsePreCompactInput(_raw: unknown): PreCompactEvent;
|
|
37
|
+
parseSessionStartInput(_raw: unknown): SessionStartEvent;
|
|
38
|
+
formatPreToolUseResponse(_response: PreToolUseResponse): unknown;
|
|
39
|
+
formatPostToolUseResponse(_response: PostToolUseResponse): unknown;
|
|
40
|
+
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
41
|
+
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve OMP agent root, honoring `PI_CODING_AGENT_DIR` when set
|
|
44
|
+
* (the upstream OMP convention — see `packages/utils/src/dirs.ts`)
|
|
45
|
+
* and falling back to `~/.omp/agent`.
|
|
46
|
+
*/
|
|
47
|
+
private getAgentDir;
|
|
48
|
+
getSettingsPath(): string;
|
|
49
|
+
/**
|
|
50
|
+
* OMP nests its config under the agent dir. Always absolute.
|
|
51
|
+
* `_projectDir` accepted for interface symmetry but unused — home-rooted.
|
|
52
|
+
*/
|
|
53
|
+
getConfigDir(_projectDir?: string): string;
|
|
54
|
+
getInstructionFiles(): string[];
|
|
55
|
+
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
56
|
+
readSettings(): Record<string, unknown> | null;
|
|
57
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
58
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
59
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
60
|
+
getInstalledVersion(): string;
|
|
61
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
62
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
63
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
64
|
+
getRoutingInstructions(): string;
|
|
65
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/omp — Oh My Pi (OMP) platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* OMP integration facts (verified against can1357/oh-my-pi @ v3.20.1):
|
|
5
|
+
* - MCP config: `~/.omp/agent/mcp.json` (global) or `<project>/.omp/mcp.json`
|
|
6
|
+
* (project), per `packages/utils/src/dirs.ts` `getMCPConfigPath()` and
|
|
7
|
+
* `docs/mcp-config.md` "Preferred config locations".
|
|
8
|
+
* - Agent-dir env override: `PI_CODING_AGENT_DIR` — `packages/utils/src/dirs.ts`:
|
|
9
|
+
* `let dirs = new DirResolver(process.env.PI_CODING_AGENT_DIR);`
|
|
10
|
+
* (No `OMP_*` runtime env exists; `.env`-file `OMP_*` keys are mirrored to
|
|
11
|
+
* `PI_*` BEFORE process.env is read.)
|
|
12
|
+
* - System-prompt file: `SYSTEM.md` (project `.omp/SYSTEM.md` precedence,
|
|
13
|
+
* global `~/.omp/agent/SYSTEM.md` fallback). NOT `PI.md` — no `PI.md`
|
|
14
|
+
* loader exists upstream. OMP also auto-discovers `AGENTS.md` via
|
|
15
|
+
* `packages/coding-agent/src/discovery/agents-md.ts`.
|
|
16
|
+
* - Hook surface: OMP DOES expose pre/post tool-call hooks
|
|
17
|
+
* (`~/.omp/agent/hooks/{pre,post}/*.ts`, `omp.on("tool_call", ...)`).
|
|
18
|
+
* This adapter ships MCP-only delivery for now; wiring native OMP
|
|
19
|
+
* hooks is future work tracked separately.
|
|
20
|
+
*
|
|
21
|
+
* Why a dedicated adapter rather than reusing pi:
|
|
22
|
+
* OMP and Pi share a runtime surface but different storage roots
|
|
23
|
+
* (`~/.omp/agent/` vs `~/.pi/`). Without an OMP adapter, OMP users
|
|
24
|
+
* running through a Claude-installed harness silently land their
|
|
25
|
+
* context-mode data under `~/.claude/context-mode/` (issue #473).
|
|
26
|
+
*/
|
|
27
|
+
import { readFileSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
28
|
+
import { resolve, dirname } from "node:path";
|
|
29
|
+
import { homedir } from "node:os";
|
|
30
|
+
import { BaseAdapter } from "../base.js";
|
|
31
|
+
// ─────────────────────────────────────────────────────────
|
|
32
|
+
// Adapter implementation
|
|
33
|
+
// ─────────────────────────────────────────────────────────
|
|
34
|
+
export class OMPAdapter extends BaseAdapter {
|
|
35
|
+
constructor() {
|
|
36
|
+
super([".omp"]);
|
|
37
|
+
}
|
|
38
|
+
name = "OMP";
|
|
39
|
+
paradigm = "mcp-only";
|
|
40
|
+
capabilities = {
|
|
41
|
+
preToolUse: false,
|
|
42
|
+
postToolUse: false,
|
|
43
|
+
preCompact: false,
|
|
44
|
+
sessionStart: false,
|
|
45
|
+
canModifyArgs: false,
|
|
46
|
+
canModifyOutput: false,
|
|
47
|
+
canInjectSessionContext: false,
|
|
48
|
+
};
|
|
49
|
+
// ── Input parsing ──────────────────────────────────────
|
|
50
|
+
// OMP does not support hooks. These methods exist to satisfy the
|
|
51
|
+
// interface contract but will throw if called.
|
|
52
|
+
parsePreToolUseInput(_raw) {
|
|
53
|
+
throw new Error("OMP hooks not wired by this adapter (MCP-only delivery)");
|
|
54
|
+
}
|
|
55
|
+
parsePostToolUseInput(_raw) {
|
|
56
|
+
throw new Error("OMP hooks not wired by this adapter (MCP-only delivery)");
|
|
57
|
+
}
|
|
58
|
+
parsePreCompactInput(_raw) {
|
|
59
|
+
throw new Error("OMP hooks not wired by this adapter (MCP-only delivery)");
|
|
60
|
+
}
|
|
61
|
+
parseSessionStartInput(_raw) {
|
|
62
|
+
throw new Error("OMP hooks not wired by this adapter (MCP-only delivery)");
|
|
63
|
+
}
|
|
64
|
+
// ── Response formatting ────────────────────────────────
|
|
65
|
+
// OMP does not support hooks. Return undefined for all responses.
|
|
66
|
+
formatPreToolUseResponse(_response) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
formatPostToolUseResponse(_response) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
formatPreCompactResponse(_response) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
formatSessionStartResponse(_response) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
// ── Configuration ──────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Resolve OMP agent root, honoring `PI_CODING_AGENT_DIR` when set
|
|
81
|
+
* (the upstream OMP convention — see `packages/utils/src/dirs.ts`)
|
|
82
|
+
* and falling back to `~/.omp/agent`.
|
|
83
|
+
*/
|
|
84
|
+
getAgentDir() {
|
|
85
|
+
return process.env.PI_CODING_AGENT_DIR
|
|
86
|
+
?? resolve(homedir(), ".omp", "agent");
|
|
87
|
+
}
|
|
88
|
+
getSettingsPath() {
|
|
89
|
+
return resolve(this.getAgentDir(), "mcp.json");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* OMP nests its config under the agent dir. Always absolute.
|
|
93
|
+
* `_projectDir` accepted for interface symmetry but unused — home-rooted.
|
|
94
|
+
*/
|
|
95
|
+
getConfigDir(_projectDir) {
|
|
96
|
+
return this.getAgentDir();
|
|
97
|
+
}
|
|
98
|
+
getInstructionFiles() {
|
|
99
|
+
// SYSTEM.md is the OMP-native system-prompt file (see
|
|
100
|
+
// can1357/oh-my-pi README "Custom System Prompt"). AGENTS.md is also
|
|
101
|
+
// auto-discovered by the universal discovery layer.
|
|
102
|
+
return ["SYSTEM.md", "AGENTS.md"];
|
|
103
|
+
}
|
|
104
|
+
generateHookConfig(_pluginRoot) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
readSettings() {
|
|
108
|
+
try {
|
|
109
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
110
|
+
return JSON.parse(raw);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
writeSettings(settings) {
|
|
117
|
+
const settingsPath = this.getSettingsPath();
|
|
118
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
119
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
120
|
+
}
|
|
121
|
+
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
122
|
+
validateHooks(_pluginRoot) {
|
|
123
|
+
return [
|
|
124
|
+
{
|
|
125
|
+
check: "Hook support",
|
|
126
|
+
status: "warn",
|
|
127
|
+
message: "context-mode delivers via MCP for OMP. " +
|
|
128
|
+
"Native OMP pre/post tool-call hooks are not yet wired by this adapter.",
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
checkPluginRegistration() {
|
|
133
|
+
try {
|
|
134
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
135
|
+
const config = JSON.parse(raw);
|
|
136
|
+
const mcpServers = config?.mcpServers ?? {};
|
|
137
|
+
if ("context-mode" in mcpServers) {
|
|
138
|
+
return {
|
|
139
|
+
check: "MCP registration",
|
|
140
|
+
status: "pass",
|
|
141
|
+
message: "context-mode found in mcpServers config",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
check: "MCP registration",
|
|
146
|
+
status: "fail",
|
|
147
|
+
message: "context-mode not found in mcpServers",
|
|
148
|
+
fix: `Add context-mode to mcpServers in ${this.getSettingsPath()}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return {
|
|
153
|
+
check: "MCP registration",
|
|
154
|
+
status: "warn",
|
|
155
|
+
message: `Could not read ${this.getSettingsPath()}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
getInstalledVersion() {
|
|
160
|
+
try {
|
|
161
|
+
const pkgPath = resolve(this.getAgentDir(), "extensions", "context-mode", "package.json");
|
|
162
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
163
|
+
return pkg.version ?? "unknown";
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return "not installed";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// ── Upgrade ────────────────────────────────────────────
|
|
170
|
+
configureAllHooks(_pluginRoot) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
setHookPermissions(_pluginRoot) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
177
|
+
// OMP MCP server registry is managed via mcp.json
|
|
178
|
+
}
|
|
179
|
+
getRoutingInstructions() {
|
|
180
|
+
return "# context-mode\n\nUse context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of run_command/view_file for data-heavy operations.";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oh My Pi (OMP) plugin entry point for context-mode.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Pi extension shape (`src/adapters/pi/extension.ts`) for
|
|
5
|
+
* the four OMP hook events that materially protect the context window
|
|
6
|
+
* and persist session continuity:
|
|
7
|
+
*
|
|
8
|
+
* - session_start — initialize the session row in our DB
|
|
9
|
+
* - tool_call — hard-block curl/wget/inline-HTTP in bash
|
|
10
|
+
* - tool_result — extract structured events into the session DB
|
|
11
|
+
* - session_before_compact — persist a resume snapshot before compaction
|
|
12
|
+
*
|
|
13
|
+
* Loaded by OMP via the `omp` (or `pi`) field in package.json — see
|
|
14
|
+
* upstream loader at refs/platforms/oh-my-pi/packages/coding-agent/src/
|
|
15
|
+
* extensibility/plugins/loader.ts:75:
|
|
16
|
+
* `const manifest: PluginManifest | undefined = pluginPkg.omp || pluginPkg.pi;`
|
|
17
|
+
* Hook factory contract from refs/.../extensibility/hooks/types.ts:809:
|
|
18
|
+
* `export type HookFactory = (pi: HookAPI) => void;`
|
|
19
|
+
*
|
|
20
|
+
* OMP differs from Pi in two ways that justify a dedicated plugin file:
|
|
21
|
+
* 1. Storage roots at ~/.omp/context-mode/ via OMPAdapter, not ~/.pi/
|
|
22
|
+
* 2. OMP has native MCP support (mcp.json), so no MCP bridge is needed
|
|
23
|
+
* — the bridge that Pi's extension ships (mcp-bridge.ts) is dead weight
|
|
24
|
+
* under OMP and is intentionally omitted here.
|
|
25
|
+
*/
|
|
26
|
+
export declare function _resetOmpPluginStateForTests(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Return the current session ID picked by the most recent session_start
|
|
29
|
+
* handler. Test-only — production code reads `_sessionId` directly via
|
|
30
|
+
* the closure. The shared SQLite DB at `~/.omp/context-mode/` survives
|
|
31
|
+
* between tests, so `getLatestSessionId()` cannot disambiguate which
|
|
32
|
+
* row belongs to "this" test when multiple tests insert in the same
|
|
33
|
+
* second; tests use this getter instead.
|
|
34
|
+
*/
|
|
35
|
+
export declare function _getOmpPluginSessionIdForTests(): string;
|
|
36
|
+
type ToolCallEvent = {
|
|
37
|
+
toolName: string;
|
|
38
|
+
toolCallId?: string;
|
|
39
|
+
input?: Record<string, unknown>;
|
|
40
|
+
};
|
|
41
|
+
type ToolResultEvent = {
|
|
42
|
+
toolName: string;
|
|
43
|
+
toolCallId?: string;
|
|
44
|
+
input?: Record<string, unknown>;
|
|
45
|
+
content?: Array<{
|
|
46
|
+
type: string;
|
|
47
|
+
text?: string;
|
|
48
|
+
}>;
|
|
49
|
+
isError?: boolean;
|
|
50
|
+
};
|
|
51
|
+
type ToolCallEventResult = {
|
|
52
|
+
block?: boolean;
|
|
53
|
+
reason?: string;
|
|
54
|
+
};
|
|
55
|
+
type HookEventCtx = Record<string, unknown> | undefined;
|
|
56
|
+
type HookHandler<E, R = void> = (event: E, ctx: HookEventCtx) => R | undefined | Promise<R | undefined>;
|
|
57
|
+
export interface MinimalHookAPI {
|
|
58
|
+
on(event: "session_start", handler: HookHandler<{
|
|
59
|
+
type: "session_start";
|
|
60
|
+
}>): void;
|
|
61
|
+
on(event: "session_before_compact", handler: HookHandler<{
|
|
62
|
+
type: "session_before_compact";
|
|
63
|
+
}>): void;
|
|
64
|
+
on(event: "tool_call", handler: HookHandler<ToolCallEvent, ToolCallEventResult>): void;
|
|
65
|
+
on(event: "tool_result", handler: HookHandler<ToolResultEvent>): void;
|
|
66
|
+
on(event: string, handler: (...args: unknown[]) => unknown): void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* OMP plugin default export. Called once by the OMP runtime per
|
|
70
|
+
* upstream `extensibility/plugins/loader.ts` after `omp plugin install
|
|
71
|
+
* context-mode`. Subsequent `pi.on(...)` registrations route the four
|
|
72
|
+
* lifecycle events to our SessionDB-backed handlers below.
|
|
73
|
+
*/
|
|
74
|
+
export default function ompPlugin(pi: MinimalHookAPI): void;
|
|
75
|
+
export {};
|