@ken-jo/agent-connector 0.4.67 → 0.4.75
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/README.md +43 -6
- package/dist/{action-RUM6GP23.js → action-TCKBTTZX.js} +8 -8
- package/dist/{amazon-q-KXVJFT7Q.js → amazon-q-CB4XFX7T.js} +3 -3
- package/dist/{amp-WAPHJYTK.js → amp-6Q5OYHNW.js} +7 -3
- package/dist/amp-6Q5OYHNW.js.map +1 -0
- package/dist/{antigravity-GF3RU42A.js → antigravity-WIK3XBN5.js} +4 -4
- package/dist/{antigravity-cli-OH27IFH5.js → antigravity-cli-L3RZMF6V.js} +4 -4
- package/dist/{chunk-E45SUUD7.js → chunk-24K6JOKV.js} +3 -3
- package/dist/{chunk-IZ4XQ23W.js → chunk-2L6LTQ6L.js} +41 -6
- package/dist/chunk-2L6LTQ6L.js.map +1 -0
- package/dist/{chunk-RQCBBLTA.js → chunk-5LAFKXFM.js} +2 -2
- package/dist/{chunk-UVBAH263.js → chunk-7ITLLYWB.js} +2 -2
- package/dist/{chunk-DBQMX75U.js → chunk-BEFVJ2OE.js} +28 -2
- package/dist/chunk-BEFVJ2OE.js.map +1 -0
- package/dist/{chunk-ZSMGHEB5.js → chunk-DI6OWKH2.js} +7 -3
- package/dist/chunk-DI6OWKH2.js.map +1 -0
- package/dist/chunk-ECSRTK4M.js +386 -0
- package/dist/chunk-ECSRTK4M.js.map +1 -0
- package/dist/{chunk-F5COBB5F.js → chunk-FUXUEXDP.js} +3 -3
- package/dist/{chunk-NXR4HM5N.js → chunk-HKM3HUOK.js} +19 -19
- package/dist/{chunk-NXR4HM5N.js.map → chunk-HKM3HUOK.js.map} +1 -1
- package/dist/{chunk-P5KA5NVR.js → chunk-MDZG5QHB.js} +49 -3
- package/dist/chunk-MDZG5QHB.js.map +1 -0
- package/dist/{chunk-CSRBMJLU.js → chunk-MHDQWZ4A.js} +5 -5
- package/dist/{chunk-RHML2NOB.js → chunk-NUGMVVV2.js} +2 -2
- package/dist/chunk-NUGMVVV2.js.map +1 -0
- package/dist/{chunk-ZEFUDBTY.js → chunk-OEQJGBBW.js} +4 -4
- package/dist/{chunk-3Y23ZYYF.js → chunk-Q42SEI7W.js} +3 -3
- package/dist/{chunk-LS65GL2P.js → chunk-Q5LECKZL.js} +36 -36
- package/dist/{chunk-GCRRX4MO.js → chunk-TW4DVR4V.js} +2 -2
- package/dist/{claude-code-WLN43K7I.js → claude-code-HFBAAVWS.js} +10 -6
- package/dist/claude-code-HFBAAVWS.js.map +1 -0
- package/dist/cli/sdk.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/{cline-PEGTGVQD.js → cline-26W4FEZ6.js} +3 -3
- package/dist/{codebuff-UG6LVSOM.js → codebuff-J5KEEBIL.js} +7 -3
- package/dist/codebuff-J5KEEBIL.js.map +1 -0
- package/dist/{codex-4LVDQXCD.js → codex-JKQ6TLRI.js} +16 -23
- package/dist/codex-JKQ6TLRI.js.map +1 -0
- package/dist/{continue-DYV5V55Y.js → continue-KQSJUSWX.js} +3 -3
- package/dist/{copilot-cli-NVFXFLUF.js → copilot-cli-YBNKLYUB.js} +65 -22
- package/dist/copilot-cli-YBNKLYUB.js.map +1 -0
- package/dist/{crush-PP4HYZLT.js → crush-JTWTZIPB.js} +20 -10
- package/dist/crush-JTWTZIPB.js.map +1 -0
- package/dist/{cursor-XMXOZKAP.js → cursor-2NIZSDZ4.js} +4 -4
- package/dist/{detect-IZOWP5AA.js → detect-CWEIQCRC.js} +4 -4
- package/dist/{doctor-5IVUQ2YH.js → doctor-AC437ZAW.js} +95 -13
- package/dist/doctor-AC437ZAW.js.map +1 -0
- package/dist/{droid-ETNOUIPJ.js → droid-HF2AIFB2.js} +3 -3
- package/dist/{gemini-cli-P7XJXWVH.js → gemini-cli-2EOK26S2.js} +3 -3
- package/dist/{goose-UZVA6MQC.js → goose-55JOTXS5.js} +3 -3
- package/dist/{hermes-IC6R4CII.js → hermes-QVZOZU4B.js} +3 -3
- package/dist/{hook-7IUPIHCZ.js → hook-VYD3OCLM.js} +8 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{install-EP6GPQ65.js → install-S6BYIJ62.js} +11 -11
- package/dist/{introspect-BFcXpfUp.d.ts → introspect-5o5ick0_.d.ts} +9 -4
- package/dist/{jetbrains-copilot-MZO2RQGJ.js → jetbrains-copilot-DZ2ATXFN.js} +2 -2
- package/dist/{kilo-CCB34CQQ.js → kilo-GJNSBWVV.js} +3 -3
- package/dist/{kilo-cli-EOMN3WC7.js → kilo-cli-2CCB3DBI.js} +3 -3
- package/dist/{kimi-GUSJME55.js → kimi-YRPCSB6N.js} +3 -3
- package/dist/{kiro-6OHZ5RGV.js → kiro-BUOQFASX.js} +3 -3
- package/dist/{leaderboard-6I2E5XZK.js → leaderboard-SUSUDKHR.js} +2 -2
- package/dist/{mimo-code-ZPJJ6IIW.js → mimo-code-V4FP5PUJ.js} +3 -3
- package/dist/{mux-AFTVMGGM.js → mux-CMJSM42O.js} +3 -3
- package/dist/{nemoclaw-D5EN4G2Q.js → nemoclaw-VP56PGKC.js} +4 -4
- package/dist/{omp-GKI2PZ7G.js → omp-OOFGD3GG.js} +3 -3
- package/dist/{openclaw-A76PCNBV.js → openclaw-V73XHPXP.js} +4 -4
- package/dist/{opencode-M34E6PI7.js → opencode-MKLHSQRD.js} +3 -3
- package/dist/{package-BWLV24OO.js → package-KOAAVKHH.js} +9 -9
- package/dist/{pi-HVG6ZWFE.js → pi-NMVL7OFL.js} +2 -2
- package/dist/{qwen-code-OWLM3I5P.js → qwen-code-2XE2T6F4.js} +3 -3
- package/dist/{roo-code-RFKKTNNI.js → roo-code-YJOIKCCS.js} +3 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +6 -6
- package/dist/sdk/index.d.ts +3 -3
- package/dist/sdk/index.js +3 -3
- package/dist/sdk/test.d.ts +33 -3
- package/dist/sdk/test.js +10 -280
- package/dist/sdk/test.js.map +1 -1
- package/dist/{serve-CM4HZPMP.js → serve-BU23QUSJ.js} +8 -8
- package/dist/{status-BPB4VLQX.js → status-N5LCQQ3R.js} +6 -6
- package/dist/{statusline-HIKR2I4Q.js → statusline-442XOF7R.js} +7 -7
- package/dist/{telemetry-SBVUJYNE.js → telemetry-V4YQL2NU.js} +5 -5
- package/dist/{trae-FVHQALK3.js → trae-QSV2JZFT.js} +3 -3
- package/dist/{types-ChUrvw0j.d.ts → types-D6RWGXm1.d.ts} +13 -0
- package/dist/{uninstall-MO3RGGVE.js → uninstall-WGEKTCDZ.js} +11 -11
- package/dist/{upgrade-6XIGSPGY.js → upgrade-IH7CKPTO.js} +11 -11
- package/dist/{usage-IBN36ZVW.js → usage-DI7XRCKM.js} +2 -2
- package/dist/{usage-event-4RJP37K6.js → usage-event-2FCU55AN.js} +7 -7
- package/dist/{vscode-copilot-6FLDRTKV.js → vscode-copilot-R23RTVST.js} +7 -3
- package/dist/vscode-copilot-R23RTVST.js.map +1 -0
- package/dist/{warp-7CC52UJV.js → warp-B72XSQ5G.js} +3 -3
- package/dist/{windsurf-6ZLQMBQW.js → windsurf-642VBMQ2.js} +3 -3
- package/dist/{zed-FULJMNBV.js → zed-6UJS7JKB.js} +3 -3
- package/package.json +1 -1
- package/dist/amp-WAPHJYTK.js.map +0 -1
- package/dist/chunk-DBQMX75U.js.map +0 -1
- package/dist/chunk-IZ4XQ23W.js.map +0 -1
- package/dist/chunk-P5KA5NVR.js.map +0 -1
- package/dist/chunk-RHML2NOB.js.map +0 -1
- package/dist/chunk-ZSMGHEB5.js.map +0 -1
- package/dist/claude-code-WLN43K7I.js.map +0 -1
- package/dist/codebuff-UG6LVSOM.js.map +0 -1
- package/dist/codex-4LVDQXCD.js.map +0 -1
- package/dist/copilot-cli-NVFXFLUF.js.map +0 -1
- package/dist/crush-PP4HYZLT.js.map +0 -1
- package/dist/doctor-5IVUQ2YH.js.map +0 -1
- package/dist/vscode-copilot-6FLDRTKV.js.map +0 -1
- /package/dist/{action-RUM6GP23.js.map → action-TCKBTTZX.js.map} +0 -0
- /package/dist/{amazon-q-KXVJFT7Q.js.map → amazon-q-CB4XFX7T.js.map} +0 -0
- /package/dist/{antigravity-GF3RU42A.js.map → antigravity-WIK3XBN5.js.map} +0 -0
- /package/dist/{antigravity-cli-OH27IFH5.js.map → antigravity-cli-L3RZMF6V.js.map} +0 -0
- /package/dist/{chunk-E45SUUD7.js.map → chunk-24K6JOKV.js.map} +0 -0
- /package/dist/{chunk-RQCBBLTA.js.map → chunk-5LAFKXFM.js.map} +0 -0
- /package/dist/{chunk-UVBAH263.js.map → chunk-7ITLLYWB.js.map} +0 -0
- /package/dist/{chunk-F5COBB5F.js.map → chunk-FUXUEXDP.js.map} +0 -0
- /package/dist/{chunk-CSRBMJLU.js.map → chunk-MHDQWZ4A.js.map} +0 -0
- /package/dist/{chunk-ZEFUDBTY.js.map → chunk-OEQJGBBW.js.map} +0 -0
- /package/dist/{chunk-3Y23ZYYF.js.map → chunk-Q42SEI7W.js.map} +0 -0
- /package/dist/{chunk-LS65GL2P.js.map → chunk-Q5LECKZL.js.map} +0 -0
- /package/dist/{chunk-GCRRX4MO.js.map → chunk-TW4DVR4V.js.map} +0 -0
- /package/dist/{cline-PEGTGVQD.js.map → cline-26W4FEZ6.js.map} +0 -0
- /package/dist/{continue-DYV5V55Y.js.map → continue-KQSJUSWX.js.map} +0 -0
- /package/dist/{cursor-XMXOZKAP.js.map → cursor-2NIZSDZ4.js.map} +0 -0
- /package/dist/{detect-IZOWP5AA.js.map → detect-CWEIQCRC.js.map} +0 -0
- /package/dist/{droid-ETNOUIPJ.js.map → droid-HF2AIFB2.js.map} +0 -0
- /package/dist/{gemini-cli-P7XJXWVH.js.map → gemini-cli-2EOK26S2.js.map} +0 -0
- /package/dist/{goose-UZVA6MQC.js.map → goose-55JOTXS5.js.map} +0 -0
- /package/dist/{hermes-IC6R4CII.js.map → hermes-QVZOZU4B.js.map} +0 -0
- /package/dist/{hook-7IUPIHCZ.js.map → hook-VYD3OCLM.js.map} +0 -0
- /package/dist/{install-EP6GPQ65.js.map → install-S6BYIJ62.js.map} +0 -0
- /package/dist/{jetbrains-copilot-MZO2RQGJ.js.map → jetbrains-copilot-DZ2ATXFN.js.map} +0 -0
- /package/dist/{kilo-CCB34CQQ.js.map → kilo-GJNSBWVV.js.map} +0 -0
- /package/dist/{kilo-cli-EOMN3WC7.js.map → kilo-cli-2CCB3DBI.js.map} +0 -0
- /package/dist/{kimi-GUSJME55.js.map → kimi-YRPCSB6N.js.map} +0 -0
- /package/dist/{kiro-6OHZ5RGV.js.map → kiro-BUOQFASX.js.map} +0 -0
- /package/dist/{leaderboard-6I2E5XZK.js.map → leaderboard-SUSUDKHR.js.map} +0 -0
- /package/dist/{mimo-code-ZPJJ6IIW.js.map → mimo-code-V4FP5PUJ.js.map} +0 -0
- /package/dist/{mux-AFTVMGGM.js.map → mux-CMJSM42O.js.map} +0 -0
- /package/dist/{nemoclaw-D5EN4G2Q.js.map → nemoclaw-VP56PGKC.js.map} +0 -0
- /package/dist/{omp-GKI2PZ7G.js.map → omp-OOFGD3GG.js.map} +0 -0
- /package/dist/{openclaw-A76PCNBV.js.map → openclaw-V73XHPXP.js.map} +0 -0
- /package/dist/{opencode-M34E6PI7.js.map → opencode-MKLHSQRD.js.map} +0 -0
- /package/dist/{package-BWLV24OO.js.map → package-KOAAVKHH.js.map} +0 -0
- /package/dist/{pi-HVG6ZWFE.js.map → pi-NMVL7OFL.js.map} +0 -0
- /package/dist/{qwen-code-OWLM3I5P.js.map → qwen-code-2XE2T6F4.js.map} +0 -0
- /package/dist/{roo-code-RFKKTNNI.js.map → roo-code-YJOIKCCS.js.map} +0 -0
- /package/dist/{serve-CM4HZPMP.js.map → serve-BU23QUSJ.js.map} +0 -0
- /package/dist/{status-BPB4VLQX.js.map → status-N5LCQQ3R.js.map} +0 -0
- /package/dist/{statusline-HIKR2I4Q.js.map → statusline-442XOF7R.js.map} +0 -0
- /package/dist/{telemetry-SBVUJYNE.js.map → telemetry-V4YQL2NU.js.map} +0 -0
- /package/dist/{trae-FVHQALK3.js.map → trae-QSV2JZFT.js.map} +0 -0
- /package/dist/{uninstall-MO3RGGVE.js.map → uninstall-WGEKTCDZ.js.map} +0 -0
- /package/dist/{upgrade-6XIGSPGY.js.map → upgrade-IH7CKPTO.js.map} +0 -0
- /package/dist/{usage-IBN36ZVW.js.map → usage-DI7XRCKM.js.map} +0 -0
- /package/dist/{usage-event-4RJP37K6.js.map → usage-event-2FCU55AN.js.map} +0 -0
- /package/dist/{warp-7CC52UJV.js.map → warp-B72XSQ5G.js.map} +0 -0
- /package/dist/{windsurf-6ZLQMBQW.js.map → windsurf-642VBMQ2.js.map} +0 -0
- /package/dist/{zed-FULJMNBV.js.map → zed-6UJS7JKB.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
defineConnector
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7ITLLYWB.js";
|
|
5
5
|
import {
|
|
6
6
|
assertNoSymlinkInPath,
|
|
7
7
|
connectorDir,
|
|
@@ -181,4 +181,4 @@ export {
|
|
|
181
181
|
connectorFromMeta,
|
|
182
182
|
listRegisteredConnectors
|
|
183
183
|
};
|
|
184
|
-
//# sourceMappingURL=chunk-
|
|
184
|
+
//# sourceMappingURL=chunk-5LAFKXFM.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
REGISTERED_PLATFORM_IDS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Q5LECKZL.js";
|
|
5
5
|
import {
|
|
6
6
|
CONFIG_PATCH_SEGMENT_RE,
|
|
7
7
|
configPatchNamespaceViolation,
|
|
@@ -498,4 +498,4 @@ export {
|
|
|
498
498
|
defineConfigPatch,
|
|
499
499
|
defineNativeHook
|
|
500
500
|
};
|
|
501
|
-
//# sourceMappingURL=chunk-
|
|
501
|
+
//# sourceMappingURL=chunk-7ITLLYWB.js.map
|
|
@@ -27,10 +27,36 @@ function rewriteEnvRefs(input, render) {
|
|
|
27
27
|
(_m, name, def) => render(name, def)
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
|
+
function findUnsetEnvRefs(value, env = process.env) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
const order = [];
|
|
33
|
+
const scanString = (input) => {
|
|
34
|
+
const re = new RegExp(ENV_REF_RE.source, "g");
|
|
35
|
+
for (const m of input.matchAll(re)) {
|
|
36
|
+
const name = m[1];
|
|
37
|
+
const def = m[2];
|
|
38
|
+
const v = env[name];
|
|
39
|
+
if (v != null && v !== "") continue;
|
|
40
|
+
if (def !== void 0) continue;
|
|
41
|
+
if (!seen.has(name)) {
|
|
42
|
+
seen.add(name);
|
|
43
|
+
order.push(name);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const walk = (v) => {
|
|
48
|
+
if (typeof v === "string") scanString(v);
|
|
49
|
+
else if (Array.isArray(v)) v.forEach(walk);
|
|
50
|
+
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
51
|
+
};
|
|
52
|
+
walk(value);
|
|
53
|
+
return order;
|
|
54
|
+
}
|
|
30
55
|
|
|
31
56
|
export {
|
|
32
57
|
resolveEnvRefs,
|
|
33
58
|
resolveEnvRefsDeep,
|
|
34
|
-
rewriteEnvRefs
|
|
59
|
+
rewriteEnvRefs,
|
|
60
|
+
findUnsetEnvRefs
|
|
35
61
|
};
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
62
|
+
//# sourceMappingURL=chunk-BEFVJ2OE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/interpolate.ts"],"sourcesContent":["/**\n * core/interpolate — universal ${env:VAR} interpolation.\n *\n * The developer writes one portable syntax in their connector config:\n * \"${env:ACME_KEY}\" → value of process.env.ACME_KEY\n * \"${env:ACME_URL:-https://x}\" → value, or the default after \":-\" if unset/empty\n *\n * Two consumers:\n * • resolveEnvRefs / resolveEnvRefsDeep — resolve to literals at INSTALL time,\n * for hosts with NO native interpolation (e.g. Codex TOML).\n * • rewriteEnvRefs — translate to a host's NATIVE interpolation token (e.g.\n * Cursor/VS Code \"${env:VAR}\", Claude \"${VAR}\") so secrets are never baked\n * into config files. Adapters choose which to use.\n */\n\n/** Matches ${env:NAME} and ${env:NAME:-default}. NAME is [A-Za-z_][A-Za-z0-9_]*. */\nexport const ENV_REF_RE = /\\$\\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\\}/g;\n\n/** Resolve every ${env:VAR} in `input` to a literal from `env`. */\nexport function resolveEnvRefs(\n input: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n return input.replace(ENV_REF_RE, (_m, name: string, def?: string) => {\n const v = env[name];\n if (v != null && v !== \"\") return v;\n return def ?? \"\";\n });\n}\n\n/** Recursively resolve ${env:VAR} in all strings of a JSON-ish value. */\nexport function resolveEnvRefsDeep<T>(value: T, env: NodeJS.ProcessEnv = process.env): T {\n if (typeof value === \"string\") return resolveEnvRefs(value, env) as unknown as T;\n if (Array.isArray(value)) {\n return value.map((v) => resolveEnvRefsDeep(v, env)) as unknown as T;\n }\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) out[k] = resolveEnvRefsDeep(v, env);\n return out as T;\n }\n return value;\n}\n\n/**\n * Translate every ${env:VAR(:-default)} via `render` into a host-native token.\n * The renderer receives the var name and optional default and returns the\n * replacement string (e.g. `(name) => \"${\" + name + \"}\"` for Claude).\n */\nexport function rewriteEnvRefs(\n input: string,\n render: (name: string, def?: string) => string,\n): string {\n return input.replace(ENV_REF_RE, (_m, name: string, def?: string) =>\n render(name, def),\n );\n}\n\n/** True if the string contains at least one ${env:...} reference. */\nexport function hasEnvRef(input: string): boolean {\n ENV_REF_RE.lastIndex = 0;\n return ENV_REF_RE.test(input);\n}\n\n/**\n * Names of every ${env:VAR} reference in a JSON-ish value that\n * {@link resolveEnvRefsDeep} would bake to an EMPTY literal — i.e. the var is\n * unset/empty in `env` AND the reference carries no `:-default`. These are the\n * silent-empty-secret bakes on hosts WITHOUT native interpolation: the install\n * succeeds but writes `\"\"` where a secret was meant, surfacing only as a\n * runtime auth failure inside the MCP server. Returns deduped names in\n * first-seen order; uses the SAME unset/default semantics as resolveEnvRefs so\n * the two can never disagree. A ref WITH a `:-default` is intentional and never\n * reported (even when the default itself is empty — the dev opted into \"\").\n */\nexport function findUnsetEnvRefs<T>(\n value: T,\n env: NodeJS.ProcessEnv = process.env,\n): string[] {\n const seen = new Set<string>();\n const order: string[] = [];\n const scanString = (input: string): void => {\n // A FRESH regex per scan — the shared ENV_REF_RE carries /g lastIndex state\n // (hasEnvRef / matchAll would otherwise start mid-string and miss matches).\n const re = new RegExp(ENV_REF_RE.source, \"g\");\n for (const m of input.matchAll(re)) {\n const name = m[1] as string;\n const def = m[2] as string | undefined;\n // Mirrors resolveEnvRefs: a present, non-empty value resolves to itself;\n // otherwise the default is used (no bake of \"\"). Only an unset/empty var\n // WITHOUT a default bakes \"\" — the case we report.\n const v = env[name];\n if (v != null && v !== \"\") continue;\n if (def !== undefined) continue;\n if (!seen.has(name)) {\n seen.add(name);\n order.push(name);\n }\n }\n };\n const walk = (v: unknown): void => {\n if (typeof v === \"string\") scanString(v);\n else if (Array.isArray(v)) v.forEach(walk);\n else if (v && typeof v === \"object\") Object.values(v).forEach(walk);\n };\n walk(value);\n return order;\n}\n"],"mappings":";;;AAgBO,IAAM,aAAa;AAGnB,SAAS,eACd,OACA,MAAyB,QAAQ,KACzB;AACR,SAAO,MAAM,QAAQ,YAAY,CAAC,IAAI,MAAc,QAAiB;AACnE,UAAM,IAAI,IAAI,IAAI;AAClB,QAAI,KAAK,QAAQ,MAAM,GAAI,QAAO;AAClC,WAAO,OAAO;AAAA,EAChB,CAAC;AACH;AAGO,SAAS,mBAAsB,OAAU,MAAyB,QAAQ,KAAQ;AACvF,MAAI,OAAO,UAAU,SAAU,QAAO,eAAe,OAAO,GAAG;AAC/D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAAA,EACpD;AACA,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAG,KAAI,CAAC,IAAI,mBAAmB,GAAG,GAAG;AAC9E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,eACd,OACA,QACQ;AACR,SAAO,MAAM;AAAA,IAAQ;AAAA,IAAY,CAAC,IAAI,MAAc,QAClD,OAAO,MAAM,GAAG;AAAA,EAClB;AACF;AAmBO,SAAS,iBACd,OACA,MAAyB,QAAQ,KACvB;AACV,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,aAAa,CAAC,UAAwB;AAG1C,UAAM,KAAK,IAAI,OAAO,WAAW,QAAQ,GAAG;AAC5C,eAAW,KAAK,MAAM,SAAS,EAAE,GAAG;AAClC,YAAM,OAAO,EAAE,CAAC;AAChB,YAAM,MAAM,EAAE,CAAC;AAIf,YAAM,IAAI,IAAI,IAAI;AAClB,UAAI,KAAK,QAAQ,MAAM,GAAI;AAC3B,UAAI,QAAQ,OAAW;AACvB,UAAI,CAAC,KAAK,IAAI,IAAI,GAAG;AACnB,aAAK,IAAI,IAAI;AACb,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,CAAC,MAAqB;AACjC,QAAI,OAAO,MAAM,SAAU,YAAW,CAAC;AAAA,aAC9B,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ,IAAI;AAAA,aAChC,KAAK,OAAO,MAAM,SAAU,QAAO,OAAO,CAAC,EAAE,QAAQ,IAAI;AAAA,EACpE;AACA,OAAK,KAAK;AACV,SAAO;AACT;","names":[]}
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-H53QZQJI.js";
|
|
5
5
|
import {
|
|
6
6
|
rewriteEnvRefs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BEFVJ2OE.js";
|
|
8
8
|
import {
|
|
9
9
|
BaseAdapter,
|
|
10
10
|
renderSkillMd
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NUGMVVV2.js";
|
|
12
12
|
import {
|
|
13
13
|
buildWrappedStdio,
|
|
14
14
|
isHomeBinHookCommand
|
|
@@ -80,6 +80,10 @@ var CursorAdapter = class extends BaseAdapter {
|
|
|
80
80
|
canModifyOutput: false,
|
|
81
81
|
canInjectSessionContext: true,
|
|
82
82
|
transports: ["stdio", "http"],
|
|
83
|
+
// Server env/url use Cursor's NATIVE ${env:VAR} interpolation (rewriteEnvRefs)
|
|
84
|
+
// — the token survives into mcp.json, never a baked literal — so the
|
|
85
|
+
// installer's unset-env-ref bake warn does not apply here.
|
|
86
|
+
nativeServerEnvInterpolation: true,
|
|
83
87
|
// Content surfaces: Cursor supports all three (commands, skills, subagents).
|
|
84
88
|
supportsCommands: true,
|
|
85
89
|
supportsSkills: true,
|
|
@@ -646,4 +650,4 @@ export {
|
|
|
646
650
|
adapter,
|
|
647
651
|
cursor_default
|
|
648
652
|
};
|
|
649
|
-
//# sourceMappingURL=chunk-
|
|
653
|
+
//# sourceMappingURL=chunk-DI6OWKH2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/cursor/index.ts"],"sourcesContent":["/**\n * adapters/cursor — Cursor platform adapter for agent-connector.\n *\n * Generalized from context-mode's proven Cursor adapter: the served identity is\n * now `ctx.connector` (not a hardcoded \"context-mode\"), and every hook command\n * points at the single stable home binary (the shared hook-merge engine builds\n * the command) so one framework update propagates everywhere.\n *\n * Cursor is a json-stdio host:\n * - MCP servers: user scope → ~/.cursor/mcp.json (\"mcpServers\"); project scope\n * → <projectDir>/.cursor/mcp.json (\"mcpServers\").\n * - Hooks: <configDir>/hooks.json with shape `{ version, hooks: { <cursorEvent>:\n * [ { command, matcher? } ] } }`. Unlike Claude, each entry is a FLAT command\n * object (no `{ matcher, hooks:[...] }` wrapper).\n * - Reply: a JSON object on stdout (exit 0). deny/ask carry `permission` +\n * `user_message`; modify carries `updated_input`; context injects via\n * `agent_message` (PreToolUse) or `additional_context` (Post/SessionStart).\n *\n * Cursor supports its own `${env:VAR}` native interpolation, so env/header/url\n * values are rewritten to that native token rather than baked into the file.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { BaseAdapter, type HookMergeDescriptor } from \"../base.js\";\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\nimport type {\n ChangeRecord,\n CommandDef,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PostToolUseFailureEvent,\n PreCompactEvent,\n PreToolUseEvent,\n ServerDef,\n SessionEndEvent,\n SessionStartEvent,\n StopEvent,\n SubagentDef,\n SubagentStartEvent,\n SubagentStopEvent,\n Transport,\n UserPromptSubmitEvent,\n} from \"../../core/types.js\";\nimport { rewriteEnvRefs } from \"../../core/interpolate.js\";\nimport {\n buildWrappedStdio,\n isHomeBinHookCommand,\n} from \"../../core/spawn.js\";\nimport { renderSkillMd } from \"../claude-code/render.js\";\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\n\nconst HOST: PlatformId = \"cursor\";\nconst MCP_ROOT_KEY = \"mcpServers\";\n\n/**\n * Native hooks.json version Cursor expects. Exported as the SINGLE source of\n * truth for Cursor's hooks-file version, shared with the `cursor-plugin`\n * package emitter so the live install + the packaged bundle can never stamp\n * different versions (see core/package-formats/cursor.ts).\n */\nexport const CURSOR_HOOKS_VERSION = 1;\n\n/**\n * Cursor-native hook event names, in the lower-camel form Cursor reads from\n * hooks.json. The first four are the events the proven context-mode adapter\n * registers; postToolUseFailure / subagentStart / subagentStop are Cursor's\n * documented \"Subagent (Task tool) lifecycle\" + tool-failure hooks.\n */\nconst CURSOR_EVENT = {\n PreToolUse: \"preToolUse\",\n PostToolUse: \"postToolUse\",\n SessionStart: \"sessionStart\",\n Stop: \"stop\",\n PostToolUseFailure: \"postToolUseFailure\",\n SubagentStart: \"subagentStart\",\n SubagentStop: \"subagentStop\",\n // Lifecycle/prompt events Cursor 1.7+ documents (cursor.com/docs/hooks):\n // sessionEnd is fire-and-forget; preCompact is observational (cannot block);\n // beforeSubmitPrompt fires before a prompt is sent and CAN prevent submission\n // (Cursor itself matches beforeSubmitPrompt against the value \"UserPromptSubmit\").\n SessionEnd: \"sessionEnd\",\n PreCompact: \"preCompact\",\n UserPromptSubmit: \"beforeSubmitPrompt\",\n} as const;\n\n/**\n * Map our normalized event names to Cursor's native hook event names. Only the\n * events Cursor actually supports are present; everything else has no Cursor\n * equivalent and is reported as a skip/warn at install time.\n *\n * PermissionRequest is deliberately ABSENT: Cursor has no permission-dialog\n * event — its permission gate is the OUTPUT field `permission: \"allow\"|\"deny\"|\n * \"ask\"` of the before* hooks (beforeShellExecution / beforeMCPExecution /\n * beforeReadFile / preToolUse), not an observable event. Install reports the\n * standard skip-warn for it.\n *\n * Exported as the SINGLE source of truth for canonical→Cursor-native event\n * mapping, shared with the `cursor-plugin` package emitter (see\n * core/package-formats/cursor.ts) so the packaged bundle's hooks.json keys can\n * never silently diverge from the live install adapter's.\n */\nexport const CURSOR_EVENT_MAP: Partial<Record<HookEventName, string>> = {\n PreToolUse: CURSOR_EVENT.PreToolUse,\n PostToolUse: CURSOR_EVENT.PostToolUse,\n SessionStart: CURSOR_EVENT.SessionStart,\n Stop: CURSOR_EVENT.Stop,\n PostToolUseFailure: CURSOR_EVENT.PostToolUseFailure,\n SubagentStart: CURSOR_EVENT.SubagentStart,\n SubagentStop: CURSOR_EVENT.SubagentStop,\n SessionEnd: CURSOR_EVENT.SessionEnd,\n PreCompact: CURSOR_EVENT.PreCompact,\n UserPromptSubmit: CURSOR_EVENT.UserPromptSubmit,\n};\n\n/** A single Cursor native hook entry — a flat command object. */\ninterface CursorHookEntry {\n command: string;\n matcher?: string;\n}\n\n/** Native MCP server entry shapes Cursor accepts under `mcpServers`. */\ninterface CursorStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n}\ninterface CursorHttpServer {\n url: string;\n headers?: Record<string, string>;\n}\n\n/** Raw Cursor hook stdin payload (snake_case wire fields). */\ninterface CursorWireInput {\n connector?: unknown;\n tool_name?: string;\n tool_input?: Record<string, unknown>;\n tool_output?: string;\n error_message?: string;\n cwd?: string;\n workspace_roots?: string[];\n conversation_id?: string;\n session_id?: string;\n generation_id?: string;\n trigger?: string;\n status?: string;\n loop_count?: number;\n stop_hook_active?: boolean;\n\n // sessionStart — Cursor's stdin is EXACTLY { session_id, is_background_agent,\n // composer_mode } (cursor.com/docs/hooks ~1278-1297): there is NO source/trigger\n // start-source discriminator. composer_mode is the editor MODE (agent|ask|edit),\n // NOT our normalized SessionStart.source enum (startup|compact|resume|clear), so\n // it does not map; surfaced here only so the host shape is documented (no\n // normalized field exists for either — see core/types.ts SessionStartEvent).\n is_background_agent?: boolean;\n composer_mode?: string;\n\n // beforeSubmitPrompt (UserPromptSubmit) — the submitted prompt text.\n // sessionEnd — the documented end reason. Both field names are taken\n // verbatim from cursor.com/docs/hooks (snake_case wire).\n prompt?: string;\n reason?: string;\n\n // postToolUseFailure — Cursor's existing error vocabulary is error_message;\n // the Claude-compatible names are accepted defensively (unverified wire).\n error?: string;\n tool_use_id?: string;\n is_interrupt?: boolean;\n // postToolUse / postToolUseFailure carry a BARE `duration` (ms), not\n // `duration_ms` (cursor.com/docs/hooks ~870,883,904,918). `duration_ms` is the\n // distinct field on sessionEnd (~1315) and subagentStop (~973,994), kept below.\n duration?: number;\n duration_ms?: number;\n\n // subagentStart / subagentStop (Task-tool lifecycle). Both name families are\n // parsed defensively: agent_* (Claude-compatible) and subagent_* (Cursor-ish).\n agent_id?: string;\n agent_type?: string;\n subagent_id?: string;\n subagent_type?: string;\n // subagentStop carries the subagent's output text under `summary`, NOT\n // last_assistant_message (cursor.com/docs/hooks ~972,993). This is read into\n // the normalized lastAssistantMessage.\n summary?: string;\n}\n\nexport class CursorAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n readonly name = \"Cursor\";\n readonly paradigm: HookParadigm = \"json-stdio\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\n supportsMemory: true,\n // Cursor natively supports pre/post tool-use, session start/end, prompt\n // submission, context compaction, and stop (cursor.com/docs/hooks, v1.7+).\n preToolUse: true,\n postToolUse: true,\n preCompact: true,\n sessionStart: true,\n sessionEnd: true,\n userPromptSubmit: true,\n stop: true,\n notification: false,\n // Newer events: Cursor has dedicated postToolUseFailure + subagentStart/\n // subagentStop hooks. permissionRequest stays unset — Cursor's permission\n // gate is an OUTPUT field of its before* hooks, not an observable event\n // (see the EVENT_MAP note).\n postToolUseFailure: true,\n subagentStart: true,\n subagentStop: true,\n // Cursor's preToolUse can rewrite tool input (updated_input) but cannot\n // rewrite already-emitted tool output.\n canModifyArgs: true,\n canModifyOutput: false,\n canInjectSessionContext: true,\n transports: [\"stdio\", \"http\"],\n // Server env/url use Cursor's NATIVE ${env:VAR} interpolation (rewriteEnvRefs)\n // — the token survives into mcp.json, never a baked literal — so the\n // installer's unset-env-ref bake warn does not apply here.\n nativeServerEnvInterpolation: true,\n // Content surfaces: Cursor supports all three (commands, skills, subagents).\n supportsCommands: true,\n supportsSkills: true,\n supportsSubagents: true,\n // Native passthrough hooks: Cursor documents many granular hooks with NO\n // canonical analog — beforeShellExecution / afterShellExecution /\n // beforeMCPExecution / afterMCPExecution / beforeReadFile / afterFileEdit /\n // beforeTabFileRead / afterTabFileEdit / workspaceOpen / afterAgentResponse /\n // afterAgentThought (cursor.com/docs/hooks). They are\n // host-specific (below the >=3-host core bar; see\n // docs/research/host-specific-hook-events-design.md). A connector reaches them\n // by declaring platforms[\"cursor\"].nativeHooks; installHooks writes those\n // event-name keys verbatim into hooks.json, and the generic uninstall reverses\n // them.\n supportsNativeHooks: true,\n // Cursor has a command-driven status line (`/statusline` → `statusLine` in\n // ~/.cursor/cli-config.json, GLOBAL scope only) per the Cursor changelog, but\n // the config-key shape + the Claude-parity stdin payload are sourced from a\n // community tool (agent-status-pills), not first-party Cursor docs — payload\n // field names unverified. Deferred pending a live cursor-agent capture; not a\n // permanent gap; user-scope only when wired.\n };\n\n // ── Detection ────────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userDir = join(homedir(), \".cursor\");\n const userMcp = join(userDir, \"mcp.json\");\n const userHooks = join(userDir, \"hooks.json\");\n const projectDirCursor = join(projectDir, \".cursor\");\n const installed =\n existsSync(userDir) ||\n existsSync(userMcp) ||\n existsSync(userHooks) ||\n existsSync(projectDirCursor);\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath: userMcp,\n scope: \"user\",\n reason: installed\n ? `found Cursor config under ${userDir}`\n : `no Cursor config at ${userDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ─────────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \".cursor\")\n : join(homedir(), \".cursor\");\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"mcp.json\");\n }\n\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"hooks.json\");\n }\n\n // ── MCP server install / uninstall ───────────────────────────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n const override = connector.platforms[HOST]?.server;\n if (!connector.server || override === false) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: connector.server\n ? \"server registration disabled for cursor\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n // Shallow-merge any per-platform server override into the base ServerDef.\n const server: ServerDef =\n override && typeof override === \"object\"\n ? { ...connector.server, ...override }\n : connector.server;\n\n const serverPath = this.getServerConfigPath(ctx);\n const entry = this.renderServerEntry(ctx, server);\n\n return [\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\n ];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\n ];\n }\n\n /** Render a normalized ServerDef into Cursor's native mcpServers entry. */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): CursorStdioServer | CursorHttpServer {\n const transport: Transport = server.transport;\n\n if (transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\n\n const entry: CursorStdioServer = { command: this.rewrite(command) };\n if (args.length > 0) entry.args = args.map((a) => this.rewrite(a));\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n if (server.cwd) entry.cwd = this.rewrite(server.cwd);\n return entry;\n }\n\n // http (and any other remote transport we surface) — Cursor registers a URL.\n const entry: CursorHttpServer = { url: this.rewrite(server.url ?? \"\") };\n const headers = this.renderEnv(server.headers);\n if (headers) entry.headers = headers;\n return entry;\n }\n\n /**\n * Render env/header values. Cursor supports its own `${env:VAR}` native\n * interpolation, so translate `${env:VAR}` refs to that native token rather\n * than baking secrets into the file. Literals pass through unchanged.\n */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(env)) out[k] = this.rewrite(v);\n return out;\n }\n\n /** Translate `${env:VAR(:-default)}` to Cursor's native `${env:VAR}` token. */\n private rewrite(value: string): string {\n return rewriteEnvRefs(value, cursorEnvToken);\n }\n\n // ── Hook install / uninstall ─────────────────────────────────────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n const override = connector.platforms[HOST];\n\n // `hooks: false` disables only the NORMALIZED events. nativeHooks is a\n // sibling, cursor-scoped declaration on the same override and installs\n // regardless.\n const normalizedEvents = override?.hooks === false ? [] : connector.hookEvents;\n const nativeHooks = override?.nativeHooks ?? {};\n const nativeEvents = Object.keys(nativeHooks);\n\n if (normalizedEvents.length === 0 && nativeEvents.length === 0) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n detail: override?.hooks === false ? \"hooks disabled for cursor\" : \"connector declares no hooks\",\n },\n ];\n }\n\n // Normalized events go through the engine's mapEvent/warn path; native\n // event-name keys ride the separate `native` pass (verbatim keys, EXACT\n // command, the `(native)` detail) — see the descriptor + base.upsertHookEntries.\n const pending = normalizedEvents.map((event) => ({\n event,\n matcher: connector.hooks[event]?.matcher ?? \"\",\n }));\n const native = nativeEvents.map((event) => ({\n event,\n matcher: nativeHooks[event]?.matcher ?? \"\",\n }));\n return this.upsertHookEntries(\n ctx,\n this.getHookConfigPath(ctx),\n pending,\n this.hookDescriptor(ctx),\n native,\n );\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n return this.removeHookEntries(ctx, this.getHookConfigPath(ctx), this.hookDescriptor(ctx));\n }\n\n /**\n * Cursor's hook-merge descriptor (FLAT shape `{ command, matcher? }`):\n * - mapEvent renames canonical → Cursor's lower-camel native key\n * (CURSOR_EVENT_MAP); an unmapped event warn-skips (no Cursor equivalent).\n * - renderEntry omits the `matcher` key when the matcher is falsy — this\n * `{command}` vs `{command,matcher}` distinction is load-bearing on-disk\n * bytes (Cursor's flat entry shape), reproduced exactly here.\n * - install find is connector-generic (ANY of our commands at the flat\n * `entry.command`); uninstall drops whole owned entries (flat — NO\n * stripInner). The version stamp is install-only via onMutate.\n * - the native pass uses EXACT-command ownership so a native key coinciding\n * with a normalized event's mapped key adds a SECOND distinct command\n * rather than clobbering the normalized entry.\n */\n private hookDescriptor(ctx: InstallContext): HookMergeDescriptor<CursorHookEntry> {\n return {\n mapEvent: (e) => CURSOR_EVENT_MAP[e as HookEventName],\n unmappedWarnDetail: (e) => `${e} has no Cursor hook equivalent — skipped`,\n renderEntry: (_event, matcher, command) =>\n matcher ? { command, matcher } : { command },\n entryOwnsCommand: (entry, _command) => this.isOurCommand(entry.command, ctx),\n ownsEntryForRemove: (c) => (entry) => this.isOurCommand(entry.command, c),\n skipDetail: (e) => `hooks.${e} already registered`,\n removeDetail: (e, n) => `hooks.${e} (${n})`,\n absentDetail: \"no hooks section present\",\n noMatchDetail: \"no matching hook entries\",\n onMutate: (file) => {\n (file as { version?: number }).version = CURSOR_HOOKS_VERSION;\n },\n nativeOwnsCommand: (entry, command) => entry.command === command,\n nativeSkipDetail: (e) => `hooks.${e} (native) already registered`,\n nativeMutateDetail: (e) => `hooks.${e} (native)`,\n };\n }\n\n /** True when a hook command references our home binary AND this connector id\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\n }\n\n // ── Content surfaces: commands / skills / subagents ──────────────────────\n // CONTENT-ONLY: pure native-file writers under <configDir>/{commands,skills,\n // agents}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\n // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile\n // and reversible via removeContentFile. Honors platforms[\"cursor\"] per-surface\n // false to skip. Cursor commands are BODY-ONLY markdown (no frontmatter);\n // skills are the uniform <name>/SKILL.md + resources; subagents are md+fm.\n\n private commandsDir(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"commands\");\n }\n private skillsDir(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"skills\");\n }\n private agentsDir(ctx: InstallContext): string {\n return join(this.getConfigDir(ctx), \"agents\");\n }\n\n /** Native command file path: <configDir>/commands/<name>.md. */\n private commandPath(ctx: InstallContext, name: string): string {\n return join(this.commandsDir(ctx), `${name}.md`);\n }\n /** Native skill dir: <configDir>/skills/<name>. */\n private skillDir(ctx: InstallContext, name: string): string {\n return join(this.skillsDir(ctx), name);\n }\n /** Native subagent file path: <configDir>/agents/<name>.md. */\n private subagentPath(ctx: InstallContext, name: string): string {\n return join(this.agentsDir(ctx), `${name}.md`);\n }\n\n // ── Commands ──────────────────────────────────────────────────────────────\n\n override installCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.commands === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"commands disabled for cursor\" }];\n }\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.writeContentFile(\n this.commandPath(ctx, cmd.name),\n this.renderCommand(cmd),\n ctx.dryRun,\n ),\n );\n }\n\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.commands.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\n }\n return connector.commands.map((cmd) =>\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\n );\n }\n\n /**\n * Render a Cursor command: BODY-ONLY markdown (no frontmatter). Cursor reads\n * the file as the command prompt verbatim. We prepend the one-line description\n * as an HTML comment header when present so authoring intent survives the\n * round-trip without leaking into the rendered prompt. A description containing\n * \"-->\" would prematurely CLOSE the HTML comment and leak the remainder into\n * the prompt, so we neutralize that sequence before embedding it.\n */\n private renderCommand(cmd: CommandDef): string {\n if (!cmd.description) return `${cmd.prompt}\\n`;\n // Break any literal comment-close so it cannot terminate our header early.\n const safe = cmd.description.replace(/--+>/g, (m) => m.replace(/>/g, \">\"));\n return `<!-- ${safe} -->\\n\\n${cmd.prompt}\\n`;\n }\n\n // ── Skills ────────────────────────────────────────────────────────────────\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for cursor\" }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\n // (config-time validation already rejects these, but never trust input).\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n // ── Subagents ───────────────────────────────────────────────────────────────\n\n override installSubagents(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[HOST]?.subagents === false) {\n return [{ platform: this.id, action: \"skip\", detail: \"subagents disabled for cursor\" }];\n }\n if (connector.subagents.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\n }\n return connector.subagents.map((agent) =>\n this.writeContentFile(\n this.subagentPath(ctx, agent.name),\n this.renderSubagent(agent),\n ctx.dryRun,\n ),\n );\n }\n\n override uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.subagents.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\n }\n return connector.subagents.map((agent) =>\n this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun),\n );\n }\n\n /** Render a Cursor subagent to md+frontmatter (name, description, model, readonly) + prompt body. */\n private renderSubagent(agent: SubagentDef): string {\n const frontmatter: Record<string, unknown> = {\n name: agent.name,\n description: agent.description,\n };\n if (agent.model !== undefined) frontmatter.model = agent.model;\n if (agent.readonly !== undefined) frontmatter.readonly = agent.readonly;\n if (agent.extra) Object.assign(frontmatter, agent.extra);\n return this.renderFrontmatterMd(frontmatter, agent.prompt);\n }\n\n // ── Diagnostics ──────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const mcpPath = this.getServerConfigPath(ctx);\n const hooksPath = this.getHookConfigPath(ctx);\n const checks: HealthCheck[] = [\n {\n name: `${this.name}: mcp.json present`,\n check: () =>\n existsSync(mcpPath)\n ? { status: \"OK\", detail: mcpPath }\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\n },\n {\n name: `${this.name}: hooks.json present`,\n check: () => {\n // Same \"only assert what the connector declares\" rule as the\n // content-surface checks below: a hookless connector never writes\n // hooks.json, so its absence is healthy, not a failure.\n if (ctx.connector.hookEvents.length === 0) {\n return { status: \"OK\", detail: \"no hooks declared\" };\n }\n return existsSync(hooksPath)\n ? { status: \"OK\", detail: hooksPath }\n : { status: \"FAIL\", detail: `not found: ${hooksPath}` };\n },\n },\n ];\n\n // Content-surface checks: only assert presence of the files this connector\n // declares (skip silently for surfaces it never asked for).\n for (const cmd of ctx.connector.commands) {\n const p = this.commandPath(ctx, cmd.name);\n checks.push({\n name: `${this.name}: command ${cmd.name} present`,\n check: () =>\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n for (const skill of ctx.connector.skills) {\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\n checks.push({\n name: `${this.name}: skill ${skill.name} present`,\n check: () =>\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n for (const agent of ctx.connector.subagents) {\n const p = this.subagentPath(ctx, agent.name);\n checks.push({\n name: `${this.name}: subagent ${agent.name} present`,\n check: () =>\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\n });\n }\n return checks;\n }\n\n // ── Runtime: parse Cursor stdin JSON → normalized event ──────────────────\n\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as CursorWireInput;\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\n const sessionId = extractSessionId(input);\n const projectDir = this.getProjectDir(input);\n\n const base = {\n hostPlatform: HOST,\n connectorId,\n sessionId,\n raw,\n ...(projectDir !== undefined ? { projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.tool_name ?? \"\",\n toolInput: input.tool_input ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const toolOutput = input.tool_output ?? input.error_message;\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.tool_name ?? \"\",\n toolInput: input.tool_input ?? {},\n ...(toolOutput !== undefined ? { toolOutput } : {}),\n ...(input.error_message ? { isError: true } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n // Cursor's sessionStart stdin is exactly { session_id, is_background_agent,\n // composer_mode } — there is NO source/trigger start-source discriminator\n // (cursor.com/docs/hooks ~1278-1297). composer_mode is the editor MODE\n // (agent|ask|edit), a different concept from our normalized source enum\n // (startup|compact|resume|clear), so it does NOT map. With no discriminator\n // available, the normalized source stays at its 'startup' default (via\n // normalizeSessionSource(undefined)). is_background_agent / composer_mode\n // ride along on base.raw; there is no normalized field for either.\n const ev: SessionStartEvent = {\n ...base,\n source: normalizeSessionSource(undefined),\n };\n return ev;\n }\n case \"Stop\": {\n const ev: StopEvent = {\n ...base,\n ...(typeof input.stop_hook_active === \"boolean\"\n ? { stopHookActive: input.stop_hook_active }\n : {}),\n };\n return ev;\n }\n case \"SessionEnd\": {\n // Cursor sessionEnd: { session_id, reason, duration_ms, ... }. session_id\n // is already folded into base.sessionId via extractSessionId; only the\n // documented `reason` enum maps onto the normalized SessionEnd shape.\n const ev: SessionEndEvent = {\n ...base,\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\n };\n return ev;\n }\n case \"UserPromptSubmit\": {\n // Cursor beforeSubmitPrompt: { prompt, attachments }. attachments has no\n // normalized field and is preserved via base.raw; only `prompt` maps.\n const ev: UserPromptSubmitEvent = {\n ...base,\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\n };\n return ev;\n }\n case \"PreCompact\": {\n // Cursor preCompact: { trigger: \"auto\"|\"manual\", context_*… }. Only the\n // documented `trigger` enum maps onto the normalized PreCompact shape.\n const ev: PreCompactEvent = {\n ...base,\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\n ? { trigger: input.trigger }\n : {}),\n };\n return ev;\n }\n case \"PostToolUseFailure\": {\n // Cursor's established error field is error_message; fall back to the\n // Claude-compatible `error` defensively.\n const error = input.error_message ?? input.error ?? \"\";\n const ev: PostToolUseFailureEvent = {\n ...base,\n toolName: input.tool_name ?? \"\",\n toolInput: input.tool_input ?? {},\n error,\n ...(typeof input.tool_use_id === \"string\"\n ? { toolUseId: input.tool_use_id }\n : {}),\n ...(typeof input.is_interrupt === \"boolean\"\n ? { isInterrupt: input.is_interrupt }\n : {}),\n // postToolUseFailure reports a BARE `duration` (ms), not `duration_ms`\n // (cursor.com/docs/hooks ~904,918). duration_ms only exists on\n // sessionEnd/subagentStop.\n ...(typeof input.duration === \"number\"\n ? { durationMs: input.duration }\n : {}),\n };\n return ev;\n }\n case \"SubagentStart\": {\n const agentId = input.agent_id ?? input.subagent_id;\n const agentType = input.agent_type ?? input.subagent_type;\n const ev: SubagentStartEvent = {\n ...base,\n ...(typeof agentId === \"string\" ? { agentId } : {}),\n ...(typeof agentType === \"string\" ? { agentType } : {}),\n };\n return ev;\n }\n case \"SubagentStop\": {\n const agentId = input.agent_id ?? input.subagent_id;\n const agentType = input.agent_type ?? input.subagent_type;\n const ev: SubagentStopEvent = {\n ...base,\n ...(typeof agentId === \"string\" ? { agentId } : {}),\n ...(typeof agentType === \"string\" ? { agentType } : {}),\n // Cursor's subagentStop carries the subagent's output text under\n // `summary`, NOT last_assistant_message (cursor.com/docs/hooks ~972,993).\n ...(typeof input.summary === \"string\"\n ? { lastAssistantMessage: input.summary }\n : {}),\n ...(typeof input.stop_hook_active === \"boolean\"\n ? { stopHookActive: input.stop_hook_active }\n : {}),\n };\n return ev;\n }\n default: {\n // Cursor never delivers Notification / PermissionRequest (no native\n // equivalent — permission is an OUTPUT field of its before* hooks, not\n // an event). If the runtime dispatches one anyway, fail loudly.\n throw new Error(`unsupported cursor hook event: ${String(event)}`);\n }\n }\n }\n\n private getProjectDir(input: CursorWireInput): string | undefined {\n return input.cwd ?? input.workspace_roots?.[0] ?? undefined;\n }\n\n // ── Runtime: normalized response → Cursor native hook reply ──────────────\n\n formatReply(event: HookEventName, response: HookResponse): HookReply {\n const decision = response.decision ?? \"allow\";\n\n // postToolUseFailure (feedback beside the error) and subagentStart (context\n // injected into the SUBAGENT's conversation) are observe/context-only on\n // Cursor: \"context\" emits additional_context, and a \"deny\" DEGRADES to the\n // same shape carrying the reason (the tool already failed / the spawn is\n // not blockable). Everything else is a minimal no-op payload (Cursor\n // rejects empty stdout as \"no valid response\").\n if (event === \"PostToolUseFailure\" || event === \"SubagentStart\") {\n const context =\n decision === \"context\"\n ? response.additionalContext\n : decision === \"deny\"\n ? response.reason ?? response.additionalContext\n : undefined;\n return this.stdout({ additional_context: context ?? \"\" });\n }\n\n // beforeSubmitPrompt (UserPromptSubmit) is a BLOCK gate, not a permission/\n // context surface: its only documented output is { continue, user_message }\n // (cursor.com/docs/hooks). A \"deny\" prevents submission (continue:false +\n // reason); it cannot inject context (no additional_context/agent_message\n // field), so every non-deny decision is a no-op continue:true.\n if (event === \"UserPromptSubmit\") {\n if (decision === \"deny\") {\n return this.stdout({\n continue: false,\n user_message: response.reason ?? \"Blocked by hook\",\n });\n }\n return this.stdout({ continue: true });\n }\n\n // sessionEnd is fire-and-forget (the response is logged but not used) and\n // preCompact is observational (cannot block or modify compaction) per\n // cursor.com/docs/hooks — neither carries a decision Cursor honors, so both\n // are an unconditional no-op passthrough (mirrors Stop).\n if (event === \"SessionEnd\" || event === \"PreCompact\") {\n return { exitCode: 0 };\n }\n\n // deny → block the action with a user-facing message. (On SubagentStop\n // this follows the adapter's Stop idiom — the deny carries Stop semantics.)\n if (decision === \"deny\") {\n return this.stdout({\n permission: \"deny\",\n user_message: response.reason ?? \"Blocked by hook\",\n });\n }\n\n // ask → prompt the user to confirm.\n if (decision === \"ask\") {\n return this.stdout({\n permission: \"ask\",\n user_message: response.reason ?? \"Confirmation required by hook\",\n });\n }\n\n // modify → rewrite PreToolUse input (only where Cursor supports it).\n if (decision === \"modify\") {\n if (event === \"PreToolUse\" && response.updatedInput) {\n return this.stdout({ updated_input: response.updatedInput });\n }\n // Output rewrite is unsupported on Cursor; fall through to allow.\n }\n\n // context → inject soft guidance. PreToolUse uses `agent_message`; Post and\n // SessionStart use `additional_context`.\n if (decision === \"context\" && response.additionalContext) {\n return event === \"PreToolUse\"\n ? this.stdout({ agent_message: response.additionalContext })\n : this.stdout({ additional_context: response.additionalContext });\n }\n\n // SessionStart always emits valid JSON even on a no-op (Cursor rejects empty\n // stdout as \"no valid response\").\n if (event === \"SessionStart\") {\n return this.stdout({ additional_context: response.additionalContext ?? \"\" });\n }\n\n // allow / void → minimal no-op payload (Cursor rejects empty stdout).\n if (event === \"PreToolUse\") return this.stdout({ agent_message: \"\" });\n if (event === \"PostToolUse\") return this.stdout({ additional_context: \"\" });\n return { exitCode: 0 };\n }\n\n private stdout(payload: unknown): HookReply {\n return { exitCode: 0, stdout: JSON.stringify(payload) };\n }\n}\n\n/** Cursor native interpolation token: `${env:VAR}` → `${env:VAR}` (passthrough). */\nfunction cursorEnvToken(name: string): string {\n return `\\${env:${name}}`;\n}\n\nfunction extractSessionId(input: CursorWireInput): string {\n if (typeof input.conversation_id === \"string\") return input.conversation_id;\n if (typeof input.session_id === \"string\") return input.session_id;\n return \"\";\n}\n\nexport const adapter = new CursorAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
compileMatcher,
|
|
4
|
+
eventMatcherSubject
|
|
5
|
+
} from "./chunk-24K6JOKV.js";
|
|
6
|
+
import {
|
|
7
|
+
SURFACE_PREDICATES,
|
|
8
|
+
hostCanFireEvent
|
|
9
|
+
} from "./chunk-MDZG5QHB.js";
|
|
10
|
+
import {
|
|
11
|
+
allAdapters,
|
|
12
|
+
loadAdapter
|
|
13
|
+
} from "./chunk-Q5LECKZL.js";
|
|
14
|
+
|
|
15
|
+
// src/sdk/test-harness.ts
|
|
16
|
+
function declaredGlobalSurfaces(connector) {
|
|
17
|
+
const out = [];
|
|
18
|
+
if (connector.server) out.push("server");
|
|
19
|
+
if (Object.keys(connector.hooks).length > 0) out.push("hooks");
|
|
20
|
+
if (connector.commands.length > 0) out.push("commands");
|
|
21
|
+
if (connector.skills.length > 0) out.push("skills");
|
|
22
|
+
if (connector.subagents.length > 0) out.push("subagents");
|
|
23
|
+
if (connector.memory.length > 0) out.push("memory");
|
|
24
|
+
if (connector.statusline) out.push("statusline");
|
|
25
|
+
if (connector.actions.length > 0) out.push("actions");
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function platformScopedSurfacesFor(override) {
|
|
29
|
+
const out = [];
|
|
30
|
+
if (!override) return out;
|
|
31
|
+
if ((override.configPatch?.length ?? 0) > 0) out.push("configPatch");
|
|
32
|
+
if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {
|
|
33
|
+
out.push("nativeHooks");
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function isDisabledForHost(connector, host, surface) {
|
|
38
|
+
const override = connector.platforms[host];
|
|
39
|
+
if (!override) return false;
|
|
40
|
+
switch (surface) {
|
|
41
|
+
case "server":
|
|
42
|
+
return override.server === false;
|
|
43
|
+
case "hooks":
|
|
44
|
+
return override.hooks === false;
|
|
45
|
+
case "commands":
|
|
46
|
+
return override.commands === false;
|
|
47
|
+
case "skills":
|
|
48
|
+
return override.skills === false;
|
|
49
|
+
case "subagents":
|
|
50
|
+
return override.subagents === false;
|
|
51
|
+
case "memory":
|
|
52
|
+
return override.memory === false;
|
|
53
|
+
case "statusline":
|
|
54
|
+
return override.statusline === false;
|
|
55
|
+
case "actions":
|
|
56
|
+
case "configPatch":
|
|
57
|
+
case "nativeHooks":
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function explain(connector) {
|
|
62
|
+
const globalSurfaces = declaredGlobalSurfaces(connector);
|
|
63
|
+
const adapters = await allAdapters();
|
|
64
|
+
const rows = [];
|
|
65
|
+
for (const adapter of adapters) {
|
|
66
|
+
const override = connector.platforms[adapter.id];
|
|
67
|
+
const surfaces = [
|
|
68
|
+
...globalSurfaces,
|
|
69
|
+
...platformScopedSurfacesFor(override)
|
|
70
|
+
];
|
|
71
|
+
for (const surface of surfaces) {
|
|
72
|
+
if (isDisabledForHost(connector, adapter.id, surface)) {
|
|
73
|
+
rows.push({
|
|
74
|
+
host: adapter.id,
|
|
75
|
+
surface,
|
|
76
|
+
support: "disabled",
|
|
77
|
+
reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (surface === "hooks") {
|
|
82
|
+
rows.push(hooksRow(connector, adapter.id, adapter.capabilities));
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const native = SURFACE_PREDICATES[surface](adapter.capabilities);
|
|
86
|
+
rows.push({
|
|
87
|
+
host: adapter.id,
|
|
88
|
+
surface,
|
|
89
|
+
support: native ? "native" : "skip-warn",
|
|
90
|
+
reason: explainReason(adapter.id, surface, native)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
rows.sort(
|
|
95
|
+
(a, b) => a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host)
|
|
96
|
+
);
|
|
97
|
+
return rows;
|
|
98
|
+
}
|
|
99
|
+
function hooksRow(connector, host, capabilities) {
|
|
100
|
+
const declared = connector.hookEvents;
|
|
101
|
+
const dead = declared.filter((e) => !hostCanFireEvent(capabilities, e));
|
|
102
|
+
if (dead.length === 0) {
|
|
103
|
+
return {
|
|
104
|
+
host,
|
|
105
|
+
surface: "hooks",
|
|
106
|
+
support: "native",
|
|
107
|
+
reason: `${host} can fire every declared hook event (${declared.join(", ")})`
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const fireable = declared.filter((e) => hostCanFireEvent(capabilities, e));
|
|
111
|
+
const reason = fireable.length === 0 ? `${host} cannot fire any declared hook event (${dead.join(", ")}); install skip-warns each` : `${host} cannot fire ${dead.join(", ")} (install skip-warns these); honors ${fireable.join(", ")}`;
|
|
112
|
+
return { host, surface: "hooks", support: "skip-warn", reason };
|
|
113
|
+
}
|
|
114
|
+
function explainReason(host, surface, native) {
|
|
115
|
+
if (surface === "server") {
|
|
116
|
+
return native ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)` : `${host} advertises no MCP transport; install skip-warns`;
|
|
117
|
+
}
|
|
118
|
+
return native ? `${host} natively supports ${surface}` : `${host} cannot honor ${surface}; install skip-warns`;
|
|
119
|
+
}
|
|
120
|
+
function coerceInput(input) {
|
|
121
|
+
if (input == null) return {};
|
|
122
|
+
if (typeof input === "string") {
|
|
123
|
+
const trimmed = input.trim();
|
|
124
|
+
if (trimmed === "") return {};
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(trimmed);
|
|
127
|
+
} catch {
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return input;
|
|
132
|
+
}
|
|
133
|
+
function normalizeResponse(value) {
|
|
134
|
+
if (value == null || typeof value !== "object") return {};
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
function parseReplyStdout(stdout) {
|
|
138
|
+
if (stdout == null || stdout.trim() === "") return void 0;
|
|
139
|
+
try {
|
|
140
|
+
const value = JSON.parse(stdout);
|
|
141
|
+
return value != null && typeof value === "object" ? value : void 0;
|
|
142
|
+
} catch {
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function deepEqual(a, b) {
|
|
147
|
+
if (a === b) return true;
|
|
148
|
+
if (typeof a !== typeof b || a == null || b == null) return false;
|
|
149
|
+
if (typeof a !== "object") return false;
|
|
150
|
+
const aArr = Array.isArray(a);
|
|
151
|
+
if (aArr !== Array.isArray(b)) return false;
|
|
152
|
+
if (aArr) {
|
|
153
|
+
const x2 = a;
|
|
154
|
+
const y2 = b;
|
|
155
|
+
return x2.length === y2.length && x2.every((v, i) => deepEqual(v, y2[i]));
|
|
156
|
+
}
|
|
157
|
+
const x = a;
|
|
158
|
+
const y = b;
|
|
159
|
+
const xk = Object.keys(x);
|
|
160
|
+
const yk = Object.keys(y);
|
|
161
|
+
return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));
|
|
162
|
+
}
|
|
163
|
+
function judgeHookHonor(host, event, response, stdout) {
|
|
164
|
+
const decision = response.decision;
|
|
165
|
+
if (decision === void 0 || decision === "allow") {
|
|
166
|
+
return { honored: true, reason: "pass-through allow" };
|
|
167
|
+
}
|
|
168
|
+
const reply = parseReplyStdout(stdout);
|
|
169
|
+
const hso = reply?.hookSpecificOutput;
|
|
170
|
+
if (decision === "context") {
|
|
171
|
+
const want = response.additionalContext;
|
|
172
|
+
const got = hso?.additionalContext ?? reply?.systemMessage;
|
|
173
|
+
if (reply && got === want) {
|
|
174
|
+
return { honored: true, reason: `${host} injects context on ${event}` };
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
honored: false,
|
|
178
|
+
reason: `${host} drops context on ${event} (no stdout path)`
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (decision === "deny") {
|
|
182
|
+
if (!reply) {
|
|
183
|
+
return {
|
|
184
|
+
honored: false,
|
|
185
|
+
reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (hso?.permissionDecision === "deny" || hso?.decision?.behavior === "deny") {
|
|
189
|
+
return { honored: true, reason: `${host} blocks ${event}` };
|
|
190
|
+
}
|
|
191
|
+
if (reply.decision === "block") {
|
|
192
|
+
return {
|
|
193
|
+
honored: true,
|
|
194
|
+
reason: `${host} continues the session/subagent on ${event} (persistence) \u2014 NOT a block`
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (hso?.additionalContext != null) {
|
|
198
|
+
return {
|
|
199
|
+
honored: false,
|
|
200
|
+
reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
honored: false,
|
|
205
|
+
reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (decision === "ask") {
|
|
209
|
+
if (hso?.permissionDecision === "ask") {
|
|
210
|
+
return { honored: true, reason: `${host} asks on ${event}` };
|
|
211
|
+
}
|
|
212
|
+
if (event === "PermissionRequest" && !reply) {
|
|
213
|
+
return {
|
|
214
|
+
honored: true,
|
|
215
|
+
reason: `${host} shows the native confirmation dialog on ${event} (the ask)`
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
honored: false,
|
|
220
|
+
reason: `${host} drops ask on ${event} (no ask path)`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;
|
|
224
|
+
if (reply && wrote !== void 0 && deepEqual(wrote, response.updatedInput)) {
|
|
225
|
+
return { honored: true, reason: `${host} rewrites tool input on ${event}` };
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
honored: false,
|
|
229
|
+
reason: `${host} drops modify on ${event} (modify not honored)`
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function simulate(connector, opts) {
|
|
233
|
+
try {
|
|
234
|
+
const adapter = await loadAdapter(opts.host);
|
|
235
|
+
if (!adapter) {
|
|
236
|
+
return { honored: false, reason: `unknown host "${opts.host}"` };
|
|
237
|
+
}
|
|
238
|
+
if (opts.surface === "statusline") {
|
|
239
|
+
if (!(adapter.capabilities.supportsStatusline ?? false) || !adapter.parseStatusInput || !adapter.formatStatusOutput) {
|
|
240
|
+
return { honored: false, reason: `${opts.host} has no statusline surface` };
|
|
241
|
+
}
|
|
242
|
+
if (!connector.statusline || typeof connector.statusline.render !== "function") {
|
|
243
|
+
return { honored: false, reason: "connector declares no statusline" };
|
|
244
|
+
}
|
|
245
|
+
const ctx = adapter.parseStatusInput(coerceInput(opts.input));
|
|
246
|
+
ctx.connectorId = connector.id;
|
|
247
|
+
if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;
|
|
248
|
+
const perHost2 = connector.statusline.hosts?.[opts.host]?.render;
|
|
249
|
+
const render = typeof perHost2 === "function" ? perHost2 : connector.statusline.render;
|
|
250
|
+
const rendered = await render(ctx);
|
|
251
|
+
const line = rendered == null ? "" : String(rendered);
|
|
252
|
+
const reply2 = adapter.formatStatusOutput(line);
|
|
253
|
+
return {
|
|
254
|
+
honored: true,
|
|
255
|
+
...reply2.stdout !== void 0 ? { hostReply: reply2.stdout } : {},
|
|
256
|
+
reason: "rendered"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const event = opts.event;
|
|
260
|
+
if (!event) {
|
|
261
|
+
return { honored: false, reason: 'event is required when surface === "hooks"' };
|
|
262
|
+
}
|
|
263
|
+
if (!adapter.parseEvent || !adapter.formatReply) {
|
|
264
|
+
return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };
|
|
265
|
+
}
|
|
266
|
+
const evt = adapter.parseEvent(event, coerceInput(opts.input));
|
|
267
|
+
evt.connectorId = connector.id;
|
|
268
|
+
evt.capabilities = adapter.capabilities;
|
|
269
|
+
const definition = connector.hooks[event];
|
|
270
|
+
if (!definition || typeof definition.handler !== "function") {
|
|
271
|
+
return { honored: false, reason: `connector declares no ${event} handler` };
|
|
272
|
+
}
|
|
273
|
+
const subject = eventMatcherSubject(evt);
|
|
274
|
+
if (subject !== void 0) {
|
|
275
|
+
const re = compileMatcher(definition.matcher);
|
|
276
|
+
if (re && !re.test(subject)) {
|
|
277
|
+
return {
|
|
278
|
+
honored: true,
|
|
279
|
+
reason: `matcher excludes ${subject} (handler not run)`
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const perHost = definition.hosts?.[opts.host]?.handler;
|
|
284
|
+
const handler = typeof perHost === "function" ? perHost : definition.handler;
|
|
285
|
+
const response = normalizeResponse(
|
|
286
|
+
await handler(evt)
|
|
287
|
+
);
|
|
288
|
+
const reply = adapter.formatReply(event, response);
|
|
289
|
+
const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);
|
|
290
|
+
return {
|
|
291
|
+
honored: verdict.honored,
|
|
292
|
+
...reply.stdout !== void 0 ? { hostReply: reply.stdout } : {},
|
|
293
|
+
reason: verdict.reason
|
|
294
|
+
};
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
297
|
+
const verb = opts.surface === "statusline" ? "render" : "handler";
|
|
298
|
+
return { honored: false, reason: `${verb} threw: ${message}` };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function matchingSubject(matcher, fallback) {
|
|
302
|
+
if (!matcher || matcher === "") return fallback;
|
|
303
|
+
return /^[\w.-]+$/.test(matcher) ? matcher : fallback;
|
|
304
|
+
}
|
|
305
|
+
function syntheticPayloadFor(event, matcher) {
|
|
306
|
+
switch (event) {
|
|
307
|
+
case "PreToolUse":
|
|
308
|
+
case "PostToolUse":
|
|
309
|
+
case "PostToolUseFailure":
|
|
310
|
+
case "PermissionRequest": {
|
|
311
|
+
const tool = matchingSubject(matcher, "Bash");
|
|
312
|
+
return { tool_name: tool, tool_input: { command: "echo hi" } };
|
|
313
|
+
}
|
|
314
|
+
case "SubagentStart":
|
|
315
|
+
case "SubagentStop": {
|
|
316
|
+
const agent = matchingSubject(matcher, "general");
|
|
317
|
+
return { agent_type: agent, subagent_type: agent };
|
|
318
|
+
}
|
|
319
|
+
case "UserPromptSubmit":
|
|
320
|
+
return { prompt: "hello" };
|
|
321
|
+
default:
|
|
322
|
+
return {};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function explainHooks(connector, hosts) {
|
|
326
|
+
const events = connector.hookEvents;
|
|
327
|
+
const rows = [];
|
|
328
|
+
for (const host of hosts) {
|
|
329
|
+
const adapter = await loadAdapter(host);
|
|
330
|
+
if (!adapter) {
|
|
331
|
+
for (const event of events) {
|
|
332
|
+
rows.push({
|
|
333
|
+
host,
|
|
334
|
+
event,
|
|
335
|
+
verdict: "dropped",
|
|
336
|
+
reason: `unknown host "${host}"`
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const hasHookRuntime = !!adapter.parseEvent && !!adapter.formatReply;
|
|
342
|
+
for (const event of events) {
|
|
343
|
+
if (!hasHookRuntime) {
|
|
344
|
+
rows.push({
|
|
345
|
+
host: adapter.id,
|
|
346
|
+
event,
|
|
347
|
+
verdict: "dropped",
|
|
348
|
+
reason: `${adapter.id} has no hook runtime (mcp-only) \u2014 ${event} never fires`
|
|
349
|
+
});
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (!hostCanFireEvent(adapter.capabilities, event)) {
|
|
353
|
+
rows.push({
|
|
354
|
+
host: adapter.id,
|
|
355
|
+
event,
|
|
356
|
+
verdict: "dropped",
|
|
357
|
+
reason: `${adapter.id} has no ${event} equivalent \u2014 install skip-warns it; never fires`
|
|
358
|
+
});
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const sim = await simulate(connector, {
|
|
362
|
+
surface: "hooks",
|
|
363
|
+
host: adapter.id,
|
|
364
|
+
event,
|
|
365
|
+
input: syntheticPayloadFor(event, connector.hooks[event]?.matcher)
|
|
366
|
+
});
|
|
367
|
+
rows.push({
|
|
368
|
+
host: adapter.id,
|
|
369
|
+
event,
|
|
370
|
+
verdict: sim.honored ? "honored" : "degraded",
|
|
371
|
+
reason: sim.reason
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
rows.sort(
|
|
376
|
+
(a, b) => a.host === b.host ? a.event.localeCompare(b.event) : a.host.localeCompare(b.host)
|
|
377
|
+
);
|
|
378
|
+
return rows;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export {
|
|
382
|
+
explain,
|
|
383
|
+
simulate,
|
|
384
|
+
explainHooks
|
|
385
|
+
};
|
|
386
|
+
//# sourceMappingURL=chunk-ECSRTK4M.js.map
|