@mxalbert/context-mode 2.0.0
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 +33 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/hooks.json +65 -0
- package/.codex-plugin/mcp.json +13 -0
- package/.codex-plugin/plugin.json +31 -0
- package/.openclaw-plugin/index.ts +12 -0
- package/.openclaw-plugin/openclaw.plugin.json +38 -0
- package/.openclaw-plugin/package.json +28 -0
- package/LICENSE +94 -0
- package/README.md +1619 -0
- package/bin/statusline.mjs +378 -0
- package/build/adapters/antigravity/index.d.ts +51 -0
- package/build/adapters/antigravity/index.js +169 -0
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/base.d.ts +92 -0
- package/build/adapters/base.js +135 -0
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +106 -0
- package/build/adapters/claude-code/hooks.js +183 -0
- package/build/adapters/claude-code/index.d.ts +80 -0
- package/build/adapters/claude-code/index.js +569 -0
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +42 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +55 -0
- package/build/adapters/codex/hooks.js +64 -0
- package/build/adapters/codex/index.d.ts +87 -0
- package/build/adapters/codex/index.js +995 -0
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/codex/usage.d.ts +107 -0
- package/build/adapters/codex/usage.js +227 -0
- package/build/adapters/copilot-base.d.ts +94 -0
- package/build/adapters/copilot-base.js +297 -0
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/cursor/config.d.ts +4 -0
- package/build/adapters/cursor/config.js +4 -0
- package/build/adapters/cursor/hooks.d.ts +55 -0
- package/build/adapters/cursor/hooks.js +82 -0
- package/build/adapters/cursor/index.d.ts +64 -0
- package/build/adapters/cursor/index.js +517 -0
- package/build/adapters/detect.d.ts +136 -0
- package/build/adapters/detect.js +623 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +83 -0
- package/build/adapters/gemini-cli/hooks.js +115 -0
- package/build/adapters/gemini-cli/index.d.ts +80 -0
- package/build/adapters/gemini-cli/index.js +483 -0
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
- package/build/adapters/jetbrains-copilot/hooks.js +87 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
- package/build/adapters/jetbrains-copilot/index.js +131 -0
- package/build/adapters/kimi/config.d.ts +8 -0
- package/build/adapters/kimi/config.js +8 -0
- package/build/adapters/kimi/hooks.d.ts +28 -0
- package/build/adapters/kimi/hooks.js +34 -0
- package/build/adapters/kimi/index.d.ts +66 -0
- package/build/adapters/kimi/index.js +537 -0
- package/build/adapters/kimi/paths.d.ts +21 -0
- package/build/adapters/kimi/paths.js +52 -0
- package/build/adapters/kimi/usage.d.ts +82 -0
- package/build/adapters/kimi/usage.js +217 -0
- package/build/adapters/kiro/hooks.d.ts +61 -0
- package/build/adapters/kiro/hooks.js +94 -0
- package/build/adapters/kiro/index.d.ts +61 -0
- package/build/adapters/kiro/index.js +301 -0
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +81 -0
- package/build/adapters/omp/plugin.js +331 -0
- package/build/adapters/omp/usage.d.ts +49 -0
- package/build/adapters/omp/usage.js +110 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +74 -0
- package/build/adapters/openclaw/index.js +439 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +207 -0
- package/build/adapters/openclaw/plugin.d.ts +141 -0
- package/build/adapters/openclaw/plugin.js +818 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/openclaw/usage.d.ts +34 -0
- package/build/adapters/openclaw/usage.js +52 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +144 -0
- package/build/adapters/opencode/index.js +638 -0
- package/build/adapters/opencode/plugin.d.ts +249 -0
- package/build/adapters/opencode/plugin.js +1536 -0
- package/build/adapters/opencode/v2.d.ts +117 -0
- package/build/adapters/opencode/v2.js +224 -0
- package/build/adapters/opencode/zod3tov4.d.ts +1 -0
- package/build/adapters/opencode/zod3tov4.js +132 -0
- package/build/adapters/pi/extension.d.ts +81 -0
- package/build/adapters/pi/extension.js +861 -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 +268 -0
- package/build/adapters/pi/mcp-bridge.js +894 -0
- package/build/adapters/qwen-code/hooks.d.ts +26 -0
- package/build/adapters/qwen-code/hooks.js +29 -0
- package/build/adapters/qwen-code/index.d.ts +40 -0
- package/build/adapters/qwen-code/index.js +361 -0
- package/build/adapters/qwen-code/usage.d.ts +90 -0
- package/build/adapters/qwen-code/usage.js +222 -0
- package/build/adapters/types.d.ts +360 -0
- package/build/adapters/types.js +118 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
- package/build/adapters/vscode-copilot/hooks.js +98 -0
- package/build/adapters/vscode-copilot/index.d.ts +32 -0
- package/build/adapters/vscode-copilot/index.js +222 -0
- package/build/adapters/zed/index.d.ts +40 -0
- package/build/adapters/zed/index.js +163 -0
- package/build/cli.d.ts +34 -0
- package/build/cli.js +1828 -0
- package/build/db-base.d.ts +250 -0
- package/build/db-base.js +756 -0
- package/build/executor.d.ts +72 -0
- package/build/executor.js +678 -0
- package/build/exit-classify.d.ts +19 -0
- package/build/exit-classify.js +12 -0
- package/build/fetch/blocks.d.ts +145 -0
- package/build/fetch/blocks.js +244 -0
- package/build/fetch/extract.d.ts +68 -0
- package/build/fetch/extract.js +123 -0
- package/build/fetch/page-store.d.ts +66 -0
- package/build/fetch/page-store.js +174 -0
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +124 -0
- package/build/lifecycle.js +278 -0
- package/build/package-identity.d.ts +26 -0
- package/build/package-identity.js +28 -0
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.d.ts +71 -0
- package/build/runtime.js +659 -0
- package/build/search/auto-memory.d.ts +47 -0
- package/build/search/auto-memory.js +170 -0
- package/build/search/ctx-search-schema.d.ts +90 -0
- package/build/search/ctx-search-schema.js +135 -0
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/search/unified.d.ts +56 -0
- package/build/search/unified.js +104 -0
- package/build/security.d.ts +206 -0
- package/build/security.js +780 -0
- package/build/server.d.ts +240 -0
- package/build/server.js +5034 -0
- package/build/session/analytics.d.ts +773 -0
- package/build/session/analytics.js +2420 -0
- package/build/session/db.d.ts +468 -0
- package/build/session/db.js +1330 -0
- package/build/session/error-classifier.d.ts +87 -0
- package/build/session/error-classifier.js +303 -0
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +333 -0
- package/build/session/extract.js +2688 -0
- package/build/session/model-prices.json +429 -0
- package/build/session/persist-tool-calls.d.ts +54 -0
- package/build/session/persist-tool-calls.js +105 -0
- package/build/session/pricing.d.ts +64 -0
- package/build/session/pricing.js +151 -0
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +245 -0
- package/build/session/purge.d.ts +138 -0
- package/build/session/purge.js +240 -0
- package/build/session/retrieval-marker.d.ts +39 -0
- package/build/session/retrieval-marker.js +65 -0
- package/build/session/snapshot.d.ts +46 -0
- package/build/session/snapshot.js +532 -0
- package/build/store-directory.d.ts +56 -0
- package/build/store-directory.js +254 -0
- package/build/store.d.ts +147 -0
- package/build/store.js +1714 -0
- package/build/truncate.d.ts +57 -0
- package/build/truncate.js +142 -0
- package/build/types.d.ts +117 -0
- package/build/types.js +20 -0
- package/build/util/claude-config.d.ts +32 -0
- package/build/util/claude-config.js +84 -0
- package/build/util/hook-config.d.ts +27 -0
- package/build/util/hook-config.js +76 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/build/util/plugin-cache-integrity.d.ts +51 -0
- package/build/util/plugin-cache-integrity.js +146 -0
- package/build/util/project-dir.d.ts +149 -0
- package/build/util/project-dir.js +340 -0
- package/build/util/sibling-mcp.d.ts +79 -0
- package/build/util/sibling-mcp.js +181 -0
- package/cli.bundle.mjs +1495 -0
- package/configs/antigravity/GEMINI.md +68 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +91 -0
- package/configs/codex/AGENTS.md +99 -0
- package/configs/codex/config.toml +8 -0
- package/configs/codex/hooks.json +47 -0
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/cursor/context-mode.mdc +76 -0
- package/configs/cursor/hooks.json +21 -0
- package/configs/cursor/mcp.json +7 -0
- package/configs/gemini-cli/GEMINI.md +88 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +60 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +7 -0
- package/configs/kilo/AGENTS.md +84 -0
- package/configs/kilo/kilo.json +6 -0
- package/configs/kimi/hooks.json +54 -0
- package/configs/kiro/KIRO.md +84 -0
- package/configs/kiro/agent.json +18 -0
- package/configs/kiro/mcp.json +7 -0
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +87 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/configs/opencode/AGENTS.md +84 -0
- package/configs/opencode/opencode.json +6 -0
- package/configs/pi/AGENTS.md +3 -0
- package/configs/qwen-code/QWEN.md +91 -0
- package/configs/vscode-copilot/copilot-instructions.md +80 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +7 -0
- package/configs/zed/AGENTS.md +68 -0
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/auto-injection.mjs +102 -0
- package/hooks/cache-heal-utils.mjs +379 -0
- package/hooks/codex/platform.mjs +1 -0
- package/hooks/codex/posttooluse.mjs +66 -0
- package/hooks/codex/precompact.mjs +70 -0
- package/hooks/codex/pretooluse.mjs +42 -0
- package/hooks/codex/sessionstart.mjs +121 -0
- package/hooks/codex/stop.mjs +138 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +356 -0
- package/hooks/core/mcp-ready.mjs +109 -0
- package/hooks/core/platform-detect.mjs +49 -0
- package/hooks/core/routing.mjs +1050 -0
- package/hooks/core/stdin.mjs +90 -0
- package/hooks/core/tool-naming.mjs +57 -0
- package/hooks/cursor/afteragentresponse.mjs +74 -0
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +72 -0
- package/hooks/cursor/pretooluse.mjs +26 -0
- package/hooks/cursor/sessionstart.mjs +97 -0
- package/hooks/cursor/stop.mjs +48 -0
- package/hooks/ensure-deps.mjs +254 -0
- package/hooks/formatters/claude-code.mjs +113 -0
- package/hooks/formatters/cursor.mjs +37 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftermodel.mjs +70 -0
- package/hooks/gemini-cli/aftertool.mjs +61 -0
- package/hooks/gemini-cli/beforeagent.mjs +99 -0
- package/hooks/gemini-cli/beforetool.mjs +27 -0
- package/hooks/gemini-cli/precompress.mjs +55 -0
- package/hooks/gemini-cli/sessionstart.mjs +137 -0
- package/hooks/heal-partial-install.mjs +712 -0
- package/hooks/hooks.json +143 -0
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +55 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
- package/hooks/kimi/platform.mjs +1 -0
- package/hooks/kimi/posttooluse.mjs +72 -0
- package/hooks/kimi/precompact.mjs +80 -0
- package/hooks/kimi/pretooluse.mjs +42 -0
- package/hooks/kimi/sessionend.mjs +61 -0
- package/hooks/kimi/sessionstart.mjs +113 -0
- package/hooks/kimi/stop.mjs +132 -0
- package/hooks/kimi/userpromptsubmit.mjs +90 -0
- package/hooks/kiro/agentspawn.mjs +97 -0
- package/hooks/kiro/posttooluse.mjs +51 -0
- package/hooks/kiro/pretooluse.mjs +64 -0
- package/hooks/kiro/userpromptsubmit.mjs +88 -0
- package/hooks/normalize-hooks.mjs +323 -0
- package/hooks/platform-bridge.mjs +340 -0
- package/hooks/posttooluse.mjs +225 -0
- package/hooks/precompact.mjs +100 -0
- package/hooks/pretooluse.mjs +227 -0
- package/hooks/qwen-code/platform.mjs +1 -0
- package/hooks/qwen-code/stop.mjs +168 -0
- package/hooks/routing-block.mjs +105 -0
- package/hooks/run-hook.mjs +95 -0
- package/hooks/security.bundle.mjs +2 -0
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +145 -0
- package/hooks/session-directive.mjs +520 -0
- package/hooks/session-extract.bundle.mjs +3 -0
- package/hooks/session-helpers.mjs +428 -0
- package/hooks/session-loaders.mjs +448 -0
- package/hooks/session-snapshot.bundle.mjs +32 -0
- package/hooks/sessionstart.mjs +464 -0
- package/hooks/stop.mjs +82 -0
- package/hooks/suppress-stderr.mjs +25 -0
- package/hooks/userpromptsubmit.mjs +108 -0
- package/hooks/vscode-copilot/posttooluse.mjs +61 -0
- package/hooks/vscode-copilot/precompact.mjs +55 -0
- package/hooks/vscode-copilot/pretooluse.mjs +27 -0
- package/hooks/vscode-copilot/sessionstart.mjs +129 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +132 -0
- package/scripts/heal-better-sqlite3.mjs +430 -0
- package/scripts/heal-installed-plugins.mjs +635 -0
- package/scripts/plugin-cache-integrity.mjs +248 -0
- package/scripts/postinstall.mjs +397 -0
- package/server.bundle.mjs +1421 -0
- package/server.js +5 -0
- package/skills/.ignore +7 -0
- package/skills/context-mode/SKILL.md +300 -0
- package/skills/context-mode/references/anti-patterns.md +283 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
- package/skills/ctx-doctor/SKILL.md +22 -0
- package/skills/ctx-index/SKILL.md +46 -0
- package/skills/ctx-insight/SKILL.md +24 -0
- package/skills/ctx-purge/SKILL.md +49 -0
- package/skills/ctx-search/SKILL.md +35 -0
- package/skills/ctx-stats/SKILL.md +26 -0
- package/skills/ctx-upgrade/SKILL.md +31 -0
- package/start.mjs +616 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/copilot-cli — GitHub Copilot CLI adapter.
|
|
3
|
+
*
|
|
4
|
+
* Native config:
|
|
5
|
+
* - MCP: $COPILOT_HOME/mcp-config.json or ~/.copilot/mcp-config.json
|
|
6
|
+
* under root key `mcpServers`.
|
|
7
|
+
* - Hooks: $COPILOT_HOME/hooks/context-mode.json or
|
|
8
|
+
* ~/.copilot/hooks/context-mode.json.
|
|
9
|
+
*
|
|
10
|
+
* Hooks use Copilot CLI's native camelCase event keys (preToolUse /
|
|
11
|
+
* postToolUse / sessionStart / userPromptSubmitted / agentStop / preCompact)
|
|
12
|
+
* with flat `{ type, command }` entries. (The CLI also accepts PascalCase
|
|
13
|
+
* event names alongside camelCase — copilot-cli changelog.md:1065 — so the
|
|
14
|
+
* casing is not load-bearing; we use camelCase because it is the CLI's native
|
|
15
|
+
* naming.) Copilot CLI's command output contract is top-level
|
|
16
|
+
* (`permissionDecision`, `modifiedArgs`, `additionalContext`), so this adapter
|
|
17
|
+
* overrides the response formatter from CopilotBaseAdapter.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { dirname, join, resolve } from "node:path";
|
|
22
|
+
import { CopilotBaseAdapter } from "../copilot-base.js";
|
|
23
|
+
import { resolveContextModeDataRoot } from "../base.js";
|
|
24
|
+
import { parseJsonc } from "../../util/jsonc.js";
|
|
25
|
+
import { HOOK_TYPES as COPILOT_HOOK_NAMES, HOOK_SCRIPTS as COPILOT_HOOK_SCRIPTS, buildHookCommand as buildCopilotHookCommand, } from "./hooks.js";
|
|
26
|
+
const COPILOT_PLUGIN_ENV = "CONTEXT_MODE_COPILOT_PLUGIN";
|
|
27
|
+
function isCopilotPluginRuntime() {
|
|
28
|
+
return process.env[COPILOT_PLUGIN_ENV] === "1";
|
|
29
|
+
}
|
|
30
|
+
function copilotPluginHooksPath(pluginRoot) {
|
|
31
|
+
return resolve(pluginRoot, "configs", "copilot-cli", "hooks.json");
|
|
32
|
+
}
|
|
33
|
+
function readHookConfig(path) {
|
|
34
|
+
return parseJsonc(readFileSync(path, "utf-8")) ?? {};
|
|
35
|
+
}
|
|
36
|
+
function hasHook(hooks, hookName) {
|
|
37
|
+
return Array.isArray(hooks?.[hookName]) && (hooks?.[hookName]).length > 0;
|
|
38
|
+
}
|
|
39
|
+
export function copilotCliHome() {
|
|
40
|
+
const raw = process.env.COPILOT_HOME;
|
|
41
|
+
if (raw && raw.trim() !== "") {
|
|
42
|
+
if (raw.startsWith("~")) {
|
|
43
|
+
return join(homedir(), raw.replace(/^~[/\\]?/, ""));
|
|
44
|
+
}
|
|
45
|
+
return resolve(raw);
|
|
46
|
+
}
|
|
47
|
+
return join(homedir(), ".copilot");
|
|
48
|
+
}
|
|
49
|
+
export function copilotCliMcpConfigPath() {
|
|
50
|
+
return join(copilotCliHome(), "mcp-config.json");
|
|
51
|
+
}
|
|
52
|
+
export class CopilotCliAdapter extends CopilotBaseAdapter {
|
|
53
|
+
constructor() {
|
|
54
|
+
super([".copilot"]);
|
|
55
|
+
}
|
|
56
|
+
name = "GitHub Copilot CLI";
|
|
57
|
+
hookModule = {
|
|
58
|
+
HOOK_TYPES: COPILOT_HOOK_NAMES,
|
|
59
|
+
HOOK_SCRIPTS: COPILOT_HOOK_SCRIPTS,
|
|
60
|
+
buildHookCommand: buildCopilotHookCommand,
|
|
61
|
+
};
|
|
62
|
+
hookSubdir = "copilot-cli";
|
|
63
|
+
extractSessionId(input) {
|
|
64
|
+
const raw = input;
|
|
65
|
+
if (raw.transcript_path) {
|
|
66
|
+
const match = raw.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
|
|
67
|
+
if (match)
|
|
68
|
+
return match[1];
|
|
69
|
+
}
|
|
70
|
+
if (raw.conversation_id)
|
|
71
|
+
return raw.conversation_id;
|
|
72
|
+
// `session_id` (snake_case) is the field Copilot CLI documents in its hook
|
|
73
|
+
// payloads (copilot-cli changelog.md:811). Prefer it; `sessionId` (camelCase)
|
|
74
|
+
// is kept only as a defensive fallback for non-standard payload shapes and is
|
|
75
|
+
// not a documented Copilot CLI field.
|
|
76
|
+
if (raw.session_id)
|
|
77
|
+
return raw.session_id;
|
|
78
|
+
if (input.sessionId)
|
|
79
|
+
return input.sessionId;
|
|
80
|
+
return `pid-${process.ppid}`;
|
|
81
|
+
}
|
|
82
|
+
getProjectDir() {
|
|
83
|
+
return process.cwd();
|
|
84
|
+
}
|
|
85
|
+
parsePreToolUseInput(raw) {
|
|
86
|
+
const input = raw;
|
|
87
|
+
return {
|
|
88
|
+
toolName: input.tool_name ?? input.toolName ?? "",
|
|
89
|
+
toolInput: input.tool_input ?? input.toolArgs ?? {},
|
|
90
|
+
sessionId: this.extractSessionId(input),
|
|
91
|
+
projectDir: typeof input.cwd === "string" && input.cwd ? input.cwd : process.cwd(),
|
|
92
|
+
raw,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
parsePostToolUseInput(raw) {
|
|
96
|
+
const input = raw;
|
|
97
|
+
const output = input.tool_result?.text_result_for_llm ??
|
|
98
|
+
input.toolResult?.textResultForLlm ??
|
|
99
|
+
(typeof input.tool_response === "string" ? input.tool_response : undefined) ??
|
|
100
|
+
input.tool_output;
|
|
101
|
+
return {
|
|
102
|
+
toolName: input.tool_name ?? input.toolName ?? "",
|
|
103
|
+
toolInput: input.tool_input ?? input.toolArgs ?? {},
|
|
104
|
+
toolOutput: output,
|
|
105
|
+
isError: input.is_error,
|
|
106
|
+
sessionId: this.extractSessionId(input),
|
|
107
|
+
projectDir: typeof input.cwd === "string" && input.cwd ? input.cwd : process.cwd(),
|
|
108
|
+
raw,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
getSettingsPath(_projectDir) {
|
|
112
|
+
return join(copilotCliHome(), "hooks", "context-mode.json");
|
|
113
|
+
}
|
|
114
|
+
getConfigDir(_projectDir) {
|
|
115
|
+
return copilotCliHome();
|
|
116
|
+
}
|
|
117
|
+
getSessionDir() {
|
|
118
|
+
// Parity with codex/kimi: honor CONTEXT_MODE_DATA_DIR first, else root
|
|
119
|
+
// session storage at getConfigDir() (= copilotCliHome(), COPILOT_HOME-aware)
|
|
120
|
+
// so the TS server reads sessions from the SAME place the hook runtime
|
|
121
|
+
// (COPILOT_OPTS configDirEnv: "COPILOT_HOME") writes them. Without this, a
|
|
122
|
+
// relocated COPILOT_HOME splits hook writes ($COPILOT_HOME/...) from server
|
|
123
|
+
// reads (~/.copilot/...) and sessions appear empty/orphaned.
|
|
124
|
+
const override = resolveContextModeDataRoot();
|
|
125
|
+
const dir = override
|
|
126
|
+
? join(override, "context-mode", "sessions")
|
|
127
|
+
: join(this.getConfigDir(), "context-mode", "sessions");
|
|
128
|
+
mkdirSync(dir, { recursive: true });
|
|
129
|
+
return dir;
|
|
130
|
+
}
|
|
131
|
+
getInstructionFiles() {
|
|
132
|
+
return [".github/copilot-instructions.md", "AGENTS.md"];
|
|
133
|
+
}
|
|
134
|
+
// ── Hook-config writing (Copilot CLI shape) ────────────
|
|
135
|
+
//
|
|
136
|
+
// GitHub Copilot CLI's hooks schema differs from the VS Code / JetBrains
|
|
137
|
+
// Copilot extensions that share CopilotBaseAdapter:
|
|
138
|
+
// - hook entries are FLAT `{ type: "command", command }` — NOT the
|
|
139
|
+
// Claude-Code nested `{ matcher, hooks: [...] }` shape the base emits, and
|
|
140
|
+
// - we emit a top-level `"version": 1`. This is OPTIONAL, not mandatory:
|
|
141
|
+
// the Copilot CLI accepts hook config files that omit the version field
|
|
142
|
+
// (copilot-cli changelog.md:1109). We still write version:1 to pin the
|
|
143
|
+
// schema explicitly — it is harmless and self-documenting, NOT a
|
|
144
|
+
// hooks-never-fire requirement.
|
|
145
|
+
// The shared base also hardcodes `mkdir .github/hooks` and writes there, but
|
|
146
|
+
// this adapter's settings live at ~/.copilot/hooks/context-mode.json, so
|
|
147
|
+
// writeSettings is overridden to create the real parent directory. All three
|
|
148
|
+
// overrides are scoped to copilot-cli so vscode/jetbrains-copilot keep the
|
|
149
|
+
// shared base behavior unchanged.
|
|
150
|
+
generateHookConfig(pluginRoot) {
|
|
151
|
+
const { HOOK_TYPES, buildHookCommand } = this.hookModule;
|
|
152
|
+
const flat = (hookType) => [
|
|
153
|
+
{ type: "command", command: buildHookCommand(hookType, pluginRoot) },
|
|
154
|
+
];
|
|
155
|
+
// Emit an entry for every declared hook type (preToolUse, postToolUse,
|
|
156
|
+
// preCompact, sessionStart, userPromptSubmitted, agentStop).
|
|
157
|
+
const config = {};
|
|
158
|
+
for (const hookType of Object.values(HOOK_TYPES)) {
|
|
159
|
+
config[hookType] = flat(hookType);
|
|
160
|
+
}
|
|
161
|
+
return config;
|
|
162
|
+
}
|
|
163
|
+
writeSettings(settings) {
|
|
164
|
+
const configPath = this.getSettingsPath();
|
|
165
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
166
|
+
writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
167
|
+
}
|
|
168
|
+
configureAllHooks(pluginRoot) {
|
|
169
|
+
const changes = [];
|
|
170
|
+
const settings = this.readSettings() ?? {};
|
|
171
|
+
const hooks = settings.hooks ?? {};
|
|
172
|
+
const { HOOK_TYPES, HOOK_SCRIPTS, buildHookCommand } = this.hookModule;
|
|
173
|
+
// Configure every hook type the module declares (preToolUse, postToolUse,
|
|
174
|
+
// preCompact, sessionStart, userPromptSubmitted, agentStop) — driven by
|
|
175
|
+
// HOOK_TYPES so new events are picked up automatically.
|
|
176
|
+
for (const hookType of Object.values(HOOK_TYPES)) {
|
|
177
|
+
if (!HOOK_SCRIPTS[hookType])
|
|
178
|
+
continue;
|
|
179
|
+
const desired = [
|
|
180
|
+
{ type: "command", command: buildHookCommand(hookType, pluginRoot) },
|
|
181
|
+
];
|
|
182
|
+
// Only treat a hook as drift when it differs from desired, so repeated
|
|
183
|
+
// `context-mode upgrade` runs stay idempotent.
|
|
184
|
+
if (JSON.stringify(hooks[hookType]) !== JSON.stringify(desired)) {
|
|
185
|
+
hooks[hookType] = desired;
|
|
186
|
+
changes.push(`Configured ${hookType} hook`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// We pin version:1 explicitly (optional per copilot-cli changelog.md:1109,
|
|
190
|
+
// but self-documenting); treat a missing/mismatched value as drift to repair.
|
|
191
|
+
if (settings.version !== 1) {
|
|
192
|
+
changes.push("Set hooks schema version to 1");
|
|
193
|
+
}
|
|
194
|
+
if (changes.length > 0) {
|
|
195
|
+
settings.version = 1;
|
|
196
|
+
settings.hooks = hooks;
|
|
197
|
+
this.writeSettings(settings);
|
|
198
|
+
changes.push(`Wrote hook config to ${this.getSettingsPath()}`);
|
|
199
|
+
}
|
|
200
|
+
return changes;
|
|
201
|
+
}
|
|
202
|
+
readSettings() {
|
|
203
|
+
try {
|
|
204
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
205
|
+
return parseJsonc(raw) ?? null;
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
formatPreToolUseResponse(response) {
|
|
212
|
+
if (response.decision === "deny") {
|
|
213
|
+
return {
|
|
214
|
+
permissionDecision: "deny",
|
|
215
|
+
permissionDecisionReason: response.reason ?? "Blocked by context-mode hook",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (response.decision === "ask") {
|
|
219
|
+
return {
|
|
220
|
+
permissionDecision: "ask",
|
|
221
|
+
permissionDecisionReason: response.reason ?? "Action requires user confirmation",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (response.decision === "modify" && response.updatedInput) {
|
|
225
|
+
return { modifiedArgs: response.updatedInput };
|
|
226
|
+
}
|
|
227
|
+
if (response.decision === "context" && response.additionalContext) {
|
|
228
|
+
return { additionalContext: response.additionalContext };
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
formatPostToolUseResponse(response) {
|
|
233
|
+
if (response.updatedOutput) {
|
|
234
|
+
return {
|
|
235
|
+
modifiedResult: {
|
|
236
|
+
resultType: "success",
|
|
237
|
+
textResultForLlm: response.updatedOutput,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (response.additionalContext) {
|
|
242
|
+
return { additionalContext: response.additionalContext };
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
formatPreCompactResponse(response) {
|
|
247
|
+
void response;
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
formatSessionStartResponse(response) {
|
|
251
|
+
return response.context ? { additionalContext: response.context } : undefined;
|
|
252
|
+
}
|
|
253
|
+
validateHooks(pluginRoot) {
|
|
254
|
+
const results = [];
|
|
255
|
+
const pluginRuntime = isCopilotPluginRuntime();
|
|
256
|
+
const settingsPath = pluginRuntime ? copilotPluginHooksPath(pluginRoot) : this.getSettingsPath();
|
|
257
|
+
const settingsLabel = pluginRuntime
|
|
258
|
+
? `Copilot CLI plugin bundle hooks.json (${settingsPath})`
|
|
259
|
+
: settingsPath;
|
|
260
|
+
const fix = pluginRuntime
|
|
261
|
+
? "copilot plugin install mksglu/context-mode:configs/copilot-cli"
|
|
262
|
+
: "context-mode upgrade";
|
|
263
|
+
try {
|
|
264
|
+
const config = readHookConfig(settingsPath);
|
|
265
|
+
const hooks = config.hooks;
|
|
266
|
+
results.push({
|
|
267
|
+
check: "Hooks schema version",
|
|
268
|
+
status: config.version === 1 ? "pass" : "fail",
|
|
269
|
+
message: config.version === 1
|
|
270
|
+
? `${settingsLabel} declares the required "version": 1`
|
|
271
|
+
: `${settingsLabel} is missing top-level "version": 1`,
|
|
272
|
+
...(config.version === 1 ? {} : { fix }),
|
|
273
|
+
});
|
|
274
|
+
for (const hookName of Object.values(COPILOT_HOOK_NAMES)) {
|
|
275
|
+
const configured = hasHook(hooks, hookName);
|
|
276
|
+
results.push({
|
|
277
|
+
check: `${hookName} hook`,
|
|
278
|
+
status: configured ? "pass" : "fail",
|
|
279
|
+
message: configured
|
|
280
|
+
? `${hookName} hook configured in ${settingsLabel}`
|
|
281
|
+
: `${hookName} not found in ${settingsLabel}`,
|
|
282
|
+
...(configured ? {} : { fix }),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
results.push({
|
|
288
|
+
check: "Hook configuration",
|
|
289
|
+
status: "fail",
|
|
290
|
+
message: `Could not read ${settingsLabel}`,
|
|
291
|
+
fix,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return results;
|
|
295
|
+
}
|
|
296
|
+
checkPluginRegistration() {
|
|
297
|
+
if (isCopilotPluginRuntime()) {
|
|
298
|
+
return {
|
|
299
|
+
check: "MCP registration",
|
|
300
|
+
status: "pass",
|
|
301
|
+
message: "context-mode loaded from the Copilot CLI plugin bundle",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
const raw = readFileSync(copilotCliMcpConfigPath(), "utf-8");
|
|
306
|
+
const config = parseJsonc(raw) ?? {};
|
|
307
|
+
const mcpServers = config?.mcpServers ?? {};
|
|
308
|
+
if ("context-mode" in mcpServers) {
|
|
309
|
+
return {
|
|
310
|
+
check: "MCP registration",
|
|
311
|
+
status: "pass",
|
|
312
|
+
message: "context-mode found in Copilot CLI mcp-config.json",
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
check: "MCP registration",
|
|
317
|
+
status: "fail",
|
|
318
|
+
message: "context-mode not found in Copilot CLI mcpServers",
|
|
319
|
+
// `context-mode upgrade` configures HOOKS only — never the MCP server.
|
|
320
|
+
// Copilot CLI's own command writes ~/.copilot/mcp-config.json for us.
|
|
321
|
+
fix: "copilot mcp add context-mode -- context-mode",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return {
|
|
326
|
+
check: "MCP registration",
|
|
327
|
+
status: "fail",
|
|
328
|
+
message: `Could not read ${copilotCliMcpConfigPath()}`,
|
|
329
|
+
fix: "copilot mcp add context-mode -- context-mode",
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
getInstalledVersion() {
|
|
334
|
+
// Copilot's user-level MCP config and local/plugin-dir bundle do not expose
|
|
335
|
+
// a durable installed plugin version to this process. The npm package
|
|
336
|
+
// version is already checked above, so avoid the false `vconfigured` WARN.
|
|
337
|
+
return existsSync(copilotCliMcpConfigPath()) || existsSync(this.getSettingsPath())
|
|
338
|
+
? "standalone"
|
|
339
|
+
: "not installed";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/cursor/hooks — Cursor hook definitions and config helpers.
|
|
3
|
+
*
|
|
4
|
+
* Cursor native hook config lives in `.cursor/hooks.json` or `~/.cursor/hooks.json`.
|
|
5
|
+
* Unlike Claude/Gemini/VS Code Copilot, each hook entry is a flat object rather
|
|
6
|
+
* than a `{ matcher, hooks: [...] }` wrapper.
|
|
7
|
+
*/
|
|
8
|
+
/** Cursor hook type names. */
|
|
9
|
+
export declare const HOOK_TYPES: {
|
|
10
|
+
readonly PRE_TOOL_USE: "preToolUse";
|
|
11
|
+
readonly POST_TOOL_USE: "postToolUse";
|
|
12
|
+
readonly SESSION_START: "sessionStart";
|
|
13
|
+
readonly STOP: "stop";
|
|
14
|
+
readonly AFTER_AGENT_RESPONSE: "afterAgentResponse";
|
|
15
|
+
};
|
|
16
|
+
export type HookType = (typeof HOOK_TYPES)[keyof typeof HOOK_TYPES];
|
|
17
|
+
/** Map of hook types that have actual script files. */
|
|
18
|
+
export declare const HOOK_SCRIPTS: Partial<Record<HookType, string>>;
|
|
19
|
+
/**
|
|
20
|
+
* Negative-lookahead matcher for external MCP tool namespaces on Cursor (#529).
|
|
21
|
+
*
|
|
22
|
+
* Cursor MCP wire shape: `MCP:<tool>` (verified in
|
|
23
|
+
* tests/fixtures/cursor/pretooluse-mcp.json, hooks/cursor/posttooluse.mjs:19-25).
|
|
24
|
+
* Context-mode's own tools surface as `MCP:ctx_<...>`. The negative lookahead
|
|
25
|
+
* on the `ctx_` prefix fires for every other MCP tool whose payload would
|
|
26
|
+
* otherwise flood the model's context window before PostToolUse can act.
|
|
27
|
+
*
|
|
28
|
+
* Routing.mjs `isExternalMcpTool` is extended to recognise the `MCP:` prefix
|
|
29
|
+
* so the routing branch returns external-MCP guidance instead of passthrough.
|
|
30
|
+
*/
|
|
31
|
+
export declare const EXTERNAL_MCP_MATCHER_PATTERN = "MCP:(?!ctx_)";
|
|
32
|
+
/** Canonical Cursor-native matchers for tools context-mode routes proactively. */
|
|
33
|
+
export declare const PRE_TOOL_USE_MATCHERS: readonly ["Shell", "Read", "Grep", "WebFetch", "mcp_web_fetch", "mcp_fetch_tool", "Task", "MCP:ctx_execute", "MCP:ctx_execute_file", "MCP:ctx_batch_execute", "MCP:(?!ctx_)"];
|
|
34
|
+
export declare const PRE_TOOL_USE_MATCHER_PATTERN: string;
|
|
35
|
+
/** Required hooks for native Cursor support. */
|
|
36
|
+
export declare const REQUIRED_HOOKS: HookType[];
|
|
37
|
+
/** Optional hooks that improve behavior but aren't strictly required. */
|
|
38
|
+
export declare const OPTIONAL_HOOKS: HookType[];
|
|
39
|
+
/** Minimal native Cursor hook entry shape. */
|
|
40
|
+
export interface CursorHookCommandEntry {
|
|
41
|
+
type?: string;
|
|
42
|
+
command?: string;
|
|
43
|
+
matcher?: string;
|
|
44
|
+
timeout?: number;
|
|
45
|
+
loop_limit?: number | null;
|
|
46
|
+
failClosed?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Check whether a native Cursor hook entry points to context-mode. */
|
|
49
|
+
export declare function isContextModeHook(entry: CursorHookCommandEntry | {
|
|
50
|
+
hooks?: Array<{
|
|
51
|
+
command?: string;
|
|
52
|
+
}>;
|
|
53
|
+
}, hookType: HookType): boolean;
|
|
54
|
+
/** Build the CLI dispatcher command for a Cursor hook type. */
|
|
55
|
+
export declare function buildHookCommand(hookType: HookType): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/cursor/hooks — Cursor hook definitions and config helpers.
|
|
3
|
+
*
|
|
4
|
+
* Cursor native hook config lives in `.cursor/hooks.json` or `~/.cursor/hooks.json`.
|
|
5
|
+
* Unlike Claude/Gemini/VS Code Copilot, each hook entry is a flat object rather
|
|
6
|
+
* than a `{ matcher, hooks: [...] }` wrapper.
|
|
7
|
+
*/
|
|
8
|
+
/** Cursor hook type names. */
|
|
9
|
+
export const HOOK_TYPES = {
|
|
10
|
+
PRE_TOOL_USE: "preToolUse",
|
|
11
|
+
POST_TOOL_USE: "postToolUse",
|
|
12
|
+
SESSION_START: "sessionStart",
|
|
13
|
+
STOP: "stop",
|
|
14
|
+
AFTER_AGENT_RESPONSE: "afterAgentResponse",
|
|
15
|
+
};
|
|
16
|
+
/** Map of hook types that have actual script files. */
|
|
17
|
+
export const HOOK_SCRIPTS = {
|
|
18
|
+
[HOOK_TYPES.PRE_TOOL_USE]: "pretooluse.mjs",
|
|
19
|
+
[HOOK_TYPES.POST_TOOL_USE]: "posttooluse.mjs",
|
|
20
|
+
[HOOK_TYPES.SESSION_START]: "sessionstart.mjs",
|
|
21
|
+
[HOOK_TYPES.STOP]: "stop.mjs",
|
|
22
|
+
[HOOK_TYPES.AFTER_AGENT_RESPONSE]: "afteragentresponse.mjs",
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Negative-lookahead matcher for external MCP tool namespaces on Cursor (#529).
|
|
26
|
+
*
|
|
27
|
+
* Cursor MCP wire shape: `MCP:<tool>` (verified in
|
|
28
|
+
* tests/fixtures/cursor/pretooluse-mcp.json, hooks/cursor/posttooluse.mjs:19-25).
|
|
29
|
+
* Context-mode's own tools surface as `MCP:ctx_<...>`. The negative lookahead
|
|
30
|
+
* on the `ctx_` prefix fires for every other MCP tool whose payload would
|
|
31
|
+
* otherwise flood the model's context window before PostToolUse can act.
|
|
32
|
+
*
|
|
33
|
+
* Routing.mjs `isExternalMcpTool` is extended to recognise the `MCP:` prefix
|
|
34
|
+
* so the routing branch returns external-MCP guidance instead of passthrough.
|
|
35
|
+
*/
|
|
36
|
+
export const EXTERNAL_MCP_MATCHER_PATTERN = "MCP:(?!ctx_)";
|
|
37
|
+
/** Canonical Cursor-native matchers for tools context-mode routes proactively. */
|
|
38
|
+
// NOTE (Cursor-3, deferred): Cursor is closed-source and does not currently
|
|
39
|
+
// publish the exact tool name it uses for sub-agent dispatch (the analogue of
|
|
40
|
+
// Claude Code's "Task" tool). The "Task" matcher below is kept as a best-guess
|
|
41
|
+
// placeholder until probe data from a real Cursor session confirms the wire
|
|
42
|
+
// name. If/when that probe lands, replace or supplement this entry — do NOT
|
|
43
|
+
// add unverified matchers in the meantime. See Phase 7 audit
|
|
44
|
+
// `/tmp/v1.0.107-adapter-cursor.json` (Cursor-3) for the full deferral note.
|
|
45
|
+
export const PRE_TOOL_USE_MATCHERS = [
|
|
46
|
+
"Shell",
|
|
47
|
+
"Read",
|
|
48
|
+
"Grep",
|
|
49
|
+
"WebFetch",
|
|
50
|
+
"mcp_web_fetch",
|
|
51
|
+
"mcp_fetch_tool",
|
|
52
|
+
"Task",
|
|
53
|
+
"MCP:ctx_execute",
|
|
54
|
+
"MCP:ctx_execute_file",
|
|
55
|
+
"MCP:ctx_batch_execute",
|
|
56
|
+
EXTERNAL_MCP_MATCHER_PATTERN,
|
|
57
|
+
];
|
|
58
|
+
export const PRE_TOOL_USE_MATCHER_PATTERN = PRE_TOOL_USE_MATCHERS.join("|");
|
|
59
|
+
/** Required hooks for native Cursor support. */
|
|
60
|
+
export const REQUIRED_HOOKS = [
|
|
61
|
+
HOOK_TYPES.PRE_TOOL_USE,
|
|
62
|
+
];
|
|
63
|
+
/** Optional hooks that improve behavior but aren't strictly required. */
|
|
64
|
+
export const OPTIONAL_HOOKS = [HOOK_TYPES.POST_TOOL_USE];
|
|
65
|
+
/** Check whether a native Cursor hook entry points to context-mode. */
|
|
66
|
+
export function isContextModeHook(entry, hookType) {
|
|
67
|
+
const scriptName = HOOK_SCRIPTS[hookType];
|
|
68
|
+
const cliCommand = buildHookCommand(hookType);
|
|
69
|
+
if ("command" in entry) {
|
|
70
|
+
const cmd = entry.command ?? "";
|
|
71
|
+
return (scriptName != null && cmd.includes(scriptName)) || cmd.includes(cliCommand);
|
|
72
|
+
}
|
|
73
|
+
const wrappedEntry = entry;
|
|
74
|
+
return (wrappedEntry.hooks?.some((hook) => {
|
|
75
|
+
const cmd = hook.command ?? "";
|
|
76
|
+
return (scriptName != null && cmd.includes(scriptName)) || cmd.includes(cliCommand);
|
|
77
|
+
}) ?? false);
|
|
78
|
+
}
|
|
79
|
+
/** Build the CLI dispatcher command for a Cursor hook type. */
|
|
80
|
+
export function buildHookCommand(hookType) {
|
|
81
|
+
return `context-mode hook cursor ${hookType.toLowerCase()}`;
|
|
82
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/cursor — Cursor platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Native Cursor hooks use lower-camel hook names and flat command entries in
|
|
5
|
+
* `.cursor/hooks.json` / `~/.cursor/hooks.json`.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseAdapter } from "../base.js";
|
|
8
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
9
|
+
interface StopEvent {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
status: string;
|
|
12
|
+
loopCount: number;
|
|
13
|
+
generationId?: string;
|
|
14
|
+
transcriptPath?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class CursorAdapter extends BaseAdapter implements HookAdapter {
|
|
17
|
+
constructor();
|
|
18
|
+
readonly name = "Cursor";
|
|
19
|
+
readonly paradigm: HookParadigm;
|
|
20
|
+
readonly capabilities: PlatformCapabilities;
|
|
21
|
+
parsePreToolUseInput(raw: unknown): PreToolUseEvent;
|
|
22
|
+
parsePostToolUseInput(raw: unknown): PostToolUseEvent;
|
|
23
|
+
parseSessionStartInput(raw: unknown): SessionStartEvent;
|
|
24
|
+
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
25
|
+
formatPostToolUseResponse(response: PostToolUseResponse): unknown;
|
|
26
|
+
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
27
|
+
parseStopInput(raw: unknown): StopEvent;
|
|
28
|
+
formatStopResponse(response: {
|
|
29
|
+
followupMessage?: string;
|
|
30
|
+
}): Record<string, unknown>;
|
|
31
|
+
parseAfterAgentResponseInput(raw: unknown): {
|
|
32
|
+
text: string;
|
|
33
|
+
};
|
|
34
|
+
getSettingsPath(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Cursor stores conventions per project under .cursor/. Always returned
|
|
37
|
+
* as an absolute path resolved against `projectDir` (or `process.cwd()`
|
|
38
|
+
* when omitted) per the HookAdapter.getConfigDir contract.
|
|
39
|
+
*/
|
|
40
|
+
getConfigDir(projectDir?: string): string;
|
|
41
|
+
getInstructionFiles(): string[];
|
|
42
|
+
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
43
|
+
readSettings(): Record<string, unknown> | null;
|
|
44
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
45
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
46
|
+
/**
|
|
47
|
+
* Detects context-mode plugin installations under Cursor's plugin directories.
|
|
48
|
+
* Returns absolute paths to any `.cursor-plugin/plugin.json` files whose
|
|
49
|
+
* `name` matches `context-mode`.
|
|
50
|
+
*/
|
|
51
|
+
private detectPluginInstalls;
|
|
52
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
53
|
+
getInstalledVersion(): string;
|
|
54
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
55
|
+
setHookPermissions(pluginRoot: string): string[];
|
|
56
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
57
|
+
private getCandidateHookConfigPaths;
|
|
58
|
+
private getProjectDir;
|
|
59
|
+
private extractSessionId;
|
|
60
|
+
private loadNativeHookConfig;
|
|
61
|
+
private hasClaudeCompatibilityHooks;
|
|
62
|
+
private upsertHookEntry;
|
|
63
|
+
}
|
|
64
|
+
export {};
|