@ken-jo/agent-connector 0.1.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/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +348 -0
- package/dist/amp-QSFH2V7Y.js +217 -0
- package/dist/amp-QSFH2V7Y.js.map +1 -0
- package/dist/amp-TULUPXXL.js +237 -0
- package/dist/amp-TULUPXXL.js.map +1 -0
- package/dist/antigravity-CX2YLSY7.js +17 -0
- package/dist/antigravity-CX2YLSY7.js.map +1 -0
- package/dist/antigravity-STHZWMP3.js +171 -0
- package/dist/antigravity-STHZWMP3.js.map +1 -0
- package/dist/antigravity-cli-P6HADOAO.js +121 -0
- package/dist/antigravity-cli-P6HADOAO.js.map +1 -0
- package/dist/antigravity-cli-YQDFVV4U.js +86 -0
- package/dist/antigravity-cli-YQDFVV4U.js.map +1 -0
- package/dist/chunk-54EMKTTX.js +191 -0
- package/dist/chunk-54EMKTTX.js.map +1 -0
- package/dist/chunk-7L6ITC4T.js +54 -0
- package/dist/chunk-7L6ITC4T.js.map +1 -0
- package/dist/chunk-7MSDGHJW.js +316 -0
- package/dist/chunk-7MSDGHJW.js.map +1 -0
- package/dist/chunk-BCHWFTV4.js +196 -0
- package/dist/chunk-BCHWFTV4.js.map +1 -0
- package/dist/chunk-DBQMX75U.js +36 -0
- package/dist/chunk-DBQMX75U.js.map +1 -0
- package/dist/chunk-EXLS7BSI.js +29 -0
- package/dist/chunk-EXLS7BSI.js.map +1 -0
- package/dist/chunk-JNM3TPLX.js +593 -0
- package/dist/chunk-JNM3TPLX.js.map +1 -0
- package/dist/chunk-JWNT5KW7.js +197 -0
- package/dist/chunk-JWNT5KW7.js.map +1 -0
- package/dist/chunk-KHO5WNTP.js +30 -0
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/chunk-LTT56GI3.js +155 -0
- package/dist/chunk-LTT56GI3.js.map +1 -0
- package/dist/chunk-MEH7GC7R.js +111 -0
- package/dist/chunk-MEH7GC7R.js.map +1 -0
- package/dist/chunk-MQPNU2CY.js +64 -0
- package/dist/chunk-MQPNU2CY.js.map +1 -0
- package/dist/chunk-OPXAP3AC.js +12 -0
- package/dist/chunk-OPXAP3AC.js.map +1 -0
- package/dist/chunk-RK6VBLXP.js +50 -0
- package/dist/chunk-RK6VBLXP.js.map +1 -0
- package/dist/chunk-TIIP3RG2.js +420 -0
- package/dist/chunk-TIIP3RG2.js.map +1 -0
- package/dist/chunk-TRKWUNO4.js +702 -0
- package/dist/chunk-TRKWUNO4.js.map +1 -0
- package/dist/chunk-TRQKHP7D.js +93 -0
- package/dist/chunk-TRQKHP7D.js.map +1 -0
- package/dist/chunk-WCETQWWK.js +59 -0
- package/dist/chunk-WCETQWWK.js.map +1 -0
- package/dist/chunk-WV63X7IK.js +357 -0
- package/dist/chunk-WV63X7IK.js.map +1 -0
- package/dist/chunk-XD7MY6HB.js +343 -0
- package/dist/chunk-XD7MY6HB.js.map +1 -0
- package/dist/chunk-XNAM7F35.js +276 -0
- package/dist/chunk-XNAM7F35.js.map +1 -0
- package/dist/chunk-XUCWLJY5.js +238 -0
- package/dist/chunk-XUCWLJY5.js.map +1 -0
- package/dist/chunk-YNZU6PAV.js +800 -0
- package/dist/chunk-YNZU6PAV.js.map +1 -0
- package/dist/chunk-YT43MUSJ.js +146 -0
- package/dist/chunk-YT43MUSJ.js.map +1 -0
- package/dist/claude-code-HKDV7T32.js +637 -0
- package/dist/claude-code-HKDV7T32.js.map +1 -0
- package/dist/claude-code-W74MNTAC.js +652 -0
- package/dist/claude-code-W74MNTAC.js.map +1 -0
- package/dist/cli/sdk.d.ts +54 -0
- package/dist/cli/sdk.js +82 -0
- package/dist/cli/sdk.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -0
- package/dist/codebuff-3PMZJJHP.js +307 -0
- package/dist/codebuff-3PMZJJHP.js.map +1 -0
- package/dist/codebuff-QX57FJDN.js +220 -0
- package/dist/codebuff-QX57FJDN.js.map +1 -0
- package/dist/codex-JFQTS6LU.js +593 -0
- package/dist/codex-JFQTS6LU.js.map +1 -0
- package/dist/codex-Q3ATRRX3.js +364 -0
- package/dist/codex-Q3ATRRX3.js.map +1 -0
- package/dist/copilot-cli-MTCGHZ2T.js +622 -0
- package/dist/copilot-cli-MTCGHZ2T.js.map +1 -0
- package/dist/copilot-cli-SJGIXVSD.js +369 -0
- package/dist/copilot-cli-SJGIXVSD.js.map +1 -0
- package/dist/crush-I7PK3FGI.js +360 -0
- package/dist/crush-I7PK3FGI.js.map +1 -0
- package/dist/crush-LN5QFFR2.js +183 -0
- package/dist/crush-LN5QFFR2.js.map +1 -0
- package/dist/cursor-4TMXKJOZ.js +603 -0
- package/dist/cursor-4TMXKJOZ.js.map +1 -0
- package/dist/cursor-QZ6MTO74.js +194 -0
- package/dist/cursor-QZ6MTO74.js.map +1 -0
- package/dist/detect-WIAGVYS4.js +68 -0
- package/dist/detect-WIAGVYS4.js.map +1 -0
- package/dist/doctor-EZW2K4NI.js +372 -0
- package/dist/doctor-EZW2K4NI.js.map +1 -0
- package/dist/droid-KN6VRMMT.js +424 -0
- package/dist/droid-KN6VRMMT.js.map +1 -0
- package/dist/droid-W5WF5XEX.js +224 -0
- package/dist/droid-W5WF5XEX.js.map +1 -0
- package/dist/gemini-cli-CIRW37TU.js +369 -0
- package/dist/gemini-cli-CIRW37TU.js.map +1 -0
- package/dist/gemini-cli-Y35FFK4M.js +705 -0
- package/dist/gemini-cli-Y35FFK4M.js.map +1 -0
- package/dist/goose-A3M5GODH.js +212 -0
- package/dist/goose-A3M5GODH.js.map +1 -0
- package/dist/goose-ZTGTSUFJ.js +480 -0
- package/dist/goose-ZTGTSUFJ.js.map +1 -0
- package/dist/hermes-H2KV44GK.js +470 -0
- package/dist/hermes-H2KV44GK.js.map +1 -0
- package/dist/hermes-ZYLMOFLZ.js +196 -0
- package/dist/hermes-ZYLMOFLZ.js.map +1 -0
- package/dist/hook-YVDILAMZ.js +75 -0
- package/dist/hook-YVDILAMZ.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/install-HQRYZOQ2.js +60 -0
- package/dist/install-HQRYZOQ2.js.map +1 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js +633 -0
- package/dist/jetbrains-copilot-HWA3DMH7.js.map +1 -0
- package/dist/kilo-NSWAVP6P.js +235 -0
- package/dist/kilo-NSWAVP6P.js.map +1 -0
- package/dist/kilo-QXJRCSLT.js +384 -0
- package/dist/kilo-QXJRCSLT.js.map +1 -0
- package/dist/kilo-cli-EAQTHXUW.js +587 -0
- package/dist/kilo-cli-EAQTHXUW.js.map +1 -0
- package/dist/kilo-cli-EVBKUODA.js +160 -0
- package/dist/kilo-cli-EVBKUODA.js.map +1 -0
- package/dist/kimi-EI3UM3ZV.js +155 -0
- package/dist/kimi-EI3UM3ZV.js.map +1 -0
- package/dist/kimi-GQ7VXNYT.js +423 -0
- package/dist/kimi-GQ7VXNYT.js.map +1 -0
- package/dist/kiro-UCLSLBW2.js +217 -0
- package/dist/kiro-UCLSLBW2.js.map +1 -0
- package/dist/kiro-VQTT6LO2.js +451 -0
- package/dist/kiro-VQTT6LO2.js.map +1 -0
- package/dist/leaderboard-K6ON3SWW.js +137 -0
- package/dist/leaderboard-K6ON3SWW.js.map +1 -0
- package/dist/mux-T2TV4CIQ.js +211 -0
- package/dist/mux-T2TV4CIQ.js.map +1 -0
- package/dist/mux-ZMLVAXOO.js +96 -0
- package/dist/mux-ZMLVAXOO.js.map +1 -0
- package/dist/omp-PB4VDKS6.js +586 -0
- package/dist/omp-PB4VDKS6.js.map +1 -0
- package/dist/openclaw-ETN2M63O.js +751 -0
- package/dist/openclaw-ETN2M63O.js.map +1 -0
- package/dist/openclaw-IYA7ASL6.js +150 -0
- package/dist/openclaw-IYA7ASL6.js.map +1 -0
- package/dist/opencode-FZ33JKUZ.js +734 -0
- package/dist/opencode-FZ33JKUZ.js.map +1 -0
- package/dist/opencode-HWT3RSMN.js +216 -0
- package/dist/opencode-HWT3RSMN.js.map +1 -0
- package/dist/package-DLOVE2E4.js +1132 -0
- package/dist/package-DLOVE2E4.js.map +1 -0
- package/dist/pi-CIU2QHT4.js +208 -0
- package/dist/pi-CIU2QHT4.js.map +1 -0
- package/dist/pi-UFX2OYFV.js +139 -0
- package/dist/pi-UFX2OYFV.js.map +1 -0
- package/dist/qwen-code-5POCNU6P.js +573 -0
- package/dist/qwen-code-5POCNU6P.js.map +1 -0
- package/dist/qwen-code-W7NUCDEN.js +134 -0
- package/dist/qwen-code-W7NUCDEN.js.map +1 -0
- package/dist/roo-code-IOIUXRDE.js +242 -0
- package/dist/roo-code-IOIUXRDE.js.map +1 -0
- package/dist/roo-code-ZLKH4A7N.js +229 -0
- package/dist/roo-code-ZLKH4A7N.js.map +1 -0
- package/dist/runtime/index.d.ts +157 -0
- package/dist/runtime/index.js +20 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/serve-HVWWY4B6.js +65 -0
- package/dist/serve-HVWWY4B6.js.map +1 -0
- package/dist/status-FE7TECZD.js +128 -0
- package/dist/status-FE7TECZD.js.map +1 -0
- package/dist/synthetic-UKR6SMGS.js +178 -0
- package/dist/synthetic-UKR6SMGS.js.map +1 -0
- package/dist/telemetry-36GESKQQ.js +349 -0
- package/dist/telemetry-36GESKQQ.js.map +1 -0
- package/dist/trae-JSNDZFKL.js +204 -0
- package/dist/trae-JSNDZFKL.js.map +1 -0
- package/dist/trae-PA26DPBW.js +157 -0
- package/dist/trae-PA26DPBW.js.map +1 -0
- package/dist/types-CYUB1OTr.d.ts +427 -0
- package/dist/uninstall-IGCKNOSF.js +72 -0
- package/dist/uninstall-IGCKNOSF.js.map +1 -0
- package/dist/upgrade-JV2VK242.js +104 -0
- package/dist/upgrade-JV2VK242.js.map +1 -0
- package/dist/usage-6ER6EMEY.js +324 -0
- package/dist/usage-6ER6EMEY.js.map +1 -0
- package/dist/usage-event-JKBI6XCF.js +48 -0
- package/dist/usage-event-JKBI6XCF.js.map +1 -0
- package/dist/vscode-copilot-CK3NO54M.js +715 -0
- package/dist/vscode-copilot-CK3NO54M.js.map +1 -0
- package/dist/warp-JNQA7PBU.js +126 -0
- package/dist/warp-JNQA7PBU.js.map +1 -0
- package/dist/warp-QNVDXOG4.js +199 -0
- package/dist/warp-QNVDXOG4.js.map +1 -0
- package/dist/zed-NNNX5MNT.js +272 -0
- package/dist/zed-NNNX5MNT.js.map +1 -0
- package/dist/zed-VRJKWYUR.js +209 -0
- package/dist/zed-VRJKWYUR.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
rewriteEnvRefs
|
|
4
|
+
} from "./chunk-DBQMX75U.js";
|
|
5
|
+
import {
|
|
6
|
+
BaseAdapter
|
|
7
|
+
} from "./chunk-TIIP3RG2.js";
|
|
8
|
+
import "./chunk-7L6ITC4T.js";
|
|
9
|
+
import {
|
|
10
|
+
buildHomeBinHookCommand,
|
|
11
|
+
buildServeWrapperCommand,
|
|
12
|
+
isHomeBinHookCommand,
|
|
13
|
+
shouldWrapForTelemetry
|
|
14
|
+
} from "./chunk-MEH7GC7R.js";
|
|
15
|
+
import "./chunk-TRQKHP7D.js";
|
|
16
|
+
|
|
17
|
+
// src/adapters/cursor/index.ts
|
|
18
|
+
import { existsSync } from "fs";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
var HOST = "cursor";
|
|
22
|
+
var MCP_ROOT_KEY = "mcpServers";
|
|
23
|
+
var CURSOR_HOOKS_VERSION = 1;
|
|
24
|
+
var CURSOR_EVENT = {
|
|
25
|
+
PreToolUse: "preToolUse",
|
|
26
|
+
PostToolUse: "postToolUse",
|
|
27
|
+
SessionStart: "sessionStart",
|
|
28
|
+
Stop: "stop"
|
|
29
|
+
};
|
|
30
|
+
var EVENT_MAP = {
|
|
31
|
+
PreToolUse: CURSOR_EVENT.PreToolUse,
|
|
32
|
+
PostToolUse: CURSOR_EVENT.PostToolUse,
|
|
33
|
+
SessionStart: CURSOR_EVENT.SessionStart,
|
|
34
|
+
Stop: CURSOR_EVENT.Stop
|
|
35
|
+
};
|
|
36
|
+
var CursorAdapter = class extends BaseAdapter {
|
|
37
|
+
id = HOST;
|
|
38
|
+
name = "Cursor";
|
|
39
|
+
paradigm = "json-stdio";
|
|
40
|
+
capabilities = {
|
|
41
|
+
// Cursor natively supports pre/post tool-use, session start, and stop.
|
|
42
|
+
preToolUse: true,
|
|
43
|
+
postToolUse: true,
|
|
44
|
+
preCompact: false,
|
|
45
|
+
sessionStart: true,
|
|
46
|
+
sessionEnd: false,
|
|
47
|
+
userPromptSubmit: false,
|
|
48
|
+
stop: true,
|
|
49
|
+
notification: false,
|
|
50
|
+
// Cursor's preToolUse can rewrite tool input (updated_input) but cannot
|
|
51
|
+
// rewrite already-emitted tool output.
|
|
52
|
+
canModifyArgs: true,
|
|
53
|
+
canModifyOutput: false,
|
|
54
|
+
canInjectSessionContext: true,
|
|
55
|
+
transports: ["stdio", "http"],
|
|
56
|
+
// Content surfaces: Cursor supports all three (commands, skills, subagents).
|
|
57
|
+
supportsCommands: true,
|
|
58
|
+
supportsSkills: true,
|
|
59
|
+
supportsSubagents: true
|
|
60
|
+
};
|
|
61
|
+
// ── Detection ────────────────────────────────────────────────────────────
|
|
62
|
+
detectInstalled(projectDir) {
|
|
63
|
+
const userDir = join(homedir(), ".cursor");
|
|
64
|
+
const userMcp = join(userDir, "mcp.json");
|
|
65
|
+
const userHooks = join(userDir, "hooks.json");
|
|
66
|
+
const projectDirCursor = join(projectDir, ".cursor");
|
|
67
|
+
const installed = existsSync(userDir) || existsSync(userMcp) || existsSync(userHooks) || existsSync(projectDirCursor);
|
|
68
|
+
return {
|
|
69
|
+
id: this.id,
|
|
70
|
+
name: this.name,
|
|
71
|
+
installed,
|
|
72
|
+
paradigm: this.paradigm,
|
|
73
|
+
capabilities: this.capabilities,
|
|
74
|
+
configPath: userMcp,
|
|
75
|
+
scope: "user",
|
|
76
|
+
reason: installed ? `found Cursor config under ${userDir}` : `no Cursor config at ${userDir}`,
|
|
77
|
+
confidence: installed ? "high" : "low"
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// ── Native paths ─────────────────────────────────────────────────────────
|
|
81
|
+
getConfigDir(ctx) {
|
|
82
|
+
return ctx.scope === "project" ? join(ctx.projectDir, ".cursor") : join(homedir(), ".cursor");
|
|
83
|
+
}
|
|
84
|
+
getServerConfigPath(ctx) {
|
|
85
|
+
return join(this.getConfigDir(ctx), "mcp.json");
|
|
86
|
+
}
|
|
87
|
+
getHookConfigPath(ctx) {
|
|
88
|
+
return join(this.getConfigDir(ctx), "hooks.json");
|
|
89
|
+
}
|
|
90
|
+
// ── MCP server install / uninstall ───────────────────────────────────────
|
|
91
|
+
installServer(ctx) {
|
|
92
|
+
const { connector } = ctx;
|
|
93
|
+
const override = connector.platforms[HOST]?.server;
|
|
94
|
+
if (!connector.server || override === false) {
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
platform: this.id,
|
|
98
|
+
action: "skip",
|
|
99
|
+
detail: connector.server ? "server registration disabled for cursor" : "connector declares no MCP server"
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
const server = override && typeof override === "object" ? { ...connector.server, ...override } : connector.server;
|
|
104
|
+
const serverPath = this.getServerConfigPath(ctx);
|
|
105
|
+
const entry = this.renderServerEntry(ctx, server);
|
|
106
|
+
return [
|
|
107
|
+
this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun)
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
uninstallServer(ctx) {
|
|
111
|
+
const serverPath = this.getServerConfigPath(ctx);
|
|
112
|
+
return [
|
|
113
|
+
this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun)
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
/** Render a normalized ServerDef into Cursor's native mcpServers entry. */
|
|
117
|
+
renderServerEntry(ctx, server) {
|
|
118
|
+
const transport = server.transport;
|
|
119
|
+
if (transport === "stdio") {
|
|
120
|
+
let command = server.command ?? "";
|
|
121
|
+
let args = [...server.args ?? []];
|
|
122
|
+
if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {
|
|
123
|
+
const wrapped = buildServeWrapperCommand(
|
|
124
|
+
ctx.homeBinPath,
|
|
125
|
+
ctx.connector.id,
|
|
126
|
+
command,
|
|
127
|
+
args,
|
|
128
|
+
ctx.scope,
|
|
129
|
+
this.id
|
|
130
|
+
);
|
|
131
|
+
command = wrapped.command;
|
|
132
|
+
args = wrapped.args;
|
|
133
|
+
}
|
|
134
|
+
const entry2 = { command: this.rewrite(command) };
|
|
135
|
+
if (args.length > 0) entry2.args = args.map((a) => this.rewrite(a));
|
|
136
|
+
const env = this.renderEnv(server.env);
|
|
137
|
+
if (env) entry2.env = env;
|
|
138
|
+
if (server.cwd) entry2.cwd = this.rewrite(server.cwd);
|
|
139
|
+
return entry2;
|
|
140
|
+
}
|
|
141
|
+
const entry = { url: this.rewrite(server.url ?? "") };
|
|
142
|
+
const headers = this.renderEnv(server.headers);
|
|
143
|
+
if (headers) entry.headers = headers;
|
|
144
|
+
return entry;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Render env/header values. Cursor supports its own `${env:VAR}` native
|
|
148
|
+
* interpolation, so translate `${env:VAR}` refs to that native token rather
|
|
149
|
+
* than baking secrets into the file. Literals pass through unchanged.
|
|
150
|
+
*/
|
|
151
|
+
renderEnv(env) {
|
|
152
|
+
if (!env || Object.keys(env).length === 0) return void 0;
|
|
153
|
+
const out = {};
|
|
154
|
+
for (const [k, v] of Object.entries(env)) out[k] = this.rewrite(v);
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Translate `${env:VAR(:-default)}` to Cursor's native `${env:VAR}` token. */
|
|
158
|
+
rewrite(value) {
|
|
159
|
+
return rewriteEnvRefs(value, cursorEnvToken);
|
|
160
|
+
}
|
|
161
|
+
// ── Hook install / uninstall ─────────────────────────────────────────────
|
|
162
|
+
installHooks(ctx) {
|
|
163
|
+
const { connector } = ctx;
|
|
164
|
+
if (connector.platforms[HOST]?.hooks === false) {
|
|
165
|
+
return [{ platform: this.id, action: "skip", detail: "hooks disabled for cursor" }];
|
|
166
|
+
}
|
|
167
|
+
if (connector.hookEvents.length === 0) {
|
|
168
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no hooks" }];
|
|
169
|
+
}
|
|
170
|
+
const hooksPath = this.getHookConfigPath(ctx);
|
|
171
|
+
const file = this.readJson(hooksPath) ?? {};
|
|
172
|
+
const hooks = file.hooks ??= {};
|
|
173
|
+
const changes = [];
|
|
174
|
+
let mutated = false;
|
|
175
|
+
for (const event of connector.hookEvents) {
|
|
176
|
+
const cursorEvent = EVENT_MAP[event];
|
|
177
|
+
if (!cursorEvent) {
|
|
178
|
+
changes.push({
|
|
179
|
+
platform: this.id,
|
|
180
|
+
action: "warn",
|
|
181
|
+
path: hooksPath,
|
|
182
|
+
detail: `${event} has no Cursor hook equivalent \u2014 skipped`
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);
|
|
187
|
+
const matcher = connector.hooks[event]?.matcher;
|
|
188
|
+
const entry = matcher ? { command, matcher } : { command };
|
|
189
|
+
const bucket = hooks[cursorEvent] ??= [];
|
|
190
|
+
const existingIdx = bucket.findIndex((e) => this.isOurCommand(e.command, ctx));
|
|
191
|
+
if (existingIdx >= 0) {
|
|
192
|
+
if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {
|
|
193
|
+
changes.push({
|
|
194
|
+
platform: this.id,
|
|
195
|
+
action: "skip",
|
|
196
|
+
path: hooksPath,
|
|
197
|
+
detail: `hooks.${cursorEvent} already registered`
|
|
198
|
+
});
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
bucket[existingIdx] = entry;
|
|
202
|
+
changes.push({
|
|
203
|
+
platform: this.id,
|
|
204
|
+
action: "update",
|
|
205
|
+
path: hooksPath,
|
|
206
|
+
detail: `hooks.${cursorEvent}`
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
bucket.push(entry);
|
|
210
|
+
changes.push({
|
|
211
|
+
platform: this.id,
|
|
212
|
+
action: "create",
|
|
213
|
+
path: hooksPath,
|
|
214
|
+
detail: `hooks.${cursorEvent}`
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
mutated = true;
|
|
218
|
+
}
|
|
219
|
+
if (mutated) {
|
|
220
|
+
file.version = CURSOR_HOOKS_VERSION;
|
|
221
|
+
this.writeJson(hooksPath, file, ctx.dryRun);
|
|
222
|
+
}
|
|
223
|
+
return changes;
|
|
224
|
+
}
|
|
225
|
+
uninstallHooks(ctx) {
|
|
226
|
+
const hooksPath = this.getHookConfigPath(ctx);
|
|
227
|
+
const file = this.readJson(hooksPath);
|
|
228
|
+
const hooks = file?.hooks;
|
|
229
|
+
if (!file || !hooks) {
|
|
230
|
+
return [
|
|
231
|
+
{
|
|
232
|
+
platform: this.id,
|
|
233
|
+
action: "skip",
|
|
234
|
+
path: hooksPath,
|
|
235
|
+
detail: "no hooks section present"
|
|
236
|
+
}
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
const changes = [];
|
|
240
|
+
let mutated = false;
|
|
241
|
+
for (const cursorEvent of Object.keys(hooks)) {
|
|
242
|
+
const bucket = hooks[cursorEvent];
|
|
243
|
+
if (!Array.isArray(bucket)) continue;
|
|
244
|
+
const before = bucket.length;
|
|
245
|
+
const next = bucket.filter((e) => !this.isOurCommand(e.command, ctx));
|
|
246
|
+
const removed = before - next.length;
|
|
247
|
+
if (removed > 0) {
|
|
248
|
+
if (next.length > 0) hooks[cursorEvent] = next;
|
|
249
|
+
else delete hooks[cursorEvent];
|
|
250
|
+
changes.push({
|
|
251
|
+
platform: this.id,
|
|
252
|
+
action: "remove",
|
|
253
|
+
path: hooksPath,
|
|
254
|
+
detail: `hooks.${cursorEvent} (${removed})`
|
|
255
|
+
});
|
|
256
|
+
mutated = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);
|
|
260
|
+
if (changes.length === 0) {
|
|
261
|
+
changes.push({
|
|
262
|
+
platform: this.id,
|
|
263
|
+
action: "skip",
|
|
264
|
+
path: hooksPath,
|
|
265
|
+
detail: "no matching hook entries"
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return changes;
|
|
269
|
+
}
|
|
270
|
+
/** True when a hook command references our home binary AND this connector id
|
|
271
|
+
* (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */
|
|
272
|
+
isOurCommand(command, ctx) {
|
|
273
|
+
return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);
|
|
274
|
+
}
|
|
275
|
+
// ── Content surfaces: commands / skills / subagents ──────────────────────
|
|
276
|
+
// CONTENT-ONLY: pure native-file writers under <configDir>/{commands,skills,
|
|
277
|
+
// agents}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each
|
|
278
|
+
// method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile
|
|
279
|
+
// and reversible via removeContentFile. Honors platforms["cursor"] per-surface
|
|
280
|
+
// false to skip. Cursor commands are BODY-ONLY markdown (no frontmatter);
|
|
281
|
+
// skills are the uniform <name>/SKILL.md + resources; subagents are md+fm.
|
|
282
|
+
commandsDir(ctx) {
|
|
283
|
+
return join(this.getConfigDir(ctx), "commands");
|
|
284
|
+
}
|
|
285
|
+
skillsDir(ctx) {
|
|
286
|
+
return join(this.getConfigDir(ctx), "skills");
|
|
287
|
+
}
|
|
288
|
+
agentsDir(ctx) {
|
|
289
|
+
return join(this.getConfigDir(ctx), "agents");
|
|
290
|
+
}
|
|
291
|
+
/** Native command file path: <configDir>/commands/<name>.md. */
|
|
292
|
+
commandPath(ctx, name) {
|
|
293
|
+
return join(this.commandsDir(ctx), `${name}.md`);
|
|
294
|
+
}
|
|
295
|
+
/** Native skill dir: <configDir>/skills/<name>. */
|
|
296
|
+
skillDir(ctx, name) {
|
|
297
|
+
return join(this.skillsDir(ctx), name);
|
|
298
|
+
}
|
|
299
|
+
/** Native subagent file path: <configDir>/agents/<name>.md. */
|
|
300
|
+
subagentPath(ctx, name) {
|
|
301
|
+
return join(this.agentsDir(ctx), `${name}.md`);
|
|
302
|
+
}
|
|
303
|
+
// ── Commands ──────────────────────────────────────────────────────────────
|
|
304
|
+
installCommands(ctx) {
|
|
305
|
+
const { connector } = ctx;
|
|
306
|
+
if (connector.platforms[HOST]?.commands === false) {
|
|
307
|
+
return [{ platform: this.id, action: "skip", detail: "commands disabled for cursor" }];
|
|
308
|
+
}
|
|
309
|
+
if (connector.commands.length === 0) {
|
|
310
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
|
|
311
|
+
}
|
|
312
|
+
return connector.commands.map(
|
|
313
|
+
(cmd) => this.writeContentFile(
|
|
314
|
+
this.commandPath(ctx, cmd.name),
|
|
315
|
+
this.renderCommand(cmd),
|
|
316
|
+
ctx.dryRun
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
uninstallCommands(ctx) {
|
|
321
|
+
const { connector } = ctx;
|
|
322
|
+
if (connector.commands.length === 0) {
|
|
323
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
|
|
324
|
+
}
|
|
325
|
+
return connector.commands.map(
|
|
326
|
+
(cmd) => this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun)
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Render a Cursor command: BODY-ONLY markdown (no frontmatter). Cursor reads
|
|
331
|
+
* the file as the command prompt verbatim. We prepend the one-line description
|
|
332
|
+
* as an HTML comment header when present so authoring intent survives the
|
|
333
|
+
* round-trip without leaking into the rendered prompt. A description containing
|
|
334
|
+
* "-->" would prematurely CLOSE the HTML comment and leak the remainder into
|
|
335
|
+
* the prompt, so we neutralize that sequence before embedding it.
|
|
336
|
+
*/
|
|
337
|
+
renderCommand(cmd) {
|
|
338
|
+
if (!cmd.description) return `${cmd.prompt}
|
|
339
|
+
`;
|
|
340
|
+
const safe = cmd.description.replace(/--+>/g, (m) => m.replace(/>/g, ">"));
|
|
341
|
+
return `<!-- ${safe} -->
|
|
342
|
+
|
|
343
|
+
${cmd.prompt}
|
|
344
|
+
`;
|
|
345
|
+
}
|
|
346
|
+
// ── Skills ────────────────────────────────────────────────────────────────
|
|
347
|
+
installSkills(ctx) {
|
|
348
|
+
const { connector } = ctx;
|
|
349
|
+
if (connector.platforms[HOST]?.skills === false) {
|
|
350
|
+
return [{ platform: this.id, action: "skip", detail: "skills disabled for cursor" }];
|
|
351
|
+
}
|
|
352
|
+
if (connector.skills.length === 0) {
|
|
353
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
|
|
354
|
+
}
|
|
355
|
+
const changes = [];
|
|
356
|
+
for (const skill of connector.skills) {
|
|
357
|
+
const dir = this.skillDir(ctx, skill.name);
|
|
358
|
+
changes.push(
|
|
359
|
+
this.writeContentFile(join(dir, "SKILL.md"), this.renderSkill(skill), ctx.dryRun)
|
|
360
|
+
);
|
|
361
|
+
for (const [rel, contents] of Object.entries(skill.resources ?? {})) {
|
|
362
|
+
const target = this.resolveWithin(dir, rel);
|
|
363
|
+
if (target === null) {
|
|
364
|
+
changes.push({
|
|
365
|
+
platform: this.id,
|
|
366
|
+
action: "warn",
|
|
367
|
+
detail: `skill resource "${rel}" escapes the skill dir; skipped`
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
changes.push(this.writeContentFile(target, contents, ctx.dryRun));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return changes;
|
|
375
|
+
}
|
|
376
|
+
uninstallSkills(ctx) {
|
|
377
|
+
const { connector } = ctx;
|
|
378
|
+
if (connector.skills.length === 0) {
|
|
379
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
|
|
380
|
+
}
|
|
381
|
+
const changes = [];
|
|
382
|
+
for (const skill of connector.skills) {
|
|
383
|
+
const dir = this.skillDir(ctx, skill.name);
|
|
384
|
+
changes.push(this.removeContentFile(join(dir, "SKILL.md"), ctx.dryRun));
|
|
385
|
+
for (const rel of Object.keys(skill.resources ?? {})) {
|
|
386
|
+
const target = this.resolveWithin(dir, rel);
|
|
387
|
+
if (target === null) continue;
|
|
388
|
+
changes.push(this.removeContentFile(target, ctx.dryRun));
|
|
389
|
+
}
|
|
390
|
+
changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));
|
|
391
|
+
}
|
|
392
|
+
return changes;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Render a skill's SKILL.md: frontmatter (name, description + optional model,
|
|
396
|
+
* allowed-tools, disable-model-invocation) + body. Uniform across platforms;
|
|
397
|
+
* only the parent dir differs.
|
|
398
|
+
*/
|
|
399
|
+
renderSkill(skill) {
|
|
400
|
+
const frontmatter = {
|
|
401
|
+
name: skill.name,
|
|
402
|
+
description: skill.description
|
|
403
|
+
};
|
|
404
|
+
if (skill.model !== void 0) frontmatter.model = skill.model;
|
|
405
|
+
const allow = skill.tools?.allow;
|
|
406
|
+
if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
|
|
407
|
+
if (skill.disableModelInvocation !== void 0) {
|
|
408
|
+
frontmatter["disable-model-invocation"] = skill.disableModelInvocation;
|
|
409
|
+
}
|
|
410
|
+
if (skill.extra) Object.assign(frontmatter, skill.extra);
|
|
411
|
+
return this.renderFrontmatterMd(frontmatter, skill.body);
|
|
412
|
+
}
|
|
413
|
+
// ── Subagents ───────────────────────────────────────────────────────────────
|
|
414
|
+
installSubagents(ctx) {
|
|
415
|
+
const { connector } = ctx;
|
|
416
|
+
if (connector.platforms[HOST]?.subagents === false) {
|
|
417
|
+
return [{ platform: this.id, action: "skip", detail: "subagents disabled for cursor" }];
|
|
418
|
+
}
|
|
419
|
+
if (connector.subagents.length === 0) {
|
|
420
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
|
|
421
|
+
}
|
|
422
|
+
return connector.subagents.map(
|
|
423
|
+
(agent) => this.writeContentFile(
|
|
424
|
+
this.subagentPath(ctx, agent.name),
|
|
425
|
+
this.renderSubagent(agent),
|
|
426
|
+
ctx.dryRun
|
|
427
|
+
)
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
uninstallSubagents(ctx) {
|
|
431
|
+
const { connector } = ctx;
|
|
432
|
+
if (connector.subagents.length === 0) {
|
|
433
|
+
return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
|
|
434
|
+
}
|
|
435
|
+
return connector.subagents.map(
|
|
436
|
+
(agent) => this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun)
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
/** Render a Cursor subagent to md+frontmatter (name, description, model, readonly) + prompt body. */
|
|
440
|
+
renderSubagent(agent) {
|
|
441
|
+
const frontmatter = {
|
|
442
|
+
name: agent.name,
|
|
443
|
+
description: agent.description
|
|
444
|
+
};
|
|
445
|
+
if (agent.model !== void 0) frontmatter.model = agent.model;
|
|
446
|
+
if (agent.readonly !== void 0) frontmatter.readonly = agent.readonly;
|
|
447
|
+
if (agent.extra) Object.assign(frontmatter, agent.extra);
|
|
448
|
+
return this.renderFrontmatterMd(frontmatter, agent.prompt);
|
|
449
|
+
}
|
|
450
|
+
// ── Diagnostics ──────────────────────────────────────────────────────────
|
|
451
|
+
getHealthChecks(ctx) {
|
|
452
|
+
const mcpPath = this.getServerConfigPath(ctx);
|
|
453
|
+
const hooksPath = this.getHookConfigPath(ctx);
|
|
454
|
+
const checks = [
|
|
455
|
+
{
|
|
456
|
+
name: `${this.name}: mcp.json present`,
|
|
457
|
+
check: () => existsSync(mcpPath) ? { status: "OK", detail: mcpPath } : { status: "FAIL", detail: `not found: ${mcpPath}` }
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: `${this.name}: hooks.json present`,
|
|
461
|
+
check: () => existsSync(hooksPath) ? { status: "OK", detail: hooksPath } : { status: "FAIL", detail: `not found: ${hooksPath}` }
|
|
462
|
+
}
|
|
463
|
+
];
|
|
464
|
+
for (const cmd of ctx.connector.commands) {
|
|
465
|
+
const p = this.commandPath(ctx, cmd.name);
|
|
466
|
+
checks.push({
|
|
467
|
+
name: `${this.name}: command ${cmd.name} present`,
|
|
468
|
+
check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
for (const skill of ctx.connector.skills) {
|
|
472
|
+
const p = join(this.skillDir(ctx, skill.name), "SKILL.md");
|
|
473
|
+
checks.push({
|
|
474
|
+
name: `${this.name}: skill ${skill.name} present`,
|
|
475
|
+
check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
for (const agent of ctx.connector.subagents) {
|
|
479
|
+
const p = this.subagentPath(ctx, agent.name);
|
|
480
|
+
checks.push({
|
|
481
|
+
name: `${this.name}: subagent ${agent.name} present`,
|
|
482
|
+
check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return checks;
|
|
486
|
+
}
|
|
487
|
+
// ── Runtime: parse Cursor stdin JSON → normalized event ──────────────────
|
|
488
|
+
parseEvent(event, raw) {
|
|
489
|
+
const input = raw ?? {};
|
|
490
|
+
const connectorId = typeof input.connector === "string" ? input.connector : "";
|
|
491
|
+
const sessionId = extractSessionId(input);
|
|
492
|
+
const projectDir = this.getProjectDir(input);
|
|
493
|
+
const base = {
|
|
494
|
+
hostPlatform: HOST,
|
|
495
|
+
connectorId,
|
|
496
|
+
sessionId,
|
|
497
|
+
raw,
|
|
498
|
+
...projectDir !== void 0 ? { projectDir } : {}
|
|
499
|
+
};
|
|
500
|
+
switch (event) {
|
|
501
|
+
case "PreToolUse": {
|
|
502
|
+
const ev = {
|
|
503
|
+
...base,
|
|
504
|
+
toolName: input.tool_name ?? "",
|
|
505
|
+
toolInput: input.tool_input ?? {}
|
|
506
|
+
};
|
|
507
|
+
return ev;
|
|
508
|
+
}
|
|
509
|
+
case "PostToolUse": {
|
|
510
|
+
const toolOutput = input.tool_output ?? input.error_message;
|
|
511
|
+
const ev = {
|
|
512
|
+
...base,
|
|
513
|
+
toolName: input.tool_name ?? "",
|
|
514
|
+
toolInput: input.tool_input ?? {},
|
|
515
|
+
...toolOutput !== void 0 ? { toolOutput } : {},
|
|
516
|
+
...input.error_message ? { isError: true } : {}
|
|
517
|
+
};
|
|
518
|
+
return ev;
|
|
519
|
+
}
|
|
520
|
+
case "SessionStart": {
|
|
521
|
+
const ev = {
|
|
522
|
+
...base,
|
|
523
|
+
source: normalizeSessionSource(input.source ?? input.trigger)
|
|
524
|
+
};
|
|
525
|
+
return ev;
|
|
526
|
+
}
|
|
527
|
+
case "Stop": {
|
|
528
|
+
const ev = {
|
|
529
|
+
...base,
|
|
530
|
+
...typeof input.stop_hook_active === "boolean" ? { stopHookActive: input.stop_hook_active } : {}
|
|
531
|
+
};
|
|
532
|
+
return ev;
|
|
533
|
+
}
|
|
534
|
+
default: {
|
|
535
|
+
throw new Error(`unsupported cursor hook event: ${String(event)}`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
getProjectDir(input) {
|
|
540
|
+
return input.cwd ?? input.workspace_roots?.[0] ?? void 0;
|
|
541
|
+
}
|
|
542
|
+
// ── Runtime: normalized response → Cursor native hook reply ──────────────
|
|
543
|
+
formatReply(event, response) {
|
|
544
|
+
const decision = response.decision ?? "allow";
|
|
545
|
+
if (decision === "deny") {
|
|
546
|
+
return this.stdout({
|
|
547
|
+
permission: "deny",
|
|
548
|
+
user_message: response.reason ?? "Blocked by hook"
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
if (decision === "ask") {
|
|
552
|
+
return this.stdout({
|
|
553
|
+
permission: "ask",
|
|
554
|
+
user_message: response.reason ?? "Confirmation required by hook"
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (decision === "modify") {
|
|
558
|
+
if (event === "PreToolUse" && response.updatedInput) {
|
|
559
|
+
return this.stdout({ updated_input: response.updatedInput });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (decision === "context" && response.additionalContext) {
|
|
563
|
+
return event === "PreToolUse" ? this.stdout({ agent_message: response.additionalContext }) : this.stdout({ additional_context: response.additionalContext });
|
|
564
|
+
}
|
|
565
|
+
if (event === "SessionStart") {
|
|
566
|
+
return this.stdout({ additional_context: response.additionalContext ?? "" });
|
|
567
|
+
}
|
|
568
|
+
if (event === "PreToolUse") return this.stdout({ agent_message: "" });
|
|
569
|
+
if (event === "PostToolUse") return this.stdout({ additional_context: "" });
|
|
570
|
+
return { exitCode: 0 };
|
|
571
|
+
}
|
|
572
|
+
stdout(payload) {
|
|
573
|
+
return { exitCode: 0, stdout: JSON.stringify(payload) };
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
function cursorEnvToken(name) {
|
|
577
|
+
return `\${env:${name}}`;
|
|
578
|
+
}
|
|
579
|
+
function extractSessionId(input) {
|
|
580
|
+
if (typeof input.conversation_id === "string") return input.conversation_id;
|
|
581
|
+
if (typeof input.session_id === "string") return input.session_id;
|
|
582
|
+
return "";
|
|
583
|
+
}
|
|
584
|
+
function normalizeSessionSource(source) {
|
|
585
|
+
switch (source) {
|
|
586
|
+
case "compact":
|
|
587
|
+
return "compact";
|
|
588
|
+
case "resume":
|
|
589
|
+
return "resume";
|
|
590
|
+
case "clear":
|
|
591
|
+
return "clear";
|
|
592
|
+
default:
|
|
593
|
+
return "startup";
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
var adapter = new CursorAdapter();
|
|
597
|
+
var cursor_default = adapter;
|
|
598
|
+
export {
|
|
599
|
+
CursorAdapter,
|
|
600
|
+
adapter,
|
|
601
|
+
cursor_default as default
|
|
602
|
+
};
|
|
603
|
+
//# sourceMappingURL=cursor-4TMXKJOZ.js.map
|