@ken-jo/agent-connector 0.4.44 → 0.4.61

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.
Files changed (186) hide show
  1. package/README.md +17 -4
  2. package/dist/{action-72J2TCKQ.js → action-AIB2W4DU.js} +12 -12
  3. package/dist/{amazon-q-UFLBUSHG.js → amazon-q-PH7OFPJD.js} +7 -11
  4. package/dist/amazon-q-PH7OFPJD.js.map +1 -0
  5. package/dist/{amp-5DJK7VAL.js → amp-ZYRC2VCH.js} +38 -63
  6. package/dist/amp-ZYRC2VCH.js.map +1 -0
  7. package/dist/{antigravity-NWP2TUG6.js → antigravity-PM5NMS2J.js} +5 -5
  8. package/dist/{antigravity-cli-KPNLE7X4.js → antigravity-cli-LN25XGWX.js} +5 -5
  9. package/dist/{chunk-LETB5HCK.js → chunk-3QCDVSMX.js} +2 -2
  10. package/dist/{chunk-ERV4TKRV.js → chunk-4K5HEGVE.js} +2 -2
  11. package/dist/{chunk-ASHAMORS.js → chunk-AAOYOAQT.js} +2 -2
  12. package/dist/{chunk-2RPHDVHW.js → chunk-AEQRMVRB.js} +2 -2
  13. package/dist/{chunk-L7ESV37V.js → chunk-BZOOVTFG.js} +4 -2
  14. package/dist/chunk-BZOOVTFG.js.map +1 -0
  15. package/dist/{chunk-3Q6YEDCG.js → chunk-C3TFA4CJ.js} +17 -45
  16. package/dist/chunk-C3TFA4CJ.js.map +1 -0
  17. package/dist/{chunk-QY42WGBX.js → chunk-D65GIBCG.js} +34 -3
  18. package/dist/chunk-D65GIBCG.js.map +1 -0
  19. package/dist/{chunk-VPSHGDDQ.js → chunk-FVARNRCE.js} +26 -10
  20. package/dist/chunk-FVARNRCE.js.map +1 -0
  21. package/dist/{chunk-A767JFHW.js → chunk-GI4FDVWI.js} +65 -35
  22. package/dist/chunk-GI4FDVWI.js.map +1 -0
  23. package/dist/{chunk-SBV2QJ5C.js → chunk-IDKI42JW.js} +36 -36
  24. package/dist/{chunk-WZWK4ICE.js → chunk-JZIM33C2.js} +10 -7
  25. package/dist/chunk-JZIM33C2.js.map +1 -0
  26. package/dist/{chunk-IKEXNI2W.js → chunk-KAEYJGK7.js} +18 -18
  27. package/dist/{chunk-ZY25RG4Y.js → chunk-LBOGRZGY.js} +4 -2
  28. package/dist/chunk-LBOGRZGY.js.map +1 -0
  29. package/dist/{chunk-B47MDOV3.js → chunk-NFMHYIN2.js} +5 -5
  30. package/dist/{chunk-XW46WVR2.js → chunk-O3HIYTBK.js} +10 -5
  31. package/dist/chunk-O3HIYTBK.js.map +1 -0
  32. package/dist/{chunk-UXG64P6R.js → chunk-O4BKQSPP.js} +7 -4
  33. package/dist/chunk-O4BKQSPP.js.map +1 -0
  34. package/dist/{chunk-5M2SMDA4.js → chunk-QP35SRUZ.js} +2 -2
  35. package/dist/{chunk-CFM3JKIR.js → chunk-RFYQXT7C.js} +7 -7
  36. package/dist/{chunk-E3GWKJU5.js → chunk-RGQTN7K3.js} +5 -5
  37. package/dist/{chunk-M3H7PQWT.js → chunk-WOUGWPZQ.js} +4 -2
  38. package/dist/chunk-WOUGWPZQ.js.map +1 -0
  39. package/dist/{chunk-F4DSMVZ7.js → chunk-ZQCT63JU.js} +2 -2
  40. package/dist/{claude-code-HXIVRZCY.js → claude-code-GCXXEWUN.js} +15 -8
  41. package/dist/claude-code-GCXXEWUN.js.map +1 -0
  42. package/dist/cli/sdk.js +7 -7
  43. package/dist/cli.js +1 -1
  44. package/dist/{cline-AGFJF4AC.js → cline-KHZLG7CU.js} +4 -4
  45. package/dist/{codebuff-YUQ4PN7G.js → codebuff-EQGWZEP4.js} +4 -4
  46. package/dist/{codex-FLU37DV3.js → codex-7X7RC6JJ.js} +32 -6
  47. package/dist/codex-7X7RC6JJ.js.map +1 -0
  48. package/dist/{continue-ZMI4YN75.js → continue-V4MB3WQ3.js} +9 -5
  49. package/dist/continue-V4MB3WQ3.js.map +1 -0
  50. package/dist/{copilot-cli-XPRJ5MQB.js → copilot-cli-7EPIJHFM.js} +12 -32
  51. package/dist/copilot-cli-7EPIJHFM.js.map +1 -0
  52. package/dist/{crush-3OB5ZTCC.js → crush-3AAA75C5.js} +4 -4
  53. package/dist/{cursor-F4HYO2FP.js → cursor-DTYZROKS.js} +5 -5
  54. package/dist/{detect-L3NJTBO5.js → detect-LWZEM7SN.js} +4 -4
  55. package/dist/{doctor-PUE4HO3N.js → doctor-54P4JFA6.js} +14 -14
  56. package/dist/{droid-NZCKYKOV.js → droid-QMNO5WZ2.js} +6 -12
  57. package/dist/droid-QMNO5WZ2.js.map +1 -0
  58. package/dist/{gemini-cli-3ZGCXUDD.js → gemini-cli-7JU7ZWQL.js} +19 -6
  59. package/dist/gemini-cli-7JU7ZWQL.js.map +1 -0
  60. package/dist/{goose-MGGNVXJP.js → goose-36TJNDKD.js} +16 -22
  61. package/dist/goose-36TJNDKD.js.map +1 -0
  62. package/dist/{hermes-L3CBJ37L.js → hermes-S4JZ6H42.js} +44 -22
  63. package/dist/hermes-S4JZ6H42.js.map +1 -0
  64. package/dist/{hook-S3DNNGZX.js → hook-UT3Q43AB.js} +12 -12
  65. package/dist/index.d.ts +2 -2
  66. package/dist/index.js +5 -5
  67. package/dist/{install-7SUJFRYO.js → install-F4OIDYAK.js} +14 -14
  68. package/dist/{introspect-B164-hj3.d.ts → introspect-BFcXpfUp.d.ts} +1 -1
  69. package/dist/{jetbrains-copilot-M2LHJBAW.js → jetbrains-copilot-6QLKUCAY.js} +17 -12
  70. package/dist/jetbrains-copilot-6QLKUCAY.js.map +1 -0
  71. package/dist/{kilo-RMRP2EL7.js → kilo-WLZOYKZK.js} +18 -55
  72. package/dist/kilo-WLZOYKZK.js.map +1 -0
  73. package/dist/{kilo-cli-6ITBNHTH.js → kilo-cli-FBX4OFIZ.js} +18 -55
  74. package/dist/kilo-cli-FBX4OFIZ.js.map +1 -0
  75. package/dist/{kimi-QYHWAY54.js → kimi-7YXRAQ6Z.js} +8 -4
  76. package/dist/kimi-7YXRAQ6Z.js.map +1 -0
  77. package/dist/{kiro-2K5UJVGC.js → kiro-CSBQA6EX.js} +5 -5
  78. package/dist/kiro-CSBQA6EX.js.map +1 -0
  79. package/dist/{leaderboard-2VAAOK3D.js → leaderboard-ZIAXBUWD.js} +5 -5
  80. package/dist/{mimo-code-K7AMX2QW.js → mimo-code-GYJNEULG.js} +21 -56
  81. package/dist/mimo-code-GYJNEULG.js.map +1 -0
  82. package/dist/{mux-GT7FWVLH.js → mux-DAIC3VY7.js} +4 -4
  83. package/dist/{nemoclaw-GQMS6CUM.js → nemoclaw-GS3XA7V2.js} +5 -5
  84. package/dist/{omp-2RPYIZUF.js → omp-O6KNB4MM.js} +17 -46
  85. package/dist/omp-O6KNB4MM.js.map +1 -0
  86. package/dist/{openclaw-P5GMEEGC.js → openclaw-344VBMLY.js} +5 -5
  87. package/dist/{opencode-5D2SK3EX.js → opencode-JPHRZPDQ.js} +21 -56
  88. package/dist/opencode-JPHRZPDQ.js.map +1 -0
  89. package/dist/{package-VUX663ET.js → package-5FRMSWRT.js} +11 -11
  90. package/dist/{pi-ZMM4KJTO.js → pi-EFBTBI2B.js} +4 -4
  91. package/dist/{qwen-code-4ZLVGN2C.js → qwen-code-CLSMTMHX.js} +12 -5
  92. package/dist/qwen-code-CLSMTMHX.js.map +1 -0
  93. package/dist/{roo-code-67QGLQRM.js → roo-code-YIX3RPR2.js} +4 -4
  94. package/dist/runtime/index.d.ts +1 -1
  95. package/dist/runtime/index.js +10 -10
  96. package/dist/sdk/index.d.ts +3 -3
  97. package/dist/sdk/index.js +6 -6
  98. package/dist/sdk/test.d.ts +2 -2
  99. package/dist/sdk/test.js +9 -9
  100. package/dist/{serve-MOIIYFER.js → serve-NQBXN4FG.js} +12 -12
  101. package/dist/{status-SCBCWS7H.js → status-5ZM6LXHV.js} +10 -10
  102. package/dist/{statusline-NRPDOPGA.js → statusline-O76BCBUK.js} +11 -11
  103. package/dist/{telemetry-Q55B5NSZ.js → telemetry-NO7UUUSJ.js} +10 -10
  104. package/dist/{trae-TMJPHWZ5.js → trae-JESXQKAF.js} +4 -4
  105. package/dist/{types-COSBYpKs.d.ts → types-ChUrvw0j.d.ts} +8 -8
  106. package/dist/{uninstall-SNI7GPQD.js → uninstall-QCROBR6A.js} +14 -14
  107. package/dist/{upgrade-ZUNBJWJ7.js → upgrade-KJ4QRZDZ.js} +14 -14
  108. package/dist/{usage-UQDBIU42.js → usage-UY6DCND6.js} +2 -2
  109. package/dist/{usage-event-HPG2BIAY.js → usage-event-T4HWQDZF.js} +11 -11
  110. package/dist/{vscode-copilot-H7JKUKLZ.js → vscode-copilot-56USZ5X4.js} +26 -19
  111. package/dist/vscode-copilot-56USZ5X4.js.map +1 -0
  112. package/dist/{warp-SVDVKQQI.js → warp-Y6JNLLGR.js} +4 -4
  113. package/dist/{windsurf-H36YKOMR.js → windsurf-X7QCJ3XR.js} +4 -4
  114. package/dist/{zed-LHWJALLZ.js → zed-L3ML5MHF.js} +4 -4
  115. package/package.json +2 -1
  116. package/dist/amazon-q-UFLBUSHG.js.map +0 -1
  117. package/dist/amp-5DJK7VAL.js.map +0 -1
  118. package/dist/chunk-3Q6YEDCG.js.map +0 -1
  119. package/dist/chunk-A767JFHW.js.map +0 -1
  120. package/dist/chunk-L7ESV37V.js.map +0 -1
  121. package/dist/chunk-M3H7PQWT.js.map +0 -1
  122. package/dist/chunk-QY42WGBX.js.map +0 -1
  123. package/dist/chunk-UXG64P6R.js.map +0 -1
  124. package/dist/chunk-VPSHGDDQ.js.map +0 -1
  125. package/dist/chunk-WZWK4ICE.js.map +0 -1
  126. package/dist/chunk-XW46WVR2.js.map +0 -1
  127. package/dist/chunk-ZY25RG4Y.js.map +0 -1
  128. package/dist/claude-code-HXIVRZCY.js.map +0 -1
  129. package/dist/codex-FLU37DV3.js.map +0 -1
  130. package/dist/continue-ZMI4YN75.js.map +0 -1
  131. package/dist/copilot-cli-XPRJ5MQB.js.map +0 -1
  132. package/dist/droid-NZCKYKOV.js.map +0 -1
  133. package/dist/gemini-cli-3ZGCXUDD.js.map +0 -1
  134. package/dist/goose-MGGNVXJP.js.map +0 -1
  135. package/dist/hermes-L3CBJ37L.js.map +0 -1
  136. package/dist/jetbrains-copilot-M2LHJBAW.js.map +0 -1
  137. package/dist/kilo-RMRP2EL7.js.map +0 -1
  138. package/dist/kilo-cli-6ITBNHTH.js.map +0 -1
  139. package/dist/kimi-QYHWAY54.js.map +0 -1
  140. package/dist/kiro-2K5UJVGC.js.map +0 -1
  141. package/dist/mimo-code-K7AMX2QW.js.map +0 -1
  142. package/dist/omp-2RPYIZUF.js.map +0 -1
  143. package/dist/opencode-5D2SK3EX.js.map +0 -1
  144. package/dist/qwen-code-4ZLVGN2C.js.map +0 -1
  145. package/dist/vscode-copilot-H7JKUKLZ.js.map +0 -1
  146. /package/dist/{action-72J2TCKQ.js.map → action-AIB2W4DU.js.map} +0 -0
  147. /package/dist/{antigravity-NWP2TUG6.js.map → antigravity-PM5NMS2J.js.map} +0 -0
  148. /package/dist/{antigravity-cli-KPNLE7X4.js.map → antigravity-cli-LN25XGWX.js.map} +0 -0
  149. /package/dist/{chunk-LETB5HCK.js.map → chunk-3QCDVSMX.js.map} +0 -0
  150. /package/dist/{chunk-ERV4TKRV.js.map → chunk-4K5HEGVE.js.map} +0 -0
  151. /package/dist/{chunk-ASHAMORS.js.map → chunk-AAOYOAQT.js.map} +0 -0
  152. /package/dist/{chunk-2RPHDVHW.js.map → chunk-AEQRMVRB.js.map} +0 -0
  153. /package/dist/{chunk-SBV2QJ5C.js.map → chunk-IDKI42JW.js.map} +0 -0
  154. /package/dist/{chunk-IKEXNI2W.js.map → chunk-KAEYJGK7.js.map} +0 -0
  155. /package/dist/{chunk-B47MDOV3.js.map → chunk-NFMHYIN2.js.map} +0 -0
  156. /package/dist/{chunk-5M2SMDA4.js.map → chunk-QP35SRUZ.js.map} +0 -0
  157. /package/dist/{chunk-CFM3JKIR.js.map → chunk-RFYQXT7C.js.map} +0 -0
  158. /package/dist/{chunk-E3GWKJU5.js.map → chunk-RGQTN7K3.js.map} +0 -0
  159. /package/dist/{chunk-F4DSMVZ7.js.map → chunk-ZQCT63JU.js.map} +0 -0
  160. /package/dist/{cline-AGFJF4AC.js.map → cline-KHZLG7CU.js.map} +0 -0
  161. /package/dist/{codebuff-YUQ4PN7G.js.map → codebuff-EQGWZEP4.js.map} +0 -0
  162. /package/dist/{crush-3OB5ZTCC.js.map → crush-3AAA75C5.js.map} +0 -0
  163. /package/dist/{cursor-F4HYO2FP.js.map → cursor-DTYZROKS.js.map} +0 -0
  164. /package/dist/{detect-L3NJTBO5.js.map → detect-LWZEM7SN.js.map} +0 -0
  165. /package/dist/{doctor-PUE4HO3N.js.map → doctor-54P4JFA6.js.map} +0 -0
  166. /package/dist/{hook-S3DNNGZX.js.map → hook-UT3Q43AB.js.map} +0 -0
  167. /package/dist/{install-7SUJFRYO.js.map → install-F4OIDYAK.js.map} +0 -0
  168. /package/dist/{leaderboard-2VAAOK3D.js.map → leaderboard-ZIAXBUWD.js.map} +0 -0
  169. /package/dist/{mux-GT7FWVLH.js.map → mux-DAIC3VY7.js.map} +0 -0
  170. /package/dist/{nemoclaw-GQMS6CUM.js.map → nemoclaw-GS3XA7V2.js.map} +0 -0
  171. /package/dist/{openclaw-P5GMEEGC.js.map → openclaw-344VBMLY.js.map} +0 -0
  172. /package/dist/{package-VUX663ET.js.map → package-5FRMSWRT.js.map} +0 -0
  173. /package/dist/{pi-ZMM4KJTO.js.map → pi-EFBTBI2B.js.map} +0 -0
  174. /package/dist/{roo-code-67QGLQRM.js.map → roo-code-YIX3RPR2.js.map} +0 -0
  175. /package/dist/{serve-MOIIYFER.js.map → serve-NQBXN4FG.js.map} +0 -0
  176. /package/dist/{status-SCBCWS7H.js.map → status-5ZM6LXHV.js.map} +0 -0
  177. /package/dist/{statusline-NRPDOPGA.js.map → statusline-O76BCBUK.js.map} +0 -0
  178. /package/dist/{telemetry-Q55B5NSZ.js.map → telemetry-NO7UUUSJ.js.map} +0 -0
  179. /package/dist/{trae-TMJPHWZ5.js.map → trae-JESXQKAF.js.map} +0 -0
  180. /package/dist/{uninstall-SNI7GPQD.js.map → uninstall-QCROBR6A.js.map} +0 -0
  181. /package/dist/{upgrade-ZUNBJWJ7.js.map → upgrade-KJ4QRZDZ.js.map} +0 -0
  182. /package/dist/{usage-UQDBIU42.js.map → usage-UY6DCND6.js.map} +0 -0
  183. /package/dist/{usage-event-HPG2BIAY.js.map → usage-event-T4HWQDZF.js.map} +0 -0
  184. /package/dist/{warp-SVDVKQQI.js.map → warp-Y6JNLLGR.js.map} +0 -0
  185. /package/dist/{windsurf-H36YKOMR.js.map → windsurf-X7QCJ3XR.js.map} +0 -0
  186. /package/dist/{zed-LHWJALLZ.js.map → zed-L3ML5MHF.js.map} +0 -0
@@ -5,21 +5,21 @@ import {
5
5
  newRecordId,
6
6
  openStore,
7
7
  worstConfidence
8
- } from "./chunk-F4DSMVZ7.js";
8
+ } from "./chunk-ZQCT63JU.js";
9
9
  import {
10
10
  log
11
11
  } from "./chunk-KHO5WNTP.js";
12
12
  import {
13
13
  loadRegisteredConnector,
14
14
  readRegisteredMeta
15
- } from "./chunk-UXG64P6R.js";
15
+ } from "./chunk-O4BKQSPP.js";
16
16
  import {
17
17
  REGISTERED_PLATFORM_IDS,
18
18
  loadAdapter
19
- } from "./chunk-SBV2QJ5C.js";
19
+ } from "./chunk-IDKI42JW.js";
20
20
  import {
21
21
  projectIdentity
22
- } from "./chunk-QY42WGBX.js";
22
+ } from "./chunk-D65GIBCG.js";
23
23
 
24
24
  // src/runtime/telemetry-accessor.ts
25
25
  var ZERO = {
@@ -371,4 +371,4 @@ export {
371
371
  isNativeHookDeclared,
372
372
  runNativeHook
373
373
  };
374
- //# sourceMappingURL=chunk-B47MDOV3.js.map
374
+ //# sourceMappingURL=chunk-NFMHYIN2.js.map
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  BaseAdapter,
10
10
  renderSkillMd
11
- } from "./chunk-A767JFHW.js";
11
+ } from "./chunk-GI4FDVWI.js";
12
12
  import {
13
13
  buildWrappedStdio,
14
14
  isHomeBinHookCommand
@@ -498,7 +498,7 @@ ${cmd.prompt}
498
498
  case "SessionStart": {
499
499
  const ev = {
500
500
  ...base,
501
- source: normalizeSessionSource(input.source ?? input.trigger)
501
+ source: normalizeSessionSource(void 0)
502
502
  };
503
503
  return ev;
504
504
  }
@@ -539,7 +539,10 @@ ${cmd.prompt}
539
539
  error,
540
540
  ...typeof input.tool_use_id === "string" ? { toolUseId: input.tool_use_id } : {},
541
541
  ...typeof input.is_interrupt === "boolean" ? { isInterrupt: input.is_interrupt } : {},
542
- ...typeof input.duration_ms === "number" ? { durationMs: input.duration_ms } : {}
542
+ // postToolUseFailure reports a BARE `duration` (ms), not `duration_ms`
543
+ // (cursor.com/docs/hooks ~904,918). duration_ms only exists on
544
+ // sessionEnd/subagentStop.
545
+ ...typeof input.duration === "number" ? { durationMs: input.duration } : {}
543
546
  };
544
547
  return ev;
545
548
  }
@@ -560,7 +563,9 @@ ${cmd.prompt}
560
563
  ...base,
561
564
  ...typeof agentId === "string" ? { agentId } : {},
562
565
  ...typeof agentType === "string" ? { agentType } : {},
563
- ...typeof input.last_assistant_message === "string" ? { lastAssistantMessage: input.last_assistant_message } : {},
566
+ // Cursor's subagentStop carries the subagent's output text under
567
+ // `summary`, NOT last_assistant_message (cursor.com/docs/hooks ~972,993).
568
+ ...typeof input.summary === "string" ? { lastAssistantMessage: input.summary } : {},
564
569
  ...typeof input.stop_hook_active === "boolean" ? { stopHookActive: input.stop_hook_active } : {}
565
570
  };
566
571
  return ev;
@@ -641,4 +646,4 @@ export {
641
646
  adapter,
642
647
  cursor_default
643
648
  };
644
- //# sourceMappingURL=chunk-XW46WVR2.js.map
649
+ //# sourceMappingURL=chunk-O3HIYTBK.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 // 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, \"&gt;\"));\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"]}
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  defineConnector
4
- } from "./chunk-WZWK4ICE.js";
4
+ } from "./chunk-JZIM33C2.js";
5
5
  import {
6
+ assertNoSymlinkInPath,
6
7
  connectorDir,
7
8
  connectorsDir,
8
9
  ensureDir
9
- } from "./chunk-QY42WGBX.js";
10
+ } from "./chunk-D65GIBCG.js";
10
11
 
11
12
  // src/core/load-connector.ts
12
13
  import { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
@@ -99,14 +100,16 @@ function registerConnector(connector, modulePath, scope) {
99
100
  ...scope !== void 0 ? { scope } : {}
100
101
  };
101
102
  const dir = connectorDir(connector.id);
102
- ensureDir(dir);
103
103
  const outPath = join(dir, "connector.json");
104
+ assertNoSymlinkInPath(outPath);
105
+ ensureDir(dir);
104
106
  writeFileSync(outPath, `${JSON.stringify(meta, null, 2)}
105
107
  `, "utf8");
106
108
  return outPath;
107
109
  }
108
110
  function deregisterConnector(id) {
109
111
  const dir = connectorDir(id);
112
+ assertNoSymlinkInPath(dir);
110
113
  const existed = existsSync(dir);
111
114
  if (existed) rmSync(dir, { recursive: true, force: true });
112
115
  return { removed: existed, path: dir };
@@ -178,4 +181,4 @@ export {
178
181
  connectorFromMeta,
179
182
  listRegisteredConnectors
180
183
  };
181
- //# sourceMappingURL=chunk-UXG64P6R.js.map
184
+ //# sourceMappingURL=chunk-O4BKQSPP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/load-connector.ts"],"sourcesContent":["/**\n * core/load-connector — discover, load, and register connector definitions.\n *\n * Responsibilities:\n * 1. Locate the developer's `agent-connector.config.{mjs,js,json}` by walking\n * upward from a start directory (the project root convention).\n * 2. Load a config from a path into a LIVE ResolvedConnector (with handlers).\n * 3. Register a connector by persisting SERIALIZABLE metadata (no functions)\n * to `connectorDir(id)/connector.json`, pointing at the source module so\n * live handlers can be re-imported at runtime from a stable record.\n *\n * Why split live vs. serialized? Hook handlers are functions and cannot survive\n * JSON. The registry stores only what JSON can hold plus an absolute\n * `modulePath`; the runtime re-imports that module to recover the live handlers.\n */\n\nimport { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport type {\n CommandDef,\n HookEventName,\n InstallScope,\n MemoryDef,\n PlatformId,\n PlatformOverride,\n ResolvedConnector,\n ServerDef,\n SkillDef,\n SubagentDef,\n} from \"./types.js\";\nimport { defineConnector } from \"./define-connector.js\";\nimport { assertNoSymlinkInPath, connectorDir, connectorsDir, ensureDir } from \"./paths.js\";\n\n/** Candidate config filenames, in resolution-precedence order. */\nconst CONFIG_FILENAMES = [\n \"agent-connector.config.mjs\",\n \"agent-connector.config.js\",\n \"agent-connector.config.json\",\n] as const;\n\n/**\n * The serializable subset of a connector persisted to `connector.json`.\n * Hook handler functions are intentionally absent — only the absolute\n * `modulePath` is stored so the live ResolvedConnector can be re-imported.\n */\nexport interface RegisteredMeta {\n id: string;\n displayName: string;\n version: string;\n /** Absolute path to the source module that produced this connector. */\n modulePath: string;\n telemetry: ResolvedConnector[\"telemetry\"];\n /** Canonical event names the connector handles (handlers themselves stripped). */\n hookEvents: HookEventName[];\n hasServer: boolean;\n server: ServerDef | null;\n /**\n * Content surfaces (commands/skills/subagents). These are JSON-serializable\n * (no functions) so they persist cleanly and let uninstall locate the files\n * to remove even when the source module is gone.\n */\n commands: CommandDef[];\n skills: SkillDef[];\n subagents: SubagentDef[];\n /**\n * Memory entries (managed-block content surface). Plain content, persisted\n * whole so uninstall normally has the full entry list even when the source\n * module is gone (the marker prefix-scan is the fallback beyond that).\n */\n memory: MemoryDef[];\n targets: ResolvedConnector[\"targets\"];\n /** Per-platform overrides with any hook handler functions stripped. */\n platforms: Partial<Record<PlatformId, PlatformOverride>>;\n /**\n * The install scope this connector was registered under, persisted so the\n * runtime entrypoints (hook/statusline/action) can recover it cheaply (sync)\n * and stamp it onto the HostCtx/event — scope is an install-time property the\n * runtime otherwise has no access to. OPTIONAL: absent for an ad-hoc register\n * that passed no scope, or a record written before this field existed.\n * This is the install's RUN-WIDE DEFAULT scope; it can differ from the\n * effective per-host scope when a `platforms[host].scope` override is set (the\n * record is keyed by connector id only and holds no per-host scope map).\n */\n scope?: InstallScope;\n}\n\n/**\n * Search `startDir` and each ancestor for a connector config file. Returns the\n * absolute path to the first match (filesystem-root-stopping), or null.\n */\nexport function findConnectorConfig(startDir: string): string | null {\n let dir = resolve(startDir);\n // Walk upward until `dirname(dir) === dir` (filesystem root).\n for (;;) {\n for (const name of CONFIG_FILENAMES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/** True if a value already looks like a ResolvedConnector (vs. a raw config). */\nfunction isResolvedConnector(value: unknown): value is ResolvedConnector {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Array.isArray((value as { hookEvents?: unknown }).hookEvents)\n );\n}\n\n/**\n * Load a connector from an absolute or relative path.\n * - `.json` → JSON.parse, then defineConnector().\n * - `.mjs` / `.js` → dynamic import (via pathToFileURL, Windows-safe),\n * take the default export. If it already looks like a\n * ResolvedConnector use it as-is; otherwise treat it as\n * a ConnectorConfig and run it through defineConnector().\n * Returns the live connector plus the absolute module path it came from.\n */\nexport async function loadConnectorFromPath(\n path: string,\n): Promise<{ connector: ResolvedConnector; modulePath: string }> {\n let modulePath = isAbsolute(path) ? path : resolve(path);\n // Canonicalize before building the file:// URL for dynamic import. On Windows a\n // short (8.3) temp path like C:\\Users\\RUNNER~1\\...\\x.mjs round-trips the \"~\" as\n // %7E and fails to load; realpathSync.native expands it to the real long path.\n // It also resolves symlinked temp roots (e.g. macOS /var -> /private/var).\n // If the file does not exist yet, fall through with the resolve()'d path so the\n // subsequent read/import throws the real not-found error.\n try {\n modulePath = realpathSync.native(modulePath);\n } catch {\n /* keep the resolve()'d path */\n }\n\n if (modulePath.endsWith(\".json\")) {\n const text = readFileSync(modulePath, \"utf8\");\n const config = JSON.parse(text) as unknown;\n const connector = defineConnector(config as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n }\n\n // .mjs / .js — dynamic ESM import. pathToFileURL keeps Windows paths valid.\n const moduleUrl = pathToFileURL(modulePath).href;\n const mod = (await import(/* @vite-ignore */ moduleUrl)) as {\n default?: unknown;\n };\n const exported = mod.default;\n if (exported == null) {\n throw new Error(\n `Connector module has no default export: ${modulePath}`,\n );\n }\n const connector = isResolvedConnector(exported)\n ? exported\n : defineConnector(exported as Parameters<typeof defineConnector>[0]);\n return { connector, modulePath };\n}\n\n/**\n * Strip any function-valued `hooks` from per-platform overrides so the result\n * is JSON-serializable. A boolean `hooks` override is preserved; an object\n * override is dropped (it can only carry handler functions, which are not\n * serializable). `nativeHooks` declarations are persisted HANDLER-LESS (event\n * name + matcher only) so meta-based inspection (doctor / health checks / the\n * hook CLI) can still see WHICH native events were declared — the live handlers\n * are re-imported from `modulePath` at runtime, exactly like normalized hooks.\n * Everything else on the override is kept verbatim — notably `configPatch`\n * declarations, which are pure JSON (key/value/reason/docsUrl, no functions)\n * and persist WHOLE so uninstall/doctor can reason about them even when the\n * source module is gone.\n */\nfunction serializablePlatforms(\n platforms: Partial<Record<PlatformId, PlatformOverride>>,\n): Partial<Record<PlatformId, PlatformOverride>> {\n const out: Partial<Record<PlatformId, PlatformOverride>> = {};\n for (const [id, override] of Object.entries(platforms) as [\n PlatformId,\n PlatformOverride | undefined,\n ][]) {\n if (!override) continue;\n const { hooks, nativeHooks, ...rest } = override;\n const clean: PlatformOverride = { ...rest };\n // Keep only a boolean hooks flag; object hooks carry handler functions.\n if (typeof hooks === \"boolean\") clean.hooks = hooks;\n if (nativeHooks && typeof nativeHooks === \"object\") {\n const stripped: Record<string, { matcher?: string }> = {};\n for (const [event, def] of Object.entries(nativeHooks)) {\n stripped[event] =\n typeof def?.matcher === \"string\" ? { matcher: def.matcher } : {};\n }\n // Cast: the persisted record deliberately omits the (non-serializable)\n // handler; consumers of meta-derived connectors never dispatch handlers.\n clean.nativeHooks = stripped as PlatformOverride[\"nativeHooks\"];\n }\n out[id] = clean;\n }\n return out;\n}\n\n/**\n * Persist a connector's serializable metadata to `connectorDir(id)/connector.json`.\n * Handlers are NOT serialized — they are re-imported from `modulePath` at runtime.\n * The optional `scope` (the install scope the caller deployed under) is persisted\n * so the runtime entrypoints can recover it and stamp it onto the HostCtx/event.\n * Returns the absolute path of the written record.\n */\nexport function registerConnector(\n connector: ResolvedConnector,\n modulePath: string,\n scope?: InstallScope,\n): string {\n const meta: RegisteredMeta = {\n id: connector.id,\n displayName: connector.displayName,\n version: connector.version,\n modulePath: resolve(modulePath),\n telemetry: connector.telemetry,\n hookEvents: connector.hookEvents,\n hasServer: !!connector.server,\n server: connector.server ?? null,\n commands: connector.commands,\n skills: connector.skills,\n subagents: connector.subagents,\n memory: connector.memory,\n targets: connector.targets,\n platforms: serializablePlatforms(connector.platforms),\n // Persist the install scope when the caller supplied one (the installer\n // passes its install scope). Omitted from the record when undefined so an\n // ad-hoc register stays minimal and round-trips as \"no scope known\".\n ...(scope !== undefined ? { scope } : {}),\n };\n\n const dir = connectorDir(connector.id);\n const outPath = join(dir, \"connector.json\");\n assertNoSymlinkInPath(outPath);\n ensureDir(dir);\n writeFileSync(outPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n return outPath;\n}\n\n/**\n * Deregister a connector by removing its DATA-dir record directory\n * (`connectorDir(id)`) recursively. This is the inverse of {@link registerConnector}:\n * registerConnector writes `connectorDir(id)/connector.json`, but per-target\n * uninstall only strips host-native registrations — it never removes this record,\n * leaving an orphan behind. Best-effort + guarded: returns the directory path and\n * whether it existed before removal.\n */\nexport function deregisterConnector(id: string): {\n removed: boolean;\n path: string;\n} {\n const dir = connectorDir(id);\n assertNoSymlinkInPath(dir);\n const existed = existsSync(dir);\n if (existed) rmSync(dir, { recursive: true, force: true });\n return { removed: existed, path: dir };\n}\n\n/**\n * Read the registered metadata record for `id`, or null if not registered or\n * the record is unreadable/corrupt.\n */\nexport function readRegisteredMeta(id: string): RegisteredMeta | null {\n const recordPath = join(connectorDir(id), \"connector.json\");\n if (!existsSync(recordPath)) return null;\n try {\n return JSON.parse(readFileSync(recordPath, \"utf8\")) as RegisteredMeta;\n } catch {\n return null;\n }\n}\n\n/**\n * Load the LIVE ResolvedConnector (with hook handlers) for a registered id by\n * re-importing its source module. Throws a clear error when the connector is\n * not registered or its source module no longer resolves to it.\n */\nexport async function loadRegisteredConnector(\n id: string,\n): Promise<ResolvedConnector> {\n const meta = readRegisteredMeta(id);\n if (!meta) {\n throw new Error(\n `Connector \"${id}\" is not registered (no record at ${join(connectorDir(id), \"connector.json\")}). ` +\n `Run an install/register step first.`,\n );\n }\n if (!meta.modulePath || !existsSync(meta.modulePath)) {\n throw new Error(\n `Connector \"${id}\" record points at a missing module: ${meta.modulePath}`,\n );\n }\n const { connector } = await loadConnectorFromPath(meta.modulePath);\n return connector;\n}\n\n/**\n * Build a handler-less ResolvedConnector from stored metadata. Suitable for\n * inspection/doctor where live hook handlers are not needed (and where the\n * source module may even be gone). `hooks` is empty; `hookEvents` is preserved.\n */\nexport function connectorFromMeta(meta: RegisteredMeta): ResolvedConnector {\n return {\n id: meta.id,\n displayName: meta.displayName,\n version: meta.version,\n ...(meta.server ? { server: meta.server } : {}),\n hooks: {},\n hookEvents: meta.hookEvents ?? [],\n telemetry: meta.telemetry,\n commands: meta.commands ?? [],\n skills: meta.skills ?? [],\n subagents: meta.subagents ?? [],\n memory: meta.memory ?? [],\n // Actions carry live `run` handlers, so they are NOT serialized into the\n // record (like the statusline render). A handler-less meta-derived connector\n // gets [] — inspection/uninstall never dispatch action handlers, and the\n // BaseAdapter action default already tolerates an empty list.\n actions: [],\n platforms: meta.platforms ?? {},\n targets: meta.targets,\n };\n}\n\n/**\n * List every registered connector as a handler-less ResolvedConnector (read\n * from `connectorsDir()`). Used by doctor to health-check what is actually\n * installed, rather than guessing from the local working directory.\n */\nexport function listRegisteredConnectors(): ResolvedConnector[] {\n const dir = connectorsDir();\n if (!existsSync(dir)) return [];\n const out: ResolvedConnector[] = [];\n for (const entry of readdirSync(dir)) {\n const meta = readRegisteredMeta(entry);\n if (meta) out.push(connectorFromMeta(meta));\n }\n return out;\n}\n"],"mappings":"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","names":["connector"]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ADAPTER_REGISTRY
4
- } from "./chunk-SBV2QJ5C.js";
4
+ } from "./chunk-IDKI42JW.js";
5
5
 
6
6
  // src/adapters/detect.ts
7
7
  async function detectInstalledPlatforms(projectDir) {
@@ -80,4 +80,4 @@ export {
80
80
  detectInstalledPlatforms,
81
81
  detectRuntimeHost
82
82
  };
83
- //# sourceMappingURL=chunk-5M2SMDA4.js.map
83
+ //# sourceMappingURL=chunk-QP35SRUZ.js.map
@@ -9,30 +9,30 @@ import {
9
9
  } from "./chunk-7QRUZAUG.js";
10
10
  import {
11
11
  detectRuntimeHost
12
- } from "./chunk-5M2SMDA4.js";
12
+ } from "./chunk-QP35SRUZ.js";
13
13
  import {
14
14
  attachLazyTelemetryUsage,
15
15
  buildTelemetryAccessor,
16
16
  measureToolCall,
17
17
  measureToolDefs
18
- } from "./chunk-B47MDOV3.js";
18
+ } from "./chunk-NFMHYIN2.js";
19
19
  import {
20
20
  getTokenizer,
21
21
  inferModelFamily,
22
22
  newRecordId,
23
23
  openStore
24
- } from "./chunk-F4DSMVZ7.js";
24
+ } from "./chunk-ZQCT63JU.js";
25
25
  import {
26
26
  log
27
27
  } from "./chunk-KHO5WNTP.js";
28
28
  import {
29
29
  loadRegisteredConnector,
30
30
  readRegisteredMeta
31
- } from "./chunk-UXG64P6R.js";
31
+ } from "./chunk-O4BKQSPP.js";
32
32
  import {
33
33
  REGISTERED_PLATFORM_IDS,
34
34
  loadAdapter
35
- } from "./chunk-SBV2QJ5C.js";
35
+ } from "./chunk-IDKI42JW.js";
36
36
  import {
37
37
  detectLaunchMethod
38
38
  } from "./chunk-TO57WYBB.js";
@@ -40,7 +40,7 @@ import {
40
40
  dataRoot,
41
41
  ensureDir,
42
42
  projectIdentity
43
- } from "./chunk-QY42WGBX.js";
43
+ } from "./chunk-D65GIBCG.js";
44
44
 
45
45
  // src/runtime/serve.ts
46
46
  import { randomUUID } from "crypto";
@@ -691,4 +691,4 @@ export {
691
691
  runStatusline,
692
692
  runAction
693
693
  };
694
- //# sourceMappingURL=chunk-CFM3JKIR.js.map
694
+ //# sourceMappingURL=chunk-RFYQXT7C.js.map
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CURSOR_EVENT_MAP,
4
4
  CURSOR_HOOKS_VERSION
5
- } from "./chunk-XW46WVR2.js";
5
+ } from "./chunk-O3HIYTBK.js";
6
6
  import {
7
7
  MCPB_MANIFEST_VERSION,
8
8
  MCP_SERVER_SCHEMA_URL,
@@ -11,7 +11,7 @@ import {
11
11
  isConcreteSemver,
12
12
  isPlaceholderVersion,
13
13
  registryTransportType
14
- } from "./chunk-WZWK4ICE.js";
14
+ } from "./chunk-JZIM33C2.js";
15
15
  import {
16
16
  writeTomlString
17
17
  } from "./chunk-OPXAP3AC.js";
@@ -19,7 +19,7 @@ import {
19
19
  renderCommandMd,
20
20
  renderSkillMd,
21
21
  renderSubagentMd
22
- } from "./chunk-A767JFHW.js";
22
+ } from "./chunk-GI4FDVWI.js";
23
23
  import {
24
24
  buildHomeBinHookCommand,
25
25
  buildServeWrapperCommand,
@@ -29,7 +29,7 @@ import {
29
29
  ensureDir,
30
30
  firstSymlinkInPath,
31
31
  homeBinPath
32
- } from "./chunk-QY42WGBX.js";
32
+ } from "./chunk-D65GIBCG.js";
33
33
 
34
34
  // src/core/package.ts
35
35
  import { existsSync as existsSync2, readFileSync } from "fs";
@@ -1062,4 +1062,4 @@ export {
1062
1062
  packageConnectorAll,
1063
1063
  installInstructions
1064
1064
  };
1065
- //# sourceMappingURL=chunk-E3GWKJU5.js.map
1065
+ //# sourceMappingURL=chunk-RGQTN7K3.js.map
@@ -4,11 +4,12 @@ import {
4
4
  xdgConfigHome
5
5
  } from "./chunk-UM5S7DKK.js";
6
6
  import {
7
+ assertNoSymlinkInPath,
7
8
  connectorDir,
8
9
  connectorsDir,
9
10
  dataRoot,
10
11
  ensureDir
11
- } from "./chunk-QY42WGBX.js";
12
+ } from "./chunk-D65GIBCG.js";
12
13
 
13
14
  // src/core/marketplace-state.ts
14
15
  import { createHash } from "crypto";
@@ -42,6 +43,7 @@ function readMarketplaceInstalls(connectorId) {
42
43
  }
43
44
  function writeMarketplaceInstalls(connectorId, installs) {
44
45
  const path = marketplaceInstallsPath(connectorId);
46
+ assertNoSymlinkInPath(path);
45
47
  if (Object.keys(installs).length === 0) {
46
48
  rmSync(path, { force: true });
47
49
  return;
@@ -505,4 +507,4 @@ export {
505
507
  marketplaceEvidence,
506
508
  hashDirectory
507
509
  };
508
- //# sourceMappingURL=chunk-M3H7PQWT.js.map
510
+ //# sourceMappingURL=chunk-WOUGWPZQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/marketplace-state.ts"],"sourcesContent":["/**\n * core/marketplace-state — pure read/write helpers for MARKETPLACE-method\n * install state. A LEAF module (fs + paths only, no adapters, no spawning) so\n * the installer, the marketplace orchestration, doctor, and status can all\n * import it without cycles.\n *\n * Two kinds of state live here:\n *\n * 1. The per-connector state record `connectorDir(id)/marketplace-installs.json`\n * — one entry per platform a connector was marketplace-installed on\n * ({ format, bundleDir, marketplace, scope, version, contentHash,\n * installedAt }). It drives `uninstall --method auto`, upgrade staleness\n * warnings, doctor, and the double-install guard.\n *\n * 2. Read-only fs EVIDENCE probes of the hosts' own plugin state (e.g.\n * Claude Code's `$CLAUDE_CONFIG_DIR/plugins/installed_plugins.json`).\n * These catch installs the user performed manually from `package`'s\n * printed instructions, which the state record alone would miss. Probes\n * read undocumented host internals, so every reader degrades to null/false\n * on absence or parse failure — the guard convention is \"refuse only on\n * positive evidence\" (docs: design risk 4).\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n existsSync,\n readdirSync,\n readFileSync,\n realpathSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport TOML from \"@iarna/toml\";\n\nimport type { InstallScope, PlatformId } from \"./types.js\";\nimport type { PackageFormat } from \"./package.js\";\nimport {\n assertNoSymlinkInPath,\n connectorDir,\n connectorsDir,\n dataRoot,\n ensureDir,\n} from \"./paths.js\";\nimport { codexConfigHome, xdgConfigHome } from \"./host-paths.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// State record: connectorDir(id)/marketplace-installs.json\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The catalog name every staged local marketplace is registered under. */\nexport const MARKETPLACE_NAME = \"agent-connector\";\n\n/** One marketplace install of one connector on one platform. */\nexport interface MarketplaceInstallRecord {\n /** Bundle format that was staged + installed (e.g. \"claude-plugin\"). */\n format: PackageFormat;\n /** Absolute path of the staged plugin dir the host references/copied. */\n bundleDir: string;\n /** Marketplace/catalog name the install is keyed under ({@link MARKETPLACE_NAME}). */\n marketplace: string;\n /** Install scope (v1: always \"user\"). */\n scope: InstallScope;\n /** connector.version at install time (staleness warnings key off this). */\n version: string;\n /** sha256 over the staged plugin dir at install time (drift detection). */\n contentHash: string;\n /** ISO timestamp of the recorded install. */\n installedAt: string;\n}\n\n/** platform → record map persisted per connector. */\nexport type MarketplaceInstalls = Partial<\n Record<PlatformId, MarketplaceInstallRecord>\n>;\n\n/** Path of the per-connector marketplace state record. */\nexport function marketplaceInstallsPath(connectorId: string): string {\n return join(connectorDir(connectorId), \"marketplace-installs.json\");\n}\n\n/** Read the state record ({} on absence/parse failure — never throws). */\nexport function readMarketplaceInstalls(connectorId: string): MarketplaceInstalls {\n try {\n const raw = readFileSync(marketplaceInstallsPath(connectorId), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as MarketplaceInstalls;\n }\n } catch {\n /* absent / unreadable → empty */\n }\n return {};\n}\n\n/** Write the state record; an empty map DELETES the file (uninstall leaves no husk). */\nexport function writeMarketplaceInstalls(\n connectorId: string,\n installs: MarketplaceInstalls,\n): void {\n const path = marketplaceInstallsPath(connectorId);\n assertNoSymlinkInPath(path);\n if (Object.keys(installs).length === 0) {\n rmSync(path, { force: true });\n return;\n }\n ensureDir(dirname(path));\n writeFileSync(path, `${JSON.stringify(installs, null, 2)}\\n`, \"utf8\");\n}\n\n/** True when THIS connector has any recorded marketplace install. */\nexport function connectorHasMarketplaceRecords(connectorId: string): boolean {\n return Object.keys(readMarketplaceInstalls(connectorId)).length > 0;\n}\n\n/**\n * True when ANY registered connector (optionally excluding one) still records a\n * marketplace install. Guards home-bin removal on purge: an installed plugin's\n * hooks exec the home-bin OUTSIDE any adapter-managed config, so removing the\n * launcher while a marketplace install survives would silently kill its hooks.\n */\nexport function anyMarketplaceRecordsRemain(excludeConnectorId?: string): boolean {\n const dir = connectorsDir();\n if (!existsSync(dir)) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n return entries.some(\n (id) => id !== excludeConnectorId && connectorHasMarketplaceRecords(id),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Staging roots — bundles staged by-reference must survive cwd changes and\n// `rm -rf dist-plugin`, so they live under the data-root, one shared root per\n// host family, holding every marketplace-installed connector's bundle plus ONE\n// regenerated catalog named \"agent-connector\" listing them all.\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Root for every marketplace staging tree: <dataRoot>/marketplace. */\nexport function marketplaceRoot(): string {\n return join(dataRoot(), \"marketplace\");\n}\n\n/** Shared Claude-family staging root: <dataRoot>/marketplace/claude. */\nexport function claudeStagingRoot(): string {\n return join(marketplaceRoot(), \"claude\");\n}\n\n/** Shared Codex-family staging root: <dataRoot>/marketplace/codex. */\nexport function codexStagingRoot(): string {\n return join(marketplaceRoot(), \"codex\");\n}\n\n/** Shared agy (Antigravity) staging root: <dataRoot>/marketplace/agy. */\nexport function agyStagingRoot(): string {\n return join(marketplaceRoot(), \"agy\");\n}\n\n/** Shared Gemini-CLI staging root: <dataRoot>/marketplace/gemini. */\nexport function geminiStagingRoot(): string {\n return join(marketplaceRoot(), \"gemini\");\n}\n\n/** Shared Qwen-Code staging root: <dataRoot>/marketplace/qwen. */\nexport function qwenStagingRoot(): string {\n return join(marketplaceRoot(), \"qwen\");\n}\n\n/** Shared droid (Factory) staging root: <dataRoot>/marketplace/droid. */\nexport function droidStagingRoot(): string {\n return join(marketplaceRoot(), \"droid\");\n}\n\n/**\n * Shared npm-local staging root: <dataRoot>/marketplace/npm. Every npm-plugin\n * host (opencode / kilo / kilo-cli) stages here; the host config's `plugin`\n * array references the bundle by `file://<this>/<id>`, so it MUST live under the\n * stable data-root (it survives cwd changes and `rm -rf dist-plugin`).\n */\nexport function npmStagingRoot(): string {\n return join(marketplaceRoot(), \"npm\");\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Claude Code host-state readers ($CLAUDE_CONFIG_DIR || ~/.claude)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Claude Code's config dir, honoring the CLAUDE_CONFIG_DIR override. */\nexport function claudeConfigDir(): string {\n const override = process.env.CLAUDE_CONFIG_DIR;\n if (override && override.trim() !== \"\") return resolve(override);\n return join(homedir(), \".claude\");\n}\n\n/** The installed_plugins.json key our marketplace install creates for `id`. */\nexport function claudePluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\nfunction readJsonFile(path: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* absent / unreadable → null */\n }\n return null;\n}\n\n/** The `plugins` map from Claude's installed_plugins.json (null when unreadable). */\nexport function readClaudeInstalledPlugins(): Record<string, unknown> | null {\n const file = join(claudeConfigDir(), \"plugins\", \"installed_plugins.json\");\n const parsed = readJsonFile(file);\n const plugins = parsed?.plugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/** True when Claude's own state lists `<id>@agent-connector` as installed. */\nexport function claudePluginInstalled(connectorId: string): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n const entry = plugins[claudePluginKey(connectorId)];\n return Array.isArray(entry) ? entry.length > 0 : entry != null;\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Claude's state. */\nexport function anyClaudeAgentConnectorPlugins(): boolean {\n const plugins = readClaudeInstalledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, entry]) =>\n key.endsWith(`@${MARKETPLACE_NAME}`) &&\n (Array.isArray(entry) ? entry.length > 0 : entry != null),\n );\n}\n\n/**\n * The directory Claude's known_marketplaces.json records for `name`, or null\n * when not registered / unreadable. Used both as a presence probe and as the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function claudeKnownMarketplacePath(name: string): string | null {\n const file = join(claudeConfigDir(), \"plugins\", \"known_marketplaces.json\");\n const parsed = readJsonFile(file);\n const entry = parsed?.[name];\n if (!entry || typeof entry !== \"object\") return null;\n const e = entry as { installLocation?: unknown; source?: { path?: unknown } };\n if (typeof e.installLocation === \"string\") return e.installLocation;\n const sourcePath = e.source?.path;\n return typeof sourcePath === \"string\" ? sourcePath : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Codex host-state readers ($CODEX_HOME || ~/.codex → config.toml)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** The config.toml key our marketplace install creates for `id` (== claude's). */\nexport function codexPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** Parse <CODEX_HOME>/config.toml ({} on absence/parse failure — never throws). */\nfunction readCodexConfig(): Record<string, unknown> {\n const file = join(codexConfigHome(), \"config.toml\");\n try {\n return TOML.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\n/**\n * True when Codex's config.toml carries the `[plugins.\"<id>@agent-connector\"]`\n * table. The DEFINITIVE install probe — empty `plugins/cache/...` dirs linger\n * after uninstall, so the cache dir must NOT be used (docs: codex quirk).\n */\nexport function codexPluginInstalled(connectorId: string): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return codexPluginKey(connectorId) in (plugins as Record<string, unknown>);\n}\n\n/** True when ANY `*@agent-connector` plugin remains in Codex's config.toml. */\nexport function anyCodexAgentConnectorPlugins(): boolean {\n const cfg = readCodexConfig();\n const plugins = cfg[\"plugins\"];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n return Object.keys(plugins as Record<string, unknown>).some((k) =>\n k.endsWith(`@${MARKETPLACE_NAME}`),\n );\n}\n\n/**\n * The directory config.toml records as `[marketplaces.agent-connector].source`,\n * or null when not registered / unreadable. Both a presence probe and the\n * NAME-COLLISION check: a registration pointing somewhere other than our\n * staging root belongs to the user and must never be touched.\n */\nexport function codexMarketplaceSource(name: string): string | null {\n const cfg = readCodexConfig();\n const marketplaces = cfg[\"marketplaces\"];\n if (!marketplaces || typeof marketplaces !== \"object\" || Array.isArray(marketplaces)) {\n return null;\n }\n const entry = (marketplaces as Record<string, unknown>)[name];\n if (!entry || typeof entry !== \"object\") return null;\n const source = (entry as { source?: unknown }).source;\n return typeof source === \"string\" ? source : null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// droid (Factory) host-state readers (~/.factory/settings.json, JSON, NO env)\n//\n// DOCS-ONLY: no droid binary is present on this box. The settings.json key\n// shapes below (`enabledPlugins[\"<id>@agent-connector\"] === true` +\n// `extraKnownMarketplaces[\"agent-connector\"].source`) follow the documented\n// factory plugin model — confirm the exact JSON shapes live when a droid binary\n// is available. Both readers read defensively (refuse only on positive evidence).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Factory's settings file: ~/.factory/settings.json (NO dedicated env). */\nexport function factorySettingsPath(): string {\n return join(homedir(), \".factory\", \"settings.json\");\n}\n\n/** The settings.json key our marketplace install creates for `id` (== codex's). */\nexport function droidPluginKey(connectorId: string): string {\n return `${connectorId}@${MARKETPLACE_NAME}`;\n}\n\n/** The `enabledPlugins` map from droid's settings.json (null when unreadable). */\nfunction readDroidEnabledPlugins(): Record<string, unknown> | null {\n const parsed = readJsonFile(factorySettingsPath());\n const plugins = parsed?.enabledPlugins;\n if (plugins && typeof plugins === \"object\" && !Array.isArray(plugins)) {\n return plugins as Record<string, unknown>;\n }\n return null;\n}\n\n/**\n * True when droid's settings.json carries\n * `enabledPlugins[\"<id>@agent-connector\"] === true`. DOCS-only: the strict\n * `=== true` value shape is unverified live (a future droid may store an object).\n */\nexport function droidPluginInstalled(connectorId: string): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return plugins[droidPluginKey(connectorId)] === true;\n}\n\n/** True when ANY `*@agent-connector` plugin remains enabled in droid's settings. */\nexport function anyDroidAgentConnectorPlugins(): boolean {\n const plugins = readDroidEnabledPlugins();\n if (!plugins) return false;\n return Object.entries(plugins).some(\n ([key, value]) => key.endsWith(`@${MARKETPLACE_NAME}`) && value === true,\n );\n}\n\n/**\n * The directory droid's settings.json records as\n * `extraKnownMarketplaces[\"<name>\"].source`, or null when not registered /\n * unreadable. Both a presence probe and the NAME-COLLISION check (a registration\n * pointing somewhere other than our staging root belongs to the user). DOCS-only:\n * the local-path `source` field shape is undocumented, so this reads defensively\n * — accepts either a bare string source or an object carrying a `path`/`source`\n * string, and NORMALIZES to a single string for the samePath compare.\n */\nexport function droidMarketplaceSource(name: string): string | null {\n const parsed = readJsonFile(factorySettingsPath());\n const known = parsed?.extraKnownMarketplaces;\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return null;\n const entry = (known as Record<string, unknown>)[name];\n if (entry == null) return null;\n if (typeof entry === \"string\") return entry;\n if (typeof entry === \"object\" && !Array.isArray(entry)) {\n const e = entry as { source?: unknown; path?: unknown };\n if (typeof e.source === \"string\") return e.source;\n if (typeof e.path === \"string\") return e.path;\n }\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// agy (Antigravity) host-state readers (~/.gemini/config/plugins, NO env)\n// ─────────────────────────────────────────────────────────────────────────\n\n/** agy's config dir: ~/.gemini/config (NO dedicated env — HOME isolation). */\nexport function agyConfigDir(): string {\n return join(homedir(), \".gemini\", \"config\");\n}\n\n/** agy's plugins dir: ~/.gemini/config/plugins. */\nexport function agyPluginsDir(): string {\n return join(agyConfigDir(), \"plugins\");\n}\n\n/**\n * Candidate `import_manifest.json` locations. agy 1.0.7 records the manifest at\n * `<config>/plugins/import_manifest.json` on POSIX but at\n * `<config>/import_manifest.json` on Windows (live-confirmed on the my-window\n * box) — both are probed, then the copied plugin dir is the final fallback.\n */\nexport function agyImportManifestPaths(): string[] {\n return [\n join(agyConfigDir(), \"import_manifest.json\"), // win32 location\n join(agyPluginsDir(), \"import_manifest.json\"), // posix location\n ];\n}\n\n/** @deprecated single-location accessor — prefer {@link agyImportManifestPaths}. */\nexport function agyImportManifestPath(): string {\n return join(agyPluginsDir(), \"import_manifest.json\");\n}\n\n/**\n * True when `id` is installed per agy's import manifest (`imports[].name`),\n * checking BOTH the win32 and posix manifest locations, with a fallback probe of\n * the copied plugin dir's plugin.json. agy's uninstall removes the plugin dir\n * and clears the manifest entry (live-verified on Windows: install → dir +\n * manifest present, uninstall → both gone, re-install idempotent).\n */\nexport function agyPluginInstalled(connectorId: string): boolean {\n for (const manifest of agyImportManifestPaths()) {\n try {\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as {\n imports?: unknown;\n };\n const imports = parsed.imports;\n if (\n Array.isArray(imports) &&\n imports.some(\n (e) =>\n e != null &&\n typeof e === \"object\" &&\n (e as { name?: unknown }).name === connectorId,\n )\n ) {\n return true;\n }\n } catch {\n /* absent / unreadable manifest → try the next candidate */\n }\n }\n return existsSync(join(agyPluginsDir(), connectorId, \"plugin.json\"));\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Gemini CLI host-state readers (~/.gemini/extensions, NO env — HOME isolation)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Gemini CLI's config dir: ~/.gemini (NO dedicated config-dir env — isolation is\n * via HOME, live-confirmed on gemini 0.36.0). DISTINCT from agy's ~/.gemini/config\n * tree: gemini extensions land under ~/.gemini/extensions/<id>/.\n */\nexport function geminiConfigDir(): string {\n return join(homedir(), \".gemini\");\n}\n\n/**\n * True when `id` is installed per Gemini's own extension store: the marker file\n * ~/.gemini/extensions/<id>/gemini-extension.json exists. Gemini exits 0 even on\n * a logical failure, so this fs probe — not the exit code — is the source of\n * truth (the driver re-probes after every spawn).\n */\nexport function geminiExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(geminiConfigDir(), \"extensions\", connectorId, \"gemini-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Qwen Code host-state readers (~/.qwen/extensions, NO env — HOME isolation)\n//\n// DOCS-ONLY: no qwen binary is present on this box. qwen-code is a gemini-cli\n// fork, so the extension store SHAPE (~/.qwen/extensions/<id>/qwen-extension.json)\n// mirrors gemini's — confirm the marker filename live when a qwen binary lands.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Qwen Code's config dir: ~/.qwen (NO dedicated config-dir env — isolation is\n * via HOME, like its gemini-cli parent). DOCS-only: confirm live when a qwen\n * binary is available. DISTINCT from ~/.gemini (the gemini-cli store).\n */\nexport function qwenConfigDir(): string {\n return join(homedir(), \".qwen\");\n}\n\n/**\n * True when `id` is installed per Qwen's own extension store: the marker file\n * ~/.qwen/extensions/<id>/qwen-extension.json exists. Mirrors gemini's fs probe\n * (qwen is a gemini fork); the driver re-probes after every spawn. DOCS-only:\n * the marker filename (`qwen-extension.json`) is unverified live.\n */\nexport function qwenExtensionInstalled(connectorId: string): boolean {\n return existsSync(\n join(qwenConfigDir(), \"extensions\", connectorId, \"qwen-extension.json\"),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// npm-local host-state readers (opencode / kilo / kilo-cli — XDG_CONFIG_HOME)\n//\n// These hosts have NO uninstall verb; installs are recorded as `file://<dir>`\n// entries in a `plugin` array inside an opencode-style config file, which the\n// driver EDITS to remove. This module is a spawn/shared-free LEAF, so a tiny\n// posix-resolve path comparison is inlined here rather than importing\n// shared.ts's samePath (and samePath does not strip the `file://` scheme).\n// ─────────────────────────────────────────────────────────────────────────\n\n/** opencode's config dir: $XDG_CONFIG_HOME/opencode (or ~/.config/opencode). */\nexport function opencodeConfigDir(): string {\n return join(xdgConfigHome(), \"opencode\");\n}\n\n/** kilo / kilo-cli's config dir: $XDG_CONFIG_HOME/kilo (or ~/.config/kilo). */\nexport function kiloConfigDir(): string {\n return join(xdgConfigHome(), \"kilo\");\n}\n\n/**\n * Candidate config filenames per npm-local host, most-preferred first. opencode\n * accepts opencode.jsonc / opencode.json / config.json (JSONC — comments\n * tolerated); kilo / kilo-cli use opencode.json (plain JSON).\n */\nfunction npmConfigCandidates(platform: PlatformId): string[] {\n return platform === \"opencode\"\n ? [\"opencode.jsonc\", \"opencode.json\", \"config.json\"]\n : [\"opencode.json\"];\n}\n\n/** The config DIR for an npm-local host (opencode vs kilo/kilo-cli). */\nfunction npmConfigDir(platform: PlatformId): string {\n return platform === \"opencode\" ? opencodeConfigDir() : kiloConfigDir();\n}\n\n/**\n * The config FILE path for `platform`: the first existing candidate, else the\n * most-preferred candidate (the path a fresh install would write). Never throws.\n */\nexport function npmConfigFilePath(platform: PlatformId): string {\n const dir = npmConfigDir(platform);\n const candidates = npmConfigCandidates(platform);\n for (const name of candidates) {\n if (existsSync(join(dir, name))) return join(dir, name);\n }\n return join(dir, candidates[0]!);\n}\n\n/** Strip a single leading `file://` (and `file://localhost/`) scheme prefix. */\nexport function stripFileScheme(entry: string): string {\n let s = entry;\n if (s.startsWith(\"file://localhost/\")) s = s.slice(\"file://localhost\".length);\n else if (s.startsWith(\"file://\")) s = s.slice(\"file://\".length);\n return s;\n}\n\n/**\n * Strip `//` line and `/* *​/` block comments from JSONC, STRING-AWARE: a `//`\n * or `/*` inside a JSON string literal (e.g. the `file://` in a plugin entry) is\n * left intact. Scans char-by-char tracking string + escape state — a naive\n * regex would corrupt `\"file:///dir\"` by eating from the embedded `//`.\n */\nfunction stripJsoncComments(text: string): string {\n let out = \"\";\n let inString = false;\n let inLine = false;\n let inBlock = false;\n for (let i = 0; i < text.length; i++) {\n const c = text[i]!;\n const next = i + 1 < text.length ? text[i + 1]! : \"\";\n if (inLine) {\n if (c === \"\\n\") {\n inLine = false;\n out += c;\n }\n continue;\n }\n if (inBlock) {\n if (c === \"*\" && next === \"/\") {\n inBlock = false;\n i++;\n }\n continue;\n }\n if (inString) {\n out += c;\n if (c === \"\\\\\") {\n // Copy the escaped char verbatim (covers \\\" and \\\\).\n if (next !== \"\") {\n out += next;\n i++;\n }\n } else if (c === '\"') {\n inString = false;\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n out += c;\n continue;\n }\n if (c === \"/\" && next === \"/\") {\n inLine = true;\n i++;\n continue;\n }\n if (c === \"/\" && next === \"*\") {\n inBlock = true;\n i++;\n continue;\n }\n out += c;\n }\n return out;\n}\n\n/** Tolerant JSONC → object: strip // line and block comments, then JSON.parse. */\nfunction parseJsonc(text: string): Record<string, unknown> | null {\n // A parse failure degrades to null (the \"refuse only on positive evidence\"\n // convention) rather than throwing.\n try {\n const parsed = JSON.parse(stripJsoncComments(text)) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* unreadable / not JSONC → null */\n }\n return null;\n}\n\n/** Read + parse an npm-local host's config file ({}-shaped or null). Never throws. */\nexport function readNpmConfig(platform: PlatformId): Record<string, unknown> | null {\n const file = npmConfigFilePath(platform);\n try {\n return parseJsonc(readFileSync(file, \"utf8\"));\n } catch {\n return null;\n }\n}\n\n/**\n * Convert an npm `plugin`-array entry to a native absolute path. opencode/kilo\n * record a proper `file://` URL — on Windows that is `file:///C:/Users/…` (drive\n * letter + forward slashes), which a textual `file://`-strip leaves as the broken\n * `/C:/Users/…`. `fileURLToPath` does the correct cross-platform decode\n * (`file:///C:/x`→`C:\\x`, `file:///tmp/x`→`/tmp/x`). Bare-path entries (also\n * accepted by the hosts) and any malformed URL fall back to a textual strip.\n */\nfunction npmEntryToPath(entry: string): string {\n if (/^file:\\/\\//i.test(entry)) {\n try {\n return fileURLToPath(entry);\n } catch {\n /* malformed file URL (e.g. backslashes) → manual strip below */\n }\n }\n return stripFileScheme(entry);\n}\n\n/**\n * Path equality for the npm probe. Resolve symlinks with `realpathSync.native`\n * when the path exists (catches macOS `/var/folders`→`/private/var/folders` and\n * win32 8.3 short paths — the host records the realpath, we hold the symlink),\n * else lexical `resolve()`; case-fold on win32. Inlined here (leaf module)\n * rather than importing shared.ts's samePath, which pulls in the spawn layer.\n */\nfunction npmPathEquals(a: string, b: string): boolean {\n const norm = (p: string): string => {\n let s: string;\n try {\n s = realpathSync.native(p);\n } catch {\n s = resolve(p);\n }\n return process.platform === \"win32\" ? s.toLowerCase() : s;\n };\n return norm(a) === norm(b);\n}\n\n/**\n * The `plugin`-array ENTRY (the raw string, including any `file://`) whose value\n * — after stripping a leading `file://` — path-equals `id`'s staged bundle dir,\n * or null when no entry references it. Used both as the install probe and to\n * locate the exact entry the uninstall edit must drop.\n */\nexport function npmPluginArrayEntry(\n platform: PlatformId,\n connectorId: string,\n): string | null {\n const cfg = readNpmConfig(platform);\n if (!cfg) return null;\n const arr = cfg[\"plugin\"];\n if (!Array.isArray(arr)) return null;\n const target = join(npmStagingRoot(), connectorId);\n for (const raw of arr) {\n if (typeof raw !== \"string\") continue;\n if (npmPathEquals(npmEntryToPath(raw), target)) return raw;\n }\n return null;\n}\n\n/** True when `platform`'s config `plugin` array references `id`'s staged bundle. */\nexport function npmPluginInstalled(platform: PlatformId, connectorId: string): boolean {\n return npmPluginArrayEntry(platform, connectorId) != null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Cross-host marketplace-install EVIDENCE (the double-install guard's probe)\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Read-only evidence that `connectorId` is marketplace/plugin-installed on\n * `platform`: the state record first, then a cheap fs probe of the host's own\n * plugin state (catches manual installs from `package`'s printed instructions).\n * Returns a human-readable description of the evidence, or null when none.\n */\nexport function marketplaceEvidence(\n connectorId: string,\n platform: PlatformId,\n): string | null {\n if (readMarketplaceInstalls(connectorId)[platform]) {\n return \"recorded in marketplace-installs.json\";\n }\n switch (platform) {\n case \"claude-code\":\n if (claudePluginInstalled(connectorId)) {\n return `${claudePluginKey(connectorId)} listed in Claude's installed_plugins.json`;\n }\n return null;\n case \"gemini-cli\":\n return geminiExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Gemini's extensions store`\n : null;\n case \"qwen-code\":\n return qwenExtensionInstalled(connectorId)\n ? `extension ${connectorId} listed in Qwen's extensions store`\n : null;\n case \"droid\":\n return droidPluginInstalled(connectorId)\n ? `${droidPluginKey(connectorId)} enabled in droid's settings.json`\n : null;\n case \"antigravity\":\n case \"antigravity-cli\": {\n const dir = join(homedir(), \".gemini\", \"config\", \"plugins\", connectorId);\n return existsSync(dir) ? `plugin dir ${dir} exists` : null;\n }\n case \"opencode\":\n case \"kilo\":\n case \"kilo-cli\":\n return npmPluginInstalled(platform, connectorId)\n ? `${connectorId} listed in ${platform}'s config plugin array`\n : null;\n case \"codex\": {\n const dir = join(\n codexConfigHome(),\n \"plugins\",\n \"cache\",\n MARKETPLACE_NAME,\n connectorId,\n );\n return existsSync(dir) ? `codex plugin cache ${dir} exists` : null;\n }\n default:\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Content hashing — staged-bundle drift detection\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Deterministic sha256 over every file under `dir` (sorted relative paths +\n * bytes). Empty string when the dir is missing/unreadable. Used to record the\n * staged bundle's content at install time and detect drift later (claude caches\n * a versioned COPY, so an edited connector goes silently stale without this).\n */\nexport function hashDirectory(dir: string): string {\n if (!existsSync(dir)) return \"\";\n const files: string[] = [];\n const walk = (d: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(d);\n } catch {\n return;\n }\n for (const name of entries.sort()) {\n const full = join(d, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) walk(full);\n else if (st.isFile()) files.push(full);\n }\n };\n walk(dir);\n const hash = createHash(\"sha256\");\n for (const file of files.sort()) {\n hash.update(relative(dir, file).split(\"\\\\\").join(\"/\"));\n hash.update(\"\\0\");\n try {\n hash.update(readFileSync(file));\n } catch {\n /* unreadable file contributes only its path */\n }\n hash.update(\"\\0\");\n }\n return hash.digest(\"hex\");\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ensureDir,
4
4
  telemetryPath
5
- } from "./chunk-QY42WGBX.js";
5
+ } from "./chunk-D65GIBCG.js";
6
6
 
7
7
  // src/telemetry/store.ts
8
8
  import { appendFileSync, existsSync, readFileSync } from "fs";
@@ -193,4 +193,4 @@ export {
193
193
  getTokenizer,
194
194
  inferModelFamily
195
195
  };
196
- //# sourceMappingURL=chunk-F4DSMVZ7.js.map
196
+ //# sourceMappingURL=chunk-ZQCT63JU.js.map