@posthog/wizard 2.79.1 → 2.80.1

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 (227) hide show
  1. package/README.md +187 -57
  2. package/dist/{PickerMenu-dG5DQCZA.js → PickerMenu-BGGeYqd-.js} +112 -12
  3. package/dist/PickerMenu-BGGeYqd-.js.map +1 -0
  4. package/dist/README.md +9 -6
  5. package/dist/{agent-interface-FkbaBJwz.js → agent-interface-CemBvTAA.js} +130 -324
  6. package/dist/agent-interface-CemBvTAA.js.map +1 -0
  7. package/dist/{agent-prompt-loader-D0weQ-7k.js → agent-prompt-loader-DPnj6SwU.js} +6 -5
  8. package/dist/agent-prompt-loader-DPnj6SwU.js.map +1 -0
  9. package/dist/analytics-B3ROGuIr.js +2 -0
  10. package/dist/analytics-D21z3E5l.js +732 -0
  11. package/dist/analytics-D21z3E5l.js.map +1 -0
  12. package/dist/{api-CrbCIQOz.js → api-DtGIqQ2U.js} +2 -3
  13. package/dist/api-DtGIqQ2U.js.map +1 -0
  14. package/dist/api-IhROOQNv.js +2 -0
  15. package/dist/api-key-login-OEntFvu5.js +2 -0
  16. package/dist/api-key-login-h3E2G3zu.js +55 -0
  17. package/dist/api-key-login-h3E2G3zu.js.map +1 -0
  18. package/dist/bin.js +2 -2
  19. package/dist/bin.js.map +1 -1
  20. package/dist/{bounded-fs-vAq-T-K7.js → bounded-fs-CAsuF6al.js} +3 -3
  21. package/dist/{bounded-fs-vAq-T-K7.js.map → bounded-fs-CAsuF6al.js.map} +1 -1
  22. package/dist/{ci-install-COcV8SY2.js → ci-install-CEYE02t-.js} +22 -26
  23. package/dist/ci-install-CEYE02t-.js.map +1 -0
  24. package/dist/console-log-O828zyiq.js +32 -0
  25. package/dist/console-log-O828zyiq.js.map +1 -0
  26. package/dist/{debug-kAHwWGg7.js → debug-BfQPD2Hg.js} +18 -52
  27. package/dist/debug-BfQPD2Hg.js.map +1 -0
  28. package/dist/{defaults-a9TelDhJ.js → defaults-DDVl3tKN.js} +2 -51
  29. package/dist/defaults-DDVl3tKN.js.map +1 -0
  30. package/dist/{emit-BSetXzDV.js → emit-RfAewcWv.js} +1 -1
  31. package/dist/{emit-BSetXzDV.js.map → emit-RfAewcWv.js.map} +1 -1
  32. package/dist/{errors-C2domSkK.js → errors-CHrJzAZb.js} +4 -4
  33. package/dist/{errors-C2domSkK.js.map → errors-CHrJzAZb.js.map} +1 -1
  34. package/dist/family-picker-BKs-tTyu.js +46 -0
  35. package/dist/family-picker-BKs-tTyu.js.map +1 -0
  36. package/dist/{gateway-C9-GlLO9.js → gateway-D3gIlR6b.js} +122 -11
  37. package/dist/gateway-D3gIlR6b.js.map +1 -0
  38. package/dist/headless-BJjhP0SI.js +10 -0
  39. package/dist/headless-BJjhP0SI.js.map +1 -0
  40. package/dist/headless-DIX68UUA.js +3 -0
  41. package/dist/host-resolution-D8GhEVoX.js +114 -0
  42. package/dist/host-resolution-D8GhEVoX.js.map +1 -0
  43. package/dist/install-Bw8tvDvS.js +2 -0
  44. package/dist/{add-mcp-server-to-clients-DIaTRWMY.js → install-paPCzPHI.js} +34 -100
  45. package/dist/install-paPCzPHI.js.map +1 -0
  46. package/dist/interactive-tdxZKWax.js +11 -0
  47. package/dist/interactive-tdxZKWax.js.map +1 -0
  48. package/dist/{telemetry-B7zJdRmA.js → links-Cyj6v8kg.js} +4 -13
  49. package/dist/links-Cyj6v8kg.js.map +1 -0
  50. package/dist/{main-CgKxbwMP.js → main-0p-YxUNa.js} +233 -490
  51. package/dist/main-0p-YxUNa.js.map +1 -0
  52. package/dist/{mcp-DnAyW0-w.js → mcp-D3Z5MXhA.js} +3 -4
  53. package/dist/{mcp-DnAyW0-w.js.map → mcp-D3Z5MXhA.js.map} +1 -1
  54. package/dist/mcp-install-smoke-test.ts +2 -2
  55. package/dist/{mcp-prompt-streaming-DDWQ-FCA.js → mcp-prompt-streaming-COVbWnHt.js} +12 -12
  56. package/dist/mcp-prompt-streaming-COVbWnHt.js.map +1 -0
  57. package/dist/mode-label-DB3ol_sj.js +9 -0
  58. package/dist/mode-label-DB3ol_sj.js.map +1 -0
  59. package/dist/{models-WcwUnSKb.js → models-D1SnCzvl.js} +2 -2
  60. package/dist/{models-WcwUnSKb.js.map → models-D1SnCzvl.js.map} +1 -1
  61. package/dist/non-interactive-DxRBc73O.js +18 -0
  62. package/dist/non-interactive-DxRBc73O.js.map +1 -0
  63. package/dist/{oauth-session-DYM2Hrl2.js → oauth-session-Dde79Ycm.js} +3 -3
  64. package/dist/{oauth-session-DYM2Hrl2.js.map → oauth-session-Dde79Ycm.js.map} +1 -1
  65. package/dist/{orchestrator-tools-DbgNBFGA.js → orchestrator-tools-BSNhzzkT.js} +3 -3
  66. package/dist/{orchestrator-tools-DbgNBFGA.js.map → orchestrator-tools-BSNhzzkT.js.map} +1 -1
  67. package/dist/{package-json-Atg34fa4.js → package-json-2UrhE6kP.js} +1 -1
  68. package/dist/{package-json-Atg34fa4.js.map → package-json-2UrhE6kP.js.map} +1 -1
  69. package/dist/package-json-Dblip2_X.js +2 -0
  70. package/dist/package-manager-BRXlXo0p.js +105 -0
  71. package/dist/package-manager-BRXlXo0p.js.map +1 -0
  72. package/dist/{pi-8KsYsPmM.js → pi-cM_4Gd_9.js} +19 -20
  73. package/dist/pi-cM_4Gd_9.js.map +1 -0
  74. package/dist/playground-DTKy0XHT.js +12 -0
  75. package/dist/playground-DTKy0XHT.js.map +1 -0
  76. package/dist/posthog-integration-bwHwE87a.js +10291 -0
  77. package/dist/posthog-integration-bwHwE87a.js.map +1 -0
  78. package/dist/programs-BhtyA3lE.js +3 -0
  79. package/dist/programs-SeT1qNik.js +5348 -0
  80. package/dist/programs-SeT1qNik.js.map +1 -0
  81. package/dist/provisioning-BcXfeCMc.js +2 -0
  82. package/dist/{provisioning-Bo2NRxFz.js → provisioning-JbJtq7hq.js} +5 -67
  83. package/dist/provisioning-JbJtq7hq.js.map +1 -0
  84. package/dist/{queue-tools-C8mXOh7e.js → queue-tools-BIZSJyt6.js} +7 -9
  85. package/dist/queue-tools-BIZSJyt6.js.map +1 -0
  86. package/dist/results-DtKICAdz.js +55 -0
  87. package/dist/results-DtKICAdz.js.map +1 -0
  88. package/dist/run-CDurA5tO.js +248 -0
  89. package/dist/run-CDurA5tO.js.map +1 -0
  90. package/dist/run-DDCvLILJ.js +246 -0
  91. package/dist/run-DDCvLILJ.js.map +1 -0
  92. package/dist/run-tool-CTq9OOdT.js +2 -0
  93. package/dist/run-tool-DML8JJvt.js +2420 -0
  94. package/dist/run-tool-DML8JJvt.js.map +1 -0
  95. package/dist/runners-Dhu4pZTN.js +253 -0
  96. package/dist/runners-Dhu4pZTN.js.map +1 -0
  97. package/dist/{security-C71pkwqH.js → security-DEH8Rn0_.js} +5 -5
  98. package/dist/security-DEH8Rn0_.js.map +1 -0
  99. package/dist/setup-utils-C5-WM-hS.js +118 -0
  100. package/dist/setup-utils-C5-WM-hS.js.map +1 -0
  101. package/dist/smoke-test.sh +1 -1
  102. package/dist/{playground-BlYRBMj7.js → start-playground-DL4mVEUx.js} +63 -54
  103. package/dist/start-playground-DL4mVEUx.js.map +1 -0
  104. package/dist/{subagent-D1TrTchE.js → subagent-CnFvAq6E.js} +3 -3
  105. package/dist/{subagent-D1TrTchE.js.map → subagent-CnFvAq6E.js.map} +1 -1
  106. package/dist/{task-DqRlQBfs.js → task-BOScSm4W.js} +17 -18
  107. package/dist/task-BOScSm4W.js.map +1 -0
  108. package/dist/{tasks-ChWufsdO.js → tasks-BfJhVky2.js} +1 -1
  109. package/dist/tasks-BfJhVky2.js.map +1 -0
  110. package/dist/telemetry-CP4rGicd.js +13 -0
  111. package/dist/telemetry-CP4rGicd.js.map +1 -0
  112. package/dist/terminal-J3OD3dN0.js +16162 -0
  113. package/dist/terminal-J3OD3dN0.js.map +1 -0
  114. package/dist/tools-CCMV3DmB.js +544 -0
  115. package/dist/tools-CCMV3DmB.js.map +1 -0
  116. package/dist/{tools-CkJ2y9Wt.js → tools-Cz8tDptL.js} +9 -9
  117. package/dist/tools-Cz8tDptL.js.map +1 -0
  118. package/dist/tui-BUZ2PWqN.js +2 -0
  119. package/dist/tui-Csf7VJxt.js +25 -0
  120. package/dist/tui-Csf7VJxt.js.map +1 -0
  121. package/dist/{steps-BPcg3_3Q.js → upload-step-CEacB2RV.js} +17 -19
  122. package/dist/upload-step-CEacB2RV.js.map +1 -0
  123. package/dist/urls-BDELGZHq.js +67 -0
  124. package/dist/urls-BDELGZHq.js.map +1 -0
  125. package/dist/warlock-smoke-test.ts +1 -2
  126. package/dist/{wizard-abort-BPdv07np.js → wizard-abort-D3-75KyV.js} +41 -32
  127. package/dist/wizard-abort-D3-75KyV.js.map +1 -0
  128. package/dist/yara-hooks-CZ1tKxy_.js +2 -0
  129. package/dist/{yara-hooks-BnxejtXK.js → yara-hooks-D8YDyX7o.js} +283 -21
  130. package/dist/yara-hooks-D8YDyX7o.js.map +1 -0
  131. package/package.json +7 -8
  132. package/dist/PickerMenu-dG5DQCZA.js.map +0 -1
  133. package/dist/add-mcp-server-to-clients-DIaTRWMY.js.map +0 -1
  134. package/dist/add-mcp-server-to-clients-artmN7mz.js +0 -2
  135. package/dist/agent-BEjARD2a.js +0 -6
  136. package/dist/agent-BWFhuLgo.js +0 -3178
  137. package/dist/agent-BWFhuLgo.js.map +0 -1
  138. package/dist/agent-interface-FkbaBJwz.js.map +0 -1
  139. package/dist/agent-prompt-loader-D0weQ-7k.js.map +0 -1
  140. package/dist/analytics-DFtbRjYh.js +0 -2
  141. package/dist/analytics-DXK1gc12.js +0 -301
  142. package/dist/analytics-DXK1gc12.js.map +0 -1
  143. package/dist/api-Cm9UtKZh.js +0 -2
  144. package/dist/api-CrbCIQOz.js.map +0 -1
  145. package/dist/check-screens.tsx +0 -190
  146. package/dist/ci-install-COcV8SY2.js.map +0 -1
  147. package/dist/constants-DHCBSfnx.js +0 -260
  148. package/dist/constants-DHCBSfnx.js.map +0 -1
  149. package/dist/debug-BuYHGr6_.js +0 -2
  150. package/dist/debug-kAHwWGg7.js.map +0 -1
  151. package/dist/defaults-a9TelDhJ.js.map +0 -1
  152. package/dist/endpoints-BVMYm9F4.js +0 -108
  153. package/dist/endpoints-BVMYm9F4.js.map +0 -1
  154. package/dist/env-api-key-BP-NFFXt.js +0 -17
  155. package/dist/env-api-key-BP-NFFXt.js.map +0 -1
  156. package/dist/env-api-key-CaWEDj_9.js +0 -2
  157. package/dist/environment-BATlQxcQ.js +0 -39
  158. package/dist/environment-BATlQxcQ.js.map +0 -1
  159. package/dist/environment-CwD8OPYj.js +0 -2
  160. package/dist/file-C5JMsyR1.js +0 -2
  161. package/dist/file-DcRKycV8.js +0 -60
  162. package/dist/file-DcRKycV8.js.map +0 -1
  163. package/dist/file-watcher-Brq2nnHV.js +0 -123
  164. package/dist/file-watcher-Brq2nnHV.js.map +0 -1
  165. package/dist/gateway-C9-GlLO9.js.map +0 -1
  166. package/dist/headless-ui-CqI2gXlD.js +0 -44
  167. package/dist/headless-ui-CqI2gXlD.js.map +0 -1
  168. package/dist/interactive-B_Ti_CsS.js +0 -11
  169. package/dist/interactive-B_Ti_CsS.js.map +0 -1
  170. package/dist/local-dev-McsurOC-.js +0 -135
  171. package/dist/local-dev-McsurOC-.js.map +0 -1
  172. package/dist/logging-ui-Dza4D0MK.js +0 -226
  173. package/dist/logging-ui-Dza4D0MK.js.map +0 -1
  174. package/dist/main-CgKxbwMP.js.map +0 -1
  175. package/dist/mcp-prompt-streaming-DDWQ-FCA.js.map +0 -1
  176. package/dist/mint-failure-u8xQKNOn.js +0 -6392
  177. package/dist/mint-failure-u8xQKNOn.js.map +0 -1
  178. package/dist/non-interactive-DyVC1AGc.js +0 -18
  179. package/dist/non-interactive-DyVC1AGc.js.map +0 -1
  180. package/dist/package-json-D8Ga03tK.js +0 -2
  181. package/dist/pi-8KsYsPmM.js.map +0 -1
  182. package/dist/playground-BlYRBMj7.js.map +0 -1
  183. package/dist/posthog-Bt0Aplb8.js +0 -124
  184. package/dist/posthog-Bt0Aplb8.js.map +0 -1
  185. package/dist/posthog-CUgCEhH0.js +0 -2
  186. package/dist/provisioning-Bo2NRxFz.js.map +0 -1
  187. package/dist/provisioning-OGFGlzf7.js +0 -2
  188. package/dist/queue-tools-C8mXOh7e.js.map +0 -1
  189. package/dist/registry-C06e_x-p.js +0 -2
  190. package/dist/registry-CD5RQt38.js +0 -3464
  191. package/dist/registry-CD5RQt38.js.map +0 -1
  192. package/dist/run-agent-legacy-ChacZImU.js +0 -2
  193. package/dist/run-agent-legacy-DOlM6DQu.js +0 -3540
  194. package/dist/run-agent-legacy-DOlM6DQu.js.map +0 -1
  195. package/dist/runners-ov3EXF1n.js +0 -834
  196. package/dist/runners-ov3EXF1n.js.map +0 -1
  197. package/dist/security-C71pkwqH.js.map +0 -1
  198. package/dist/setup-utils-B26Lf2yF.js +0 -1512
  199. package/dist/setup-utils-B26Lf2yF.js.map +0 -1
  200. package/dist/setup-utils-CVS86NYR.js +0 -2
  201. package/dist/start-tui-Bo2L0bar.js +0 -6962
  202. package/dist/start-tui-Bo2L0bar.js.map +0 -1
  203. package/dist/steps-BPcg3_3Q.js.map +0 -1
  204. package/dist/store-BTzcvRGT.js +0 -992
  205. package/dist/store-BTzcvRGT.js.map +0 -1
  206. package/dist/store-BpqySPXm.js +0 -5
  207. package/dist/task-DqRlQBfs.js.map +0 -1
  208. package/dist/task-stream-Bw6JaLCX.js +0 -363
  209. package/dist/task-stream-Bw6JaLCX.js.map +0 -1
  210. package/dist/tasks-ChWufsdO.js.map +0 -1
  211. package/dist/telemetry-B7zJdRmA.js.map +0 -1
  212. package/dist/terminal-zgkEfoCL.js +0 -7196
  213. package/dist/terminal-zgkEfoCL.js.map +0 -1
  214. package/dist/tools-CkJ2y9Wt.js.map +0 -1
  215. package/dist/ui-Q6-RvERF.js +0 -15
  216. package/dist/ui-Q6-RvERF.js.map +0 -1
  217. package/dist/version-CHhJjz_H.js +0 -6
  218. package/dist/version-CHhJjz_H.js.map +0 -1
  219. package/dist/wizard-abort-BPdv07np.js.map +0 -1
  220. package/dist/wizard-abort-Br1qMDNa.js +0 -3
  221. package/dist/wizard-session-2w4Ru-Wz.js +0 -131
  222. package/dist/wizard-session-2w4Ru-Wz.js.map +0 -1
  223. package/dist/wizard-session-DGaUHOF3.js +0 -2
  224. package/dist/wizard-ui-DVqrYaOM.js +0 -16
  225. package/dist/wizard-ui-DVqrYaOM.js.map +0 -1
  226. package/dist/yara-hooks-BnxejtXK.js.map +0 -1
  227. package/dist/yara-hooks-DSuExwb4.js +0 -2
@@ -1 +0,0 @@
1
- {"version":3,"file":"PickerMenu-dG5DQCZA.js","names":[],"sources":["../src/ui/tui/primitives/picker-filter.ts","../src/ui/tui/primitives/PromptLabel.tsx","../src/ui/tui/primitives/ConfirmButton.tsx","../src/ui/tui/hooks/useStdoutDimensions.ts","../src/ui/tui/hooks/keyboard-hints-utils.ts","../src/ui/tui/hooks/useKeyboardHints.tsx","../src/ui/tui/hooks/useKeyBindings.ts","../src/ui/tui/primitives/PickerMenu.tsx"],"sourcesContent":["/**\n * Filter-as-you-type matching for PickerMenu. Split out from the component so the\n * ranking is testable on its own — the component only decides when to call it.\n */\n\nimport Fuse, { type IFuseOptions } from 'fuse.js';\n\n/** The searchable face of a picker option. PickerMenu's `PickerOption<T>` satisfies it. */\nexport interface Searchable {\n label: string;\n hint?: string;\n description?: string;\n}\n\n/**\n * Fuzzy fallback config. `ignoreLocation` because a term matching at the end of a\n * description is as good as one at the start, and Fuse's default distance window\n * would score the late match away. 0.5 is the loosest threshold that still bottoms\n * out: below it `gthb` misses `GitHub` entirely, which is the whole point of having\n * a fuzzy pass; above it the tail grows without surfacing anything new at the top.\n */\nexport const FUSE_OPTIONS: IFuseOptions<Searchable> = {\n keys: [\n { name: 'label', weight: 3 },\n { name: 'hint', weight: 1 },\n { name: 'description', weight: 1 },\n ],\n ignoreLocation: true,\n threshold: 0.5,\n includeScore: true,\n};\n\nexport function buildPickerIndex<T extends Searchable>(options: T[]): Fuse<T> {\n return new Fuse(options, FUSE_OPTIONS as IFuseOptions<T>);\n}\n\nfunction haystack(option: Searchable): string {\n return `${option.label} ${option.hint ?? ''} ${\n option.description ?? ''\n }`.toLowerCase();\n}\n\n/**\n * Rank options against a query, best first, or `null` for an empty query.\n *\n * Two passes, literal before fuzzy. Anything that literally contains every\n * whitespace-separated term wins outright, in the list's own order — that covers how\n * people actually type (`git iss` → `GitHub issues`) and keeps the common case as\n * tight as a plain substring filter, which fuzzy matching on its own is not: Fuse at\n * a threshold loose enough to forgive `gthb` also lets a third of the list through\n * on `replay`.\n *\n * Only when nothing matches literally does Fuse get a turn, so a typo degrades to a\n * ranked guess instead of an empty list. Terms are AND-ed there too, each matched\n * fuzzily on its own — Fuse's bitap pass over the whole query would otherwise need\n * `git iss` to be a near-edit of the text it's matching. Scores are distances (0 is\n * exact) and sum across terms, hence the ascending sort; ties keep the order the\n * first term put them in, since the sort is stable.\n */\nexport function rankOptions<T extends Searchable>(\n fuse: Fuse<T>,\n options: T[],\n query: string,\n): T[] | null {\n const terms = query.toLowerCase().split(/\\s+/).filter(Boolean);\n if (!terms.length) {\n return null;\n }\n\n const literal = options.filter((option) =>\n terms.every((term) => haystack(option).includes(term)),\n );\n if (literal.length) {\n return literal;\n }\n\n let surviving: Map<T, number> | null = null;\n for (const term of terms) {\n const hits = new Map<T, number>(\n fuse.search(term).map(({ item, score }) => [item, score ?? 0]),\n );\n if (surviving === null) {\n surviving = hits;\n continue;\n }\n // Deleting the current key mid-iteration is well-defined for a Map.\n for (const [option, total] of surviving) {\n const score = hits.get(option);\n if (score === undefined) {\n surviving.delete(option);\n } else {\n surviving.set(option, total + score);\n }\n }\n }\n\n return [...(surviving ?? [])]\n .sort(([, a], [, b]) => a - b)\n .map(([option]) => option);\n}\n","/**\n * PromptLabel — Compact inline label for input prompts.\n *\n * Renders: [!] message\n * where [!] is black text on accent background.\n */\n\nimport { Box, Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\n\ninterface PromptLabelProps {\n message?: string;\n}\n\nexport const PromptLabel = ({ message }: PromptLabelProps) => {\n // No message → no row, so label-less pickers don't get a stray blank line.\n if (!message) return null;\n return (\n <Box>\n <Text bold color={Colors.accent}>\n {' '}\n {message}\n </Text>\n </Box>\n );\n};\n","/**\n * ConfirmButton — the confirm row used to submit a selection.\n *\n * Pure render. Multi-select menus (PickerMenu mode=\"multi\", GroupedPickerMenu)\n * append this below their options as the final focusable row: the user toggles\n * options with enter, then arrows down onto this row and presses enter to\n * submit. This replaces the older \"enter anywhere submits\" pattern, which\n * confused people who expected enter to toggle the focused item.\n *\n * Renders flat, mirroring the option rows — a focus triangle and the label,\n * accent and bold when focused, dimmed otherwise — so it lines up under the\n * options instead of sitting in a separate boxed target.\n */\n\nimport { Text } from 'ink';\nimport { Icons, Colors } from '@ui/tui/styles';\n\ninterface ConfirmButtonProps {\n /** Button text. Defaults to \"Confirm\". */\n label?: string;\n /** Whether the cursor is currently on the button. */\n focused: boolean;\n /** Optional selected-item count, rendered as \"Label (n)\" when > 0. */\n count?: number;\n}\n\nexport const ConfirmButton = ({\n label = 'Confirm',\n focused,\n count,\n}: ConfirmButtonProps) => {\n const text = count && count > 0 ? `${label} (${count})` : label;\n return (\n <Text\n color={focused ? Colors.accent : undefined}\n bold={focused}\n dimColor={!focused}\n >\n {focused ? Icons.triangleSmallRight : ' '} {text}\n </Text>\n );\n};\n","/**\n * useStdoutDimensions — Returns [columns, rows] and re-renders on terminal resize.\n *\n * Ink's useStdout() does not subscribe to resize events, so layout only updates\n * when something else causes a re-render. This hook listens to the stream's\n * 'resize' event (Node TTY) and updates state so the component re-renders\n * with the new dimensions.\n */\n\nimport { useStdout } from 'ink';\nimport { useState, useEffect } from 'react';\n\nexport function useStdoutDimensions(): [number, number] {\n const { stdout } = useStdout();\n const [size, setSize] = useState<[number, number]>(() => [\n stdout.columns || 80,\n stdout.rows || 24,\n ]);\n\n useEffect(() => {\n const cols = stdout.columns || 80;\n const rows = stdout.rows || 24;\n setSize([cols, rows]);\n\n const stream = stdout as NodeJS.WriteStream & {\n on?(event: string, fn: () => void): void;\n };\n if (typeof stream.on !== 'function') return;\n\n const onResize = () => {\n const c = stdout.columns || 80;\n const r = stdout.rows || 24;\n if (c > 0 && r > 0) setSize([c, r]);\n };\n stream.on('resize', onResize);\n return () => {\n stream.off?.('resize', onResize);\n };\n }, [stdout]);\n\n return size;\n}\n","/**\n * Pure utility functions for keyboard hints — no React or Ink dependencies.\n * Extracted for testability in a node Jest environment.\n */\n\nexport interface KeyboardHint {\n label: string;\n action: string;\n priority: number;\n}\n\n/** Well-known key matches corresponding to Ink's key.* properties. */\nexport enum KeyMatch {\n UpArrow = 'upArrow',\n DownArrow = 'downArrow',\n LeftArrow = 'leftArrow',\n RightArrow = 'rightArrow',\n Return = 'return',\n Escape = 'escape',\n Space = 'space',\n Backspace = 'backspace',\n /**\n * Any single printable character typed without a modifier. Broad by design,\n * so a binding using it must sit last — the first match wins, which is what\n * lets a specific char binding still take precedence over free typing.\n */\n Printable = 'printable',\n}\n\n/** A key match: either a KeyMatch enum value or a literal character string (e.g. 'a', 's'). */\nexport type KeyMatchOrChar = KeyMatch | (string & NonNullable<unknown>);\n\n/** Default priorities by key type. */\nconst DEFAULT_PRIORITY: Record<string, number> = {\n [KeyMatch.UpArrow]: 0,\n [KeyMatch.DownArrow]: 0,\n [KeyMatch.LeftArrow]: 1,\n [KeyMatch.RightArrow]: 1,\n [KeyMatch.Space]: 10,\n [KeyMatch.Printable]: 12,\n [KeyMatch.Backspace]: 13,\n [KeyMatch.Escape]: 20,\n [KeyMatch.Return]: 21,\n};\n\n/** Get the default display priority for a key match. */\nexport function getDefaultPriority(\n match: KeyMatchOrChar | KeyMatchOrChar[],\n): number {\n const first = Array.isArray(match) ? match[0] : match;\n return DEFAULT_PRIORITY[first] ?? 15;\n}\n\n/** Check if a KeyMatchOrChar matches the given input string and key flags. */\nexport function matchesKey(\n m: KeyMatchOrChar,\n input: string,\n key: { [k: string]: unknown },\n): boolean {\n switch (m) {\n case KeyMatch.UpArrow:\n return !!key.upArrow;\n case KeyMatch.DownArrow:\n return !!key.downArrow;\n case KeyMatch.LeftArrow:\n return !!key.leftArrow;\n case KeyMatch.RightArrow:\n return !!key.rightArrow;\n case KeyMatch.Return:\n return !!key.return;\n case KeyMatch.Escape:\n return !!key.escape;\n case KeyMatch.Space:\n return input === ' ';\n case KeyMatch.Backspace:\n return !!key.backspace || !!key.delete;\n case KeyMatch.Printable:\n // Ink sets a flag for every non-text key while still passing a non-empty\n // `input`, so any flag but shift rules out typing.\n // Any single non-control character, not just ASCII — a filter query can\n // hold an accented or non-Latin letter.\n return (\n input.length === 1 &&\n input >= ' ' &&\n input !== '\\x7f' &&\n Object.entries(key).every(([flag, on]) => !on || flag === 'shift')\n );\n default:\n return input === m;\n }\n}\n\n/** Serialize hints for comparison. */\nexport function hintsKey(hints: KeyboardHint[]): string {\n return hints.map((h) => `${h.label}:${h.action}`).join('|');\n}\n\n/** Deduplicate hints by label+action and sort by priority. */\nexport function deduplicateAndSort(hints: KeyboardHint[]): KeyboardHint[] {\n const seen = new Set<string>();\n const deduped: KeyboardHint[] = [];\n for (const hint of hints) {\n const k = `${hint.label}:${hint.action}`;\n if (!seen.has(k)) {\n seen.add(k);\n deduped.push(hint);\n }\n }\n deduped.sort((a, b) => a.priority - b.priority);\n return deduped;\n}\n","/**\n * KeyboardHintsProvider — Context for collecting and displaying keyboard hints.\n *\n * Input components register their hints via useKeyBindings. The provider\n * flattens, deduplicates, and sorts them. The hints bar stays visible for as\n * long as a screen has registered hints — it never auto-dismisses.\n */\n\nimport {\n createContext,\n useCallback,\n useContext,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport {\n hintsKey,\n deduplicateAndSort,\n type KeyboardHint,\n} from './keyboard-hints-utils.js';\n\nexport type { KeyboardHint } from './keyboard-hints-utils.js';\n\ninterface KeyboardHintsContextValue {\n register(id: string, hints: KeyboardHint[]): void;\n unregister(id: string): void;\n hints: KeyboardHint[];\n}\n\nconst KeyboardHintsContext = createContext<KeyboardHintsContextValue>({\n register: () => undefined,\n unregister: () => undefined,\n hints: [],\n});\n\nexport const useKeyboardHintsContext = () => useContext(KeyboardHintsContext);\n\nexport const KeyboardHintsProvider = ({\n children,\n}: {\n children: ReactNode;\n}) => {\n const registrationsRef = useRef(new Map<string, KeyboardHint[]>());\n const [hints, setHints] = useState<KeyboardHint[]>([]);\n const prevHintsKeyRef = useRef('');\n\n const recompute = useCallback(() => {\n const all: KeyboardHint[] = [];\n for (const h of registrationsRef.current.values()) {\n all.push(...h);\n }\n const deduped = deduplicateAndSort(all);\n\n const newKey = hintsKey(deduped);\n if (newKey !== prevHintsKeyRef.current) {\n prevHintsKeyRef.current = newKey;\n setHints(deduped);\n }\n }, []);\n\n const register = useCallback(\n (id: string, h: KeyboardHint[]) => {\n registrationsRef.current.set(id, h);\n recompute();\n },\n [recompute],\n );\n\n const unregister = useCallback(\n (id: string) => {\n registrationsRef.current.delete(id);\n recompute();\n },\n [recompute],\n );\n\n return (\n <KeyboardHintsContext.Provider value={{ register, unregister, hints }}>\n {children}\n </KeyboardHintsContext.Provider>\n );\n};\n","/**\n * useKeyBindings — Declarative keyboard input + automatic hint registration.\n *\n * Replaces raw `useInput` in input components. Define bindings as data;\n * the hook wires up the Ink input handler AND registers hints in the\n * KeyboardHintsProvider. One source of truth for keys and their labels.\n */\n\nimport { useInput, type Key } from 'ink';\nimport { useEffect, useRef } from 'react';\nimport { useKeyboardHintsContext } from './useKeyboardHints.js';\nimport {\n matchesKey,\n getDefaultPriority,\n KeyMatch,\n type KeyboardHint,\n type KeyMatchOrChar,\n} from './keyboard-hints-utils.js';\n\nexport { KeyMatch };\nexport type { KeyMatchOrChar } from './keyboard-hints-utils.js';\n\nexport interface KeyBinding {\n /** Which key(s) trigger this binding. Array = multiple keys, one hint. */\n match: KeyMatchOrChar | KeyMatchOrChar[];\n /** Display label in hints bar (e.g. \"↑↓\", \"space\", \"enter\") */\n label: string;\n /** Description in hints bar (e.g. \"navigate\", \"toggle\") */\n action: string;\n /** Ordering priority (lower = further left). Defaults based on key type. */\n priority?: number;\n /** Handler called when the key matches. */\n handler: (input: string, key: Key) => void;\n}\n\n/**\n * Declarative key bindings hook. Replaces `useInput` in input components.\n * Registers hints automatically with the KeyboardHintsProvider.\n *\n * @param id Unique identifier for this component's hints registration\n * @param bindings Array of key binding definitions\n */\nexport function useKeyBindings(id: string, bindings: KeyBinding[]): void {\n const ctx = useKeyboardHintsContext();\n\n // Extract hints and register. Use a serialized key to avoid unnecessary updates.\n const hintsRef = useRef<string>('');\n const hints: KeyboardHint[] = bindings.map((b) => ({\n label: b.label,\n action: b.action,\n priority: b.priority ?? getDefaultPriority(b.match),\n }));\n const serialized = hints\n .map((h) => `${h.label}:${h.action}:${h.priority}`)\n .join('|');\n\n useEffect(() => {\n if (serialized !== hintsRef.current) {\n hintsRef.current = serialized;\n ctx.register(id, hints);\n }\n return () => ctx.unregister(id);\n // eslint-disable-next-line\n }, [id, serialized]);\n\n // Wire up input handling\n useInput((input, key) => {\n for (const binding of bindings) {\n const matches = Array.isArray(binding.match)\n ? binding.match\n : [binding.match];\n if (matches.some((m) => matchesKey(m, input, key))) {\n binding.handler(input, key);\n return;\n }\n }\n });\n}\n","/**\n * PickerMenu — Single and multi select.\n * Single mode: custom renderer with small triangle indicator; enter selects.\n * Multi mode: checkbox glyphs toggled with enter, plus a focusable\n * Confirm button below the options. The cursor moves onto the button and\n * enter submits — see MultiPickerMenu for the rationale.\n *\n * Key bindings are declared via useKeyBindings, which auto-registers\n * hints in the KeyboardHintsBar.\n */\n\nimport { Box, Text } from 'ink';\nimport { useEffect, useMemo, useState } from 'react';\nimport { buildPickerIndex, rankOptions } from './picker-filter.js';\nimport { Icons, Colors } from '@ui/tui/styles';\nimport { PromptLabel } from './PromptLabel.js';\nimport { ConfirmButton } from './ConfirmButton.js';\nimport { wordWrap } from './layout-helpers.js';\nimport { useStdoutDimensions } from '@ui/tui/hooks/useStdoutDimensions';\nimport {\n useKeyBindings,\n KeyMatch,\n type KeyBinding,\n type KeyMatchOrChar,\n} from '@ui/tui/hooks/useKeyBindings';\n\nexport interface PickerOption<T> {\n label: string;\n value: T;\n hint?: string;\n /**\n * Multi-select only: a secondary explanation rendered dimmed and wrapped on\n * its own line(s) beneath the label, for choices that need more than a title.\n * When unset, the row renders exactly as a label-only row.\n */\n description?: string;\n /** Glyph rendered before the label, in its own color — unaffected by\n * focus and disabled styling. */\n icon?: { glyph: string; color?: string };\n /** Dimmed and unselectable; navigation skips over it. */\n disabled?: boolean;\n /**\n * Section heading: unselectable (navigation skips it, like `disabled`) but\n * rendered as a bold header rather than a muted row. Set `disabled` too so\n * the skip logic treats it as non-selectable.\n */\n header?: boolean;\n /** Indent the label one level, e.g. items nested under a `header` row. */\n indent?: boolean;\n /**\n * Multi-select only: marks this option mutually exclusive with every other\n * option. Selecting it clears all other picks; selecting any non-exclusive\n * option clears it. Used e.g. for a browser connector that can't be\n * installed alongside local editors.\n */\n exclusive?: boolean;\n}\n\n/**\n * Step through a column's options in `dir`, wrapping, until an enabled\n * option is found. Returns `from` unchanged if the column is entirely\n * disabled.\n */\nfunction stepEnabled<T>(\n options: PickerOption<T>[],\n rows: number,\n from: number,\n dir: 1 | -1,\n): number {\n const col = Math.floor(from / rows);\n const colStart = col * rows;\n const colLen = Math.min(rows, options.length - colStart);\n let row = from % rows;\n for (let i = 0; i < colLen; i++) {\n row = (row + dir + colLen) % colLen;\n const idx = colStart + row;\n if (!options[idx]?.disabled) return idx;\n }\n return from;\n}\n\n/** First enabled option in [start, end), for initial focus and for entering\n * a page from the Confirm button. */\nfunction firstEnabled<T>(\n options: PickerOption<T>[],\n start = 0,\n end = options.length,\n): number {\n for (let i = start; i < end; i++) {\n if (!options[i]?.disabled) return i;\n }\n return start;\n}\n\n/** Last enabled option in [start, end), for wrapping from the button onto\n * the bottom of the page. */\nfunction lastEnabled<T>(\n options: PickerOption<T>[],\n start = 0,\n end = options.length,\n): number {\n for (let i = end - 1; i >= start; i--) {\n if (!options[i]?.disabled) return i;\n }\n return end - 1;\n}\n\n/**\n * Rows the surrounding screen or overlay consumes above and below a picker —\n * border + padding, title, prompt text, and the keyboard-hints bar. A\n * deliberately generous estimate: overshooting just shows a few fewer rows,\n * whereas undershooting lets a long list overflow the viewport (the bug this\n * windowing guards against). Mirrors GroupedPickerMenu's budgeting.\n */\nconst CHROME_OVERHEAD = 13;\n/**\n * Max visual rows a picker renders regardless of terminal height. Without a\n * ceiling a tall terminal lets a long list fill the whole viewport, which\n * reads as a wall of options; ~12 rows keeps the menu scannable and leaves\n * known breathing room above and below.\n */\nconst MAX_LIST_ROWS = 12;\n/** Extra rows a multi-select adds below its options: marginTop + Confirm button. */\nconst CONFIRM_CHROME = 3;\n/** Lists shorter than this never page — tall rows would split 2-3 options into pages of one. */\nconst MIN_COUNT_TO_PAGE = 5;\n/** Width the multi-select wraps option descriptions to (matches the render). */\nconst DESCRIPTION_WIDTH = 56;\n\ninterface PickerViewport {\n needsScroll: boolean;\n /** First option index on the current page. */\n start: number;\n /** One past the last option index on the current page. */\n end: number;\n hiddenAbove: number;\n hiddenBelow: number;\n /** Focus target one page over (wrapping), for the n/p keys. */\n pageStep: (focused: number, dir: 1 | -1) => number;\n}\n\n/**\n * Pages a single-column option list to the terminal height. The visible page\n * is derived from the focused index — no scroll state — so ↑/↓ flip pages as\n * focus crosses a page edge and n/p jump a whole page. Pages hold a fixed\n * option count sized to the tallest row (`rowCost`), trading a sparser page\n * on mixed-height lists for arithmetic-only paging. Engages only for\n * single-column pickers — multi-column grids already compress vertically.\n */\nfunction usePickerViewport(\n count: number,\n rowCost: number,\n chromeBelow: number,\n enabled: boolean,\n focused: number,\n): PickerViewport {\n const [, termRows] = useStdoutDimensions();\n const budget = Math.max(\n 5,\n Math.min(termRows - CHROME_OVERHEAD - chromeBelow, MAX_LIST_ROWS),\n );\n const needsScroll =\n enabled && count >= MIN_COUNT_TO_PAGE && count * rowCost > budget;\n // Reserve two rows for the \"↑/↓ N more\" indicators.\n const perPage = needsScroll\n ? Math.max(1, Math.floor((budget - 2) / rowCost))\n : count;\n const pageCount = Math.max(1, Math.ceil(count / perPage));\n const start = Math.floor(focused / perPage) * perPage;\n const end = Math.min(start + perPage, count);\n return {\n needsScroll,\n start,\n end,\n hiddenAbove: start,\n hiddenBelow: count - end,\n pageStep: (f, dir) =>\n ((Math.floor(f / perPage) + dir + pageCount) % pageCount) * perPage,\n };\n}\n\ninterface PickerMenuProps<T> {\n message?: string;\n options: PickerOption<T>[];\n mode?: 'single' | 'multi';\n centered?: boolean;\n columns?: 1 | 2 | 3 | 4;\n /**\n * Vertical space between options, in TUI rows. Defaults to 0 — i.e.\n * options stack tightly. Set to 1+ when the option labels are long\n * (wrap across multiple lines) or for visual breathing room.\n */\n optionMarginBottom?: number;\n /**\n * Filter-as-you-type. Defaults on once the list is long enough to page, where\n * finding one option means walking pages. Pass `false` to force it off.\n */\n filterable?: boolean;\n onSelect: (value: T | T[]) => void;\n}\n\n/** Lists at least this long get filtering. Shorter ones fit on a page or two,\n * where a filter row costs more rows than it saves. */\nconst FILTER_MIN_OPTIONS = 10;\n\nexport const PickerMenu = <T,>({\n message,\n options,\n mode = 'single',\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filterable,\n onSelect,\n}: PickerMenuProps<T>) => {\n const canFilter = filterable ?? options.length >= FILTER_MIN_OPTIONS;\n const [filter, setFilter] = useState('');\n const fuse = useMemo(\n () => (canFilter ? buildPickerIndex(options) : null),\n [options, canFilter],\n );\n const visible = useMemo(\n () => (fuse ? rankOptions(fuse, options, filter) ?? options : options),\n [fuse, options, filter],\n );\n\n // Keyed by label, and owned here rather than by MultiPickerMenu: filtering\n // rewrites the list under the ticks, and the remount below would drop them.\n const [selected, setSelected] = useState<Set<string>>(new Set());\n\n const shared = {\n message,\n options: visible,\n centered,\n optionMarginBottom,\n onSelect,\n filter: canFilter ? filter : null,\n setFilter,\n totalCount: options.length,\n };\n\n // A filtered list is a fresh list with its own focus and paging, so remount on\n // each query rather than leaving stale indices to be re-validated.\n const key = String(filter);\n\n if (mode === 'multi') {\n return (\n <MultiPickerMenu\n key={key}\n {...shared}\n columns={columns}\n allOptions={options}\n selected={selected}\n setSelected={setSelected}\n />\n );\n }\n\n return <SinglePickerMenu key={key} {...shared} columns={columns} />;\n};\n\n/**\n * Backspace and free typing. Append these last: `Printable` matches almost any\n * key, and bindings resolve in order, so anything specific has to be registered\n * first to keep working. Both share a label/action so the hints bar shows one.\n */\nfunction filterBindings(\n filter: string,\n setFilter: (query: string) => void,\n): KeyBinding[] {\n return [\n {\n match: KeyMatch.Backspace,\n label: 'type',\n action: 'filter',\n handler: () => setFilter(filter.slice(0, -1)),\n },\n {\n match: KeyMatch.Printable,\n label: 'type',\n action: 'filter',\n handler: (input: string) => setFilter(filter + input),\n },\n ];\n}\n\n/** The filter row above a filterable list: what's typed, and how much it left. */\nconst FilterRow = ({\n filter,\n shown,\n total,\n}: {\n filter: string;\n shown: number;\n total: number;\n}) => (\n // Indented to sit under PromptLabel, which carries a leading space.\n <Box paddingLeft={1}>\n <Text dimColor>\n {filter\n ? `Filter: ${filter} (${shown} of ${total})`\n : `Type to filter ${total} options`}\n </Text>\n </Box>\n);\n\n/** Custom single-select with triangle indicator and accent highlight. */\nconst SinglePickerMenu = <T,>({\n message,\n options,\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filter,\n setFilter,\n totalCount,\n onSelect,\n}: {\n message?: string;\n options: PickerOption<T>[];\n centered?: boolean;\n columns?: number;\n optionMarginBottom?: number;\n /** Current filter query, or null when this picker isn't filterable. */\n filter: string | null;\n setFilter: (query: string) => void;\n totalCount: number;\n onSelect: (value: T | T[]) => void;\n}) => {\n const [focused, setFocused] = useState(() => firstEnabled(options));\n const rows = Math.ceil(options.length / columns);\n // Single-select rows are label-only (no descriptions): one line plus margin.\n const viewport = usePickerViewport(\n options.length,\n 1 + optionMarginBottom,\n filter !== null ? 1 : 0,\n columns === 1,\n focused,\n );\n\n // Re-validate focus when the options change while mounted \\u2014 a list\n // that shrinks or disables entries can leave `focused` pointing at a\n // missing or disabled option, which would make enter a no-op.\n useEffect(() => {\n if (focused >= options.length || options[focused]?.disabled) {\n setFocused(firstEnabled(options));\n }\n }, [options, focused]);\n\n const bindings: KeyBinding[] = [\n {\n match: [KeyMatch.UpArrow, KeyMatch.DownArrow],\n label: '\\u2191\\u2193',\n action: 'navigate',\n handler: (_input, key) => {\n const dir = key.upArrow ? -1 : 1;\n // `start`/`end` derive from `focused`, so stepping past a page edge\n // flips the page rather than needing a bound here.\n setFocused(stepEnabled(options, rows, focused, dir));\n },\n },\n // Only on a non-filterable list. Once a filter row exists `n` and `p` are\n // characters, not commands — so they go for good, not just once a query is\n // typed, and the arrows carry paging instead by walking the whole list.\n ...(viewport.needsScroll && filter === null\n ? [\n {\n match: ['n', 'p'] as KeyMatchOrChar[],\n label: 'n/p',\n action: 'page',\n handler: (input: string) => {\n const target = viewport.pageStep(focused, input === 'n' ? 1 : -1);\n setFocused(\n options[target]?.disabled\n ? stepEnabled(options, rows, target, 1)\n : target,\n );\n },\n },\n ]\n : []),\n {\n match: KeyMatch.Return,\n label: 'enter',\n action: 'select',\n handler: () => {\n const selected = options[focused];\n if (selected && !selected.disabled) {\n onSelect(selected.value);\n }\n },\n },\n ];\n\n if (columns > 1) {\n bindings.splice(1, 0, {\n match: [KeyMatch.LeftArrow, KeyMatch.RightArrow],\n label: '\\u2190\\u2192',\n action: 'navigate',\n handler: (_input, key) => {\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n\n let next = focused;\n if (key.leftArrow) {\n const prevCol = col > 0 ? col - 1 : columns - 1;\n next = Math.min(prevCol * rows + row, options.length - 1);\n }\n if (key.rightArrow) {\n const nextCol = col < columns - 1 ? col + 1 : 0;\n next = Math.min(nextCol * rows + row, options.length - 1);\n }\n // Landing on a disabled option slides to the column's nearest\n // enabled one.\n if (options[next]?.disabled) {\n next = stepEnabled(options, rows, next, 1);\n }\n setFocused(next);\n },\n });\n }\n\n if (filter !== null) {\n bindings.push(...filterBindings(filter, setFilter));\n }\n\n useKeyBindings('single-picker', bindings);\n\n // Chunk options into columns (column-first ordering)\n const columnArrays: PickerOption<T>[][] = [];\n for (let c = 0; c < columns; c++) {\n columnArrays.push(options.slice(c * rows, c * rows + rows));\n }\n\n const align = centered ? 'center' : undefined;\n\n const renderOption = (opt: PickerOption<T>, flatIdx: number) => {\n const isFocused = flatIdx === focused;\n const base = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;\n const label = opt.indent ? ` ${base}` : base;\n return (\n <Box key={flatIdx} gap={1} marginBottom={optionMarginBottom}>\n <Text\n color={isFocused ? Colors.accent : undefined}\n dimColor={!isFocused}\n >\n {isFocused && !opt.header ? Icons.triangleSmallRight : ' '}\n </Text>\n {opt.icon && <Text color={opt.icon.color}>{opt.icon.glyph}</Text>}\n <Text\n color={\n opt.header\n ? undefined\n : opt.disabled\n ? Colors.muted\n : isFocused\n ? Colors.accent\n : undefined\n }\n bold={opt.header || (isFocused && !opt.disabled)}\n dimColor={!opt.header && (!isFocused || opt.disabled)}\n >\n {label}\n </Text>\n </Box>\n );\n };\n\n return (\n <Box flexDirection=\"column\" alignItems={align}>\n <PromptLabel message={message} />\n {filter !== null && (\n <FilterRow filter={filter} shown={options.length} total={totalCount} />\n )}\n {filter && options.length === 0 ? (\n <Text dimColor>No options match. Backspace to widen the filter.</Text>\n ) : viewport.needsScroll ? (\n <Box flexDirection=\"column\">\n <Text dimColor>\n {viewport.hiddenAbove > 0\n ? `↑ ${viewport.hiddenAbove} more${\n filter === null ? ' [P] for previous page' : ''\n }`\n : ' '}\n </Text>\n {options\n .slice(viewport.start, viewport.end)\n .map((opt, relIdx) => renderOption(opt, viewport.start + relIdx))}\n <Text dimColor>\n {viewport.hiddenBelow > 0\n ? `↓ ${viewport.hiddenBelow} more${\n filter === null ? ' [N] for next page' : ''\n }`\n : ' '}\n </Text>\n </Box>\n ) : (\n <Box flexDirection=\"row\" gap={4}>\n {columnArrays.map((colOpts, colIdx) => (\n <Box key={colIdx} flexDirection=\"column\">\n {colOpts.map((opt, rowIdx) =>\n renderOption(opt, colIdx * rows + rowIdx),\n )}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n );\n};\n\n/**\n * Custom multi-select with checkbox glyphs and accent highlight.\n *\n * Interaction model (shared with GroupedPickerMenu):\n * - \\u2191\\u2193 move the cursor through the options AND onto the Confirm button,\n * which lives just past the last option.\n * - enter toggles the focused option (no more \"space toggles but enter\n * advances\" split that tripped people up). Space is kept as an\n * undocumented alias, but the hints bar advertises only enter.\n * - moving onto the Confirm button and pressing enter submits the\n * current selection.\n */\nconst MultiPickerMenu = <T,>({\n message,\n options,\n centered = false,\n columns = 1,\n optionMarginBottom = 0,\n filter,\n setFilter,\n totalCount,\n allOptions,\n selected,\n setSelected,\n onSelect,\n}: {\n message?: string;\n options: PickerOption<T>[];\n centered?: boolean;\n columns?: number;\n optionMarginBottom?: number;\n /** Current filter query, or null when this picker isn't filterable. */\n filter: string | null;\n setFilter: (query: string) => void;\n totalCount: number;\n /** Every option, filtered or not. Ticks outlive the query, so resolving and\n * submitting them has to happen against the full list. */\n allOptions: PickerOption<T>[];\n selected: Set<string>;\n setSelected: (update: (prev: Set<string>) => Set<string>) => void;\n onSelect: (value: T | T[]) => void;\n}) => {\n const [focused, setFocused] = useState(() => firstEnabled(options));\n // When true, the cursor is on the Confirm button rather than an option.\n const [onButton, setOnButton] = useState(false);\n const rows = Math.ceil(options.length / columns);\n // A row is its label line plus any margin; a description adds one line per\n // wrapped line beneath the label. Pages size to the tallest row.\n const rowCost = options.reduce(\n (max, opt) =>\n Math.max(\n max,\n 1 +\n optionMarginBottom +\n (opt.description\n ? wordWrap(opt.description, DESCRIPTION_WIDTH).length\n : 0),\n ),\n 1,\n );\n const viewport = usePickerViewport(\n options.length,\n rowCost,\n CONFIRM_CHROME + (filter !== null ? 1 : 0),\n columns === 1,\n focused,\n );\n\n // Re-validate focus when the options change while mounted — a list\n // that shrinks or disables entries can leave `focused` pointing at a\n // missing or disabled option, which would make enter a no-op.\n useEffect(() => {\n if (focused >= options.length || options[focused]?.disabled) {\n setFocused(firstEnabled(options));\n }\n }, [options, focused]);\n\n const confirm = () => {\n // In `allOptions` order, so a pick made under one filter still submits\n // after the query moved on.\n const values = allOptions\n .filter((option) => selected.has(option.label))\n .map((option) => option.value);\n onSelect(values);\n };\n\n const bindings: KeyBinding[] = [\n {\n match: [KeyMatch.UpArrow, KeyMatch.DownArrow],\n label: '\\u2191\\u2193',\n action: 'navigate',\n handler: (_input, key) => {\n const dir = key.upArrow ? -1 : 1;\n if (onButton) {\n // Button \\u2192 back onto the current page (bottom for \\u2191, top for \\u2193).\n setOnButton(false);\n setFocused(\n dir === -1\n ? lastEnabled(options, viewport.start, viewport.end)\n : firstEnabled(options, viewport.start, viewport.end),\n );\n return;\n }\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n const colLen = Math.min(rows, options.length - col * rows);\n // Nearest enabled option above/below in this column; leaving the\n // column lands on the button.\n let r = row + dir;\n while (r >= 0 && r < colLen && options[col * rows + r]?.disabled) {\n r += dir;\n }\n if (r >= 0 && r < colLen) {\n setFocused(col * rows + r);\n } else {\n setOnButton(true);\n }\n },\n },\n // Only on a non-filterable list. Once a filter row exists `n` and `p` are\n // characters, not commands — so they go for good, not just once a query is\n // typed, and the arrows carry paging instead by walking the whole list.\n ...(viewport.needsScroll && filter === null\n ? [\n {\n match: ['n', 'p'] as KeyMatchOrChar[],\n label: 'n/p',\n action: 'page',\n handler: (input: string) => {\n const target = viewport.pageStep(focused, input === 'n' ? 1 : -1);\n setOnButton(false);\n setFocused(\n options[target]?.disabled\n ? stepEnabled(options, rows, target, 1)\n : target,\n );\n },\n },\n ]\n : []),\n {\n // Space is only an alias for enter when there's no filter to type into.\n match:\n filter !== null ? KeyMatch.Return : [KeyMatch.Space, KeyMatch.Return],\n label: 'enter',\n action: 'select',\n handler: () => {\n if (onButton) {\n confirm();\n return;\n }\n // `focused` can point past the end: filtering to zero matches leaves\n // nothing under the cursor, and enter is the natural thing to try there.\n const option = options[focused];\n if (!option || option.disabled) return;\n const label = option.label;\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(label)) {\n next.delete(label);\n return next;\n }\n // Enforce mutual exclusivity: an exclusive option clears every other\n // pick; any other option clears previously-picked exclusive ones.\n if (option.exclusive) {\n return new Set([label]);\n }\n // Against `allOptions`: the pick being cleared may be off-screen\n // under the current query.\n for (const picked of next) {\n if (allOptions.find((o) => o.label === picked)?.exclusive) {\n next.delete(picked);\n }\n }\n next.add(label);\n return next;\n });\n },\n },\n ];\n\n if (columns > 1) {\n bindings.splice(1, 0, {\n match: [KeyMatch.LeftArrow, KeyMatch.RightArrow],\n label: '\\u2190\\u2192',\n action: 'navigate',\n handler: (_input, key) => {\n if (onButton) return;\n const col = Math.floor(focused / rows);\n const row = focused % rows;\n\n let next = focused;\n if (key.leftArrow) {\n const prevCol = col > 0 ? col - 1 : columns - 1;\n next = Math.min(prevCol * rows + row, options.length - 1);\n }\n if (key.rightArrow) {\n const nextCol = col < columns - 1 ? col + 1 : 0;\n next = Math.min(nextCol * rows + row, options.length - 1);\n }\n // Landing on a disabled option slides to the column's nearest\n // enabled one.\n if (options[next]?.disabled) {\n next = stepEnabled(options, rows, next, 1);\n }\n setFocused(next);\n },\n });\n }\n\n if (filter !== null) {\n bindings.push(...filterBindings(filter, setFilter));\n }\n\n useKeyBindings('multi-picker', bindings);\n\n const columnArrays: PickerOption<T>[][] = [];\n for (let c = 0; c < columns; c++) {\n columnArrays.push(options.slice(c * rows, c * rows + rows));\n }\n\n const renderOption = (opt: PickerOption<T>, flatIdx: number) => {\n const isFocused = !onButton && flatIdx === focused;\n const isSelected = selected.has(opt.label);\n const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;\n const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;\n return (\n <Box\n key={flatIdx}\n flexDirection=\"column\"\n marginBottom={optionMarginBottom}\n >\n <Box gap={1}>\n <Text\n color={isSelected ? 'white' : Colors.muted}\n dimColor={!isFocused && !isSelected}\n >\n {checkbox}\n </Text>\n {opt.icon && <Text color={opt.icon.color}>{opt.icon.glyph}</Text>}\n <Text\n color={\n opt.disabled\n ? Colors.muted\n : isFocused\n ? Colors.accent\n : undefined\n }\n bold={isFocused && !opt.disabled}\n dimColor={!isFocused || opt.disabled}\n >\n {label}\n </Text>\n </Box>\n {/* Optional dimmed, wrapped explanation under the label. The explicit\n width forces Ink to wrap (an unconstrained Box shrinks to its\n content and never wraps). Renders only when set, so label-only rows\n are byte-for-byte unchanged. */}\n {opt.description && (\n <Box marginLeft={4} width={DESCRIPTION_WIDTH}>\n <Text dimColor wrap=\"wrap\">\n {opt.description}\n </Text>\n </Box>\n )}\n </Box>\n );\n };\n\n return (\n <Box flexDirection=\"column\" alignItems={centered ? 'center' : undefined}>\n <PromptLabel message={message} />\n {filter !== null && (\n <FilterRow filter={filter} shown={options.length} total={totalCount} />\n )}\n {filter && options.length === 0 && (\n <Text dimColor>No options match. Backspace to widen the filter.</Text>\n )}\n {viewport.needsScroll ? (\n <Box\n flexDirection=\"column\"\n marginLeft={centered ? 0 : 2}\n marginTop={message ? 1 : 0}\n >\n <Text dimColor>\n {viewport.hiddenAbove > 0\n ? `↑ ${viewport.hiddenAbove} more${\n filter === null ? ' [P] for previous page' : ''\n }`\n : ' '}\n </Text>\n {options\n .slice(viewport.start, viewport.end)\n .map((opt, relIdx) => renderOption(opt, viewport.start + relIdx))}\n <Text dimColor>\n {viewport.hiddenBelow > 0\n ? `↓ ${viewport.hiddenBelow} more${\n filter === null ? ' [N] for next page' : ''\n }`\n : ' '}\n </Text>\n </Box>\n ) : (\n <Box\n flexDirection=\"row\"\n gap={4}\n marginLeft={centered ? 0 : 2}\n marginTop={message ? 1 : 0}\n >\n {columnArrays.map((colOpts, colIdx) => (\n <Box key={colIdx} flexDirection=\"column\">\n {colOpts.map((opt, rowIdx) =>\n renderOption(opt, colIdx * rows + rowIdx),\n )}\n </Box>\n ))}\n </Box>\n )}\n <Box marginTop={1} marginLeft={centered ? 0 : 2}>\n <ConfirmButton focused={onButton} count={selected.size} />\n </Box>\n </Box>\n );\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"add-mcp-server-to-clients-DIaTRWMY.js","names":["fs","path","path","os","PLUGIN_MARKETPLACE","PLUGIN_MARKETPLACE_SOURCE","PLUGIN_REF","RUN_TIMEOUT_MS","MAX_OUTPUT_BYTES","path","os","fs","execSync","z","os","path","z","os","path","path","fs","z","path","fs","os"],"sources":["../src/steps/add-mcp-server-to-clients/MCPClient.ts","../src/steps/add-mcp-server-to-clients/clients/cursor.ts","../src/steps/add-mcp-server-to-clients/clients/claude-code.ts","../src/steps/add-mcp-server-to-clients/clients/claude-web.ts","../src/steps/add-mcp-server-to-clients/clients/visual-studio-code.ts","../src/steps/add-mcp-server-to-clients/clients/zed.ts","../src/steps/add-mcp-server-to-clients/clients/codex.ts","../src/steps/add-mcp-server-to-clients/clients/opencode.ts","../src/steps/add-mcp-server-to-clients/plugin-client.ts","../src/steps/add-mcp-server-to-clients/index.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport * as jsonc from 'jsonc-parser';\nimport { getDefaultServerConfig } from './defaults';\nimport type { InstallResult } from './results';\n\nexport type MCPServerConfig = Record<string, unknown>;\n\nexport abstract class MCPClient {\n name: string;\n abstract getConfigPath(): Promise<string>;\n abstract getServerPropertyName(): string;\n abstract isServerInstalled(local?: boolean): Promise<boolean>;\n abstract addServer(\n apiKey?: string,\n selectedFeatures?: string[],\n local?: boolean,\n ): Promise<InstallResult>;\n abstract removeServer(local?: boolean): Promise<InstallResult>;\n abstract isClientSupported(): Promise<boolean>;\n}\n\nexport abstract class DefaultMCPClient extends MCPClient {\n name = 'Default';\n\n constructor() {\n super();\n }\n\n getServerPropertyName(): string {\n return 'mcpServers';\n }\n\n getServerConfig(\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n ): MCPServerConfig {\n return getDefaultServerConfig(apiKey, selectedFeatures, local);\n }\n\n async isServerInstalled(local?: boolean): Promise<boolean> {\n try {\n const configPath = await this.getConfigPath();\n\n if (!fs.existsSync(configPath)) {\n return false;\n }\n\n const configContent = await fs.promises.readFile(configPath, 'utf8');\n const config = jsonc.parse(configContent) as Record<string, any>;\n const serverPropertyName = this.getServerPropertyName();\n const serverName = local ? 'posthog-local' : 'posthog';\n\n return (\n serverPropertyName in config && serverName in config[serverPropertyName]\n );\n } catch {\n return false;\n }\n }\n\n async addServer(\n apiKey?: string,\n selectedFeatures?: string[],\n local?: boolean,\n ): Promise<InstallResult> {\n try {\n const configPath = await this.getConfigPath();\n const configDir = path.dirname(configPath);\n\n await fs.promises.mkdir(configDir, { recursive: true });\n\n const serverPropertyName = this.getServerPropertyName();\n let configContent = '';\n let existingConfig = {};\n\n if (fs.existsSync(configPath)) {\n configContent = await fs.promises.readFile(configPath, 'utf8');\n existingConfig = jsonc.parse(configContent) || {};\n }\n\n const newServerConfig = this.getServerConfig(\n apiKey,\n selectedFeatures,\n local,\n );\n const typedConfig = existingConfig as Record<string, any>;\n if (!typedConfig[serverPropertyName]) {\n typedConfig[serverPropertyName] = {};\n }\n const serverName = local ? 'posthog-local' : 'posthog';\n\n // An identical entry means this config is already set up — leave the file\n // untouched and report it, so a re-run says \"already installed\" instead of\n // claiming a write that changed nothing. A differing entry (new features,\n // new key) still gets overwritten and reported as installed.\n const existingServerConfig = typedConfig[serverPropertyName][serverName];\n if (\n existingServerConfig !== undefined &&\n JSON.stringify(existingServerConfig) === JSON.stringify(newServerConfig)\n ) {\n return { success: true, alreadyInstalled: true };\n }\n\n typedConfig[serverPropertyName][serverName] = newServerConfig;\n\n const edits = jsonc.modify(\n configContent,\n [serverPropertyName, serverName],\n newServerConfig,\n {\n formattingOptions: {\n tabSize: 2,\n insertSpaces: true,\n },\n },\n );\n\n const modifiedContent = jsonc.applyEdits(configContent, edits);\n\n await fs.promises.writeFile(configPath, modifiedContent, 'utf8');\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n async removeServer(local?: boolean): Promise<InstallResult> {\n let configPath = '';\n try {\n configPath = await this.getConfigPath();\n\n if (!fs.existsSync(configPath)) {\n return { success: true, alreadyInstalled: true };\n }\n\n const configContent = await fs.promises.readFile(configPath, 'utf8');\n const config = jsonc.parse(configContent) as Record<string, any>;\n const serverPropertyName = this.getServerPropertyName();\n\n const serverName = local ? 'posthog-local' : 'posthog';\n\n if (\n serverPropertyName in config &&\n serverName in config[serverPropertyName]\n ) {\n const edits = jsonc.modify(\n configContent,\n [serverPropertyName, serverName],\n undefined,\n {\n formattingOptions: {\n tabSize: 2,\n insertSpaces: true,\n },\n },\n );\n\n const modifiedContent = jsonc.applyEdits(configContent, edits);\n\n await fs.promises.writeFile(configPath, modifiedContent, 'utf8');\n\n return { success: true };\n }\n // No PostHog entry to delete — the config changed under us between the\n // detection pass and now. Nothing failed, so don't report a failure.\n return { success: true, alreadyInstalled: true };\n } catch (error) {\n return {\n success: false,\n reason: `${configPath}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n };\n }\n }\n}\n","import {\n DefaultMCPClient,\n MCPServerConfig,\n} from '@steps/add-mcp-server-to-clients/MCPClient';\nimport * as path from 'path';\nimport * as os from 'os';\nimport {\n DefaultMCPClientConfig,\n getNativeHTTPServerConfig,\n} from '@steps/add-mcp-server-to-clients/defaults';\nimport { z } from 'zod';\n\nexport const CursorMCPConfig = DefaultMCPClientConfig;\n\nexport type CursorMCPConfig = z.infer<typeof DefaultMCPClientConfig>;\n\nexport class CursorMCPClient extends DefaultMCPClient {\n name = 'Cursor';\n\n constructor() {\n super();\n }\n\n async isClientSupported(): Promise<boolean> {\n return Promise.resolve(\n process.platform === 'darwin' || process.platform === 'win32',\n );\n }\n\n async getConfigPath(): Promise<string> {\n return Promise.resolve(path.join(os.homedir(), '.cursor', 'mcp.json'));\n }\n\n getServerConfig(\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n ): MCPServerConfig {\n return getNativeHTTPServerConfig(apiKey, selectedFeatures, local);\n }\n}\n","import { DefaultMCPClient } from '@steps/add-mcp-server-to-clients/MCPClient';\nimport {\n DefaultMCPClientConfig,\n buildMCPUrl,\n} from '@steps/add-mcp-server-to-clients/defaults';\nimport {\n PluginCapable,\n PluginInstallResult,\n} from '@steps/add-mcp-server-to-clients/plugin-client';\nimport {\n redactSecrets,\n scrubHomePaths,\n type InstallResult,\n} from '@steps/add-mcp-server-to-clients/results';\nimport { LoginCapable } from '@steps/add-mcp-server-to-clients/login-client';\nimport { z } from 'zod';\nimport { execSync, execFile } from 'child_process';\nimport { analytics } from '@utils/analytics';\nimport { debug } from '@utils/debug';\nimport * as os from 'os';\nimport * as path from 'path';\nimport * as fs from 'fs';\n\nexport const ClaudeCodeMCPConfig = DefaultMCPClientConfig;\n\nexport type ClaudeCodeMCPConfig = z.infer<typeof DefaultMCPClientConfig>;\n\n// `plugin install <name>` only resolves against marketplaces the user already\n// registered, and the wizard never opens Claude Code interactively — so without\n// the add below the install dies with `not found in any configured marketplace`.\n// Our own marketplace, not Anthropic's: that one carries us as a pinned sha, so\n// installing through it would gate plugin releases on Anthropic bumping the pin.\nconst PLUGIN_NAME = 'posthog';\nconst PLUGIN_MARKETPLACE = 'posthog';\nconst PLUGIN_MARKETPLACE_SOURCE = 'PostHog/ai-plugin';\nconst PLUGIN_REF = `${PLUGIN_NAME}@${PLUGIN_MARKETPLACE}`;\n\n/** The plugin is missing from every catalog the CLI can currently see. */\nconst NOT_IN_A_MARKETPLACE = /not found in/i;\n\n/** One `plugin list --json` row; the CLI returns more fields than we read. */\ninterface ListedPlugin {\n /** `<plugin>@<marketplace>`, e.g. `posthog@posthog`. */\n id: string;\n /**\n * A plugin disabled in `/plugin` stays listed with `enabled: false`. It is\n * still installed, but it contributes no MCP server, so treating it as\n * installed reports \"you are good to go\" with nothing serving posthog.\n */\n enabled?: boolean;\n}\n\ninterface ListedMarketplace {\n name: string;\n /**\n * `owner/repo`, present since the CLI started emitting `--json`. A\n * marketplace merely *named* `posthog` may be someone else's, and installing\n * from it would ship a different plugin under our name.\n */\n repo?: string;\n}\n\ntype ClaudeRun = { ok: boolean; output: string };\n\n/**\n * `plugin marketplace add` clones a git repository, the slowest thing we run.\n * Past this the command is not slow, it is stuck, and a clone waiting on a\n * credential prompt would otherwise hang the wizard with the spinner frozen.\n */\nconst RUN_TIMEOUT_MS = 120_000;\n\n/**\n * `execFile` kills the child once output passes this. The default is 1 MB,\n * which a verbose clone can reach, and the kill then looks like Claude Code\n * rejecting the install rather than us cutting it off.\n */\nconst MAX_OUTPUT_BYTES = 16 * 1024 * 1024;\n\n/**\n * Our catalog, by name *and* repository. A marketplace merely called `posthog`\n * may be someone else's, and neither installing from it nor removing it is\n * ours to do. `repo` is absent on CLIs predating it, where the name is all\n * there is.\n */\nconst isOurMarketplace = (m: ListedMarketplace | undefined): boolean =>\n m?.name === PLUGIN_MARKETPLACE &&\n (m.repo === undefined ||\n m.repo.toLowerCase() === PLUGIN_MARKETPLACE_SOURCE.toLowerCase());\n\nexport class ClaudeCodeMCPClient\n extends DefaultMCPClient\n implements PluginCapable, LoginCapable\n{\n name = 'Claude Code';\n private claudeBinaryPath: string | null = null;\n\n constructor() {\n super();\n }\n\n private findClaudeBinary(): string | null {\n if (this.claudeBinaryPath) {\n return this.claudeBinaryPath;\n }\n\n // Common installation paths for Claude Code CLI\n const possiblePaths = [\n path.join(os.homedir(), '.claude', 'local', 'claude'),\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\n for (const claudePath of possiblePaths) {\n if (fs.existsSync(claudePath)) {\n debug(` Found claude binary at: ${claudePath}`);\n this.claudeBinaryPath = claudePath;\n return claudePath;\n }\n }\n\n // Try PATH as fallback\n try {\n execSync('command -v claude', { stdio: 'pipe' });\n debug(' Found claude in PATH');\n this.claudeBinaryPath = 'claude';\n return 'claude';\n } catch {\n // Not in PATH\n }\n\n return null;\n }\n\n isClientSupported(): Promise<boolean> {\n try {\n debug(' Checking for Claude Code...');\n const claudeBinary = this.findClaudeBinary();\n\n if (!claudeBinary) {\n debug(' Claude Code not found. Installation paths checked:');\n debug(` - ${path.join(os.homedir(), '.claude', 'local', 'claude')}`);\n debug(` - /usr/local/bin/claude`);\n debug(` - /opt/homebrew/bin/claude`);\n debug(` - PATH`);\n return Promise.resolve(false);\n }\n\n const output = execSync(`${claudeBinary} --version`, { stdio: 'pipe' });\n const version = output.toString().trim();\n debug(` Claude Code detected: ${version}`);\n return Promise.resolve(true);\n } catch (error) {\n debug(\n ` Claude Code check failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return Promise.resolve(false);\n }\n }\n\n isServerInstalled(local?: boolean): Promise<boolean> {\n const binary = this.findClaudeBinary();\n if (!binary) return Promise.resolve(false);\n const serverName = local ? 'posthog-local' : 'posthog';\n // `mcp get <name>` exits non-zero when the entry doesn't exist. A substring\n // scan of `mcp list` also matches the claude.ai \"PostHog\" connector and the\n // plugin's `plugin:posthog:posthog`, reporting installed forever.\n try {\n execSync(`${binary} mcp get ${serverName}`, { stdio: 'pipe' });\n return Promise.resolve(true);\n } catch {\n return Promise.resolve(false);\n }\n }\n\n getConfigPath(): Promise<string> {\n throw new Error('Not implemented');\n }\n\n addServer(\n apiKey?: string,\n selectedFeatures?: string[],\n local?: boolean,\n ): Promise<InstallResult> {\n const binary = this.findClaudeBinary();\n if (!binary)\n return Promise.resolve({\n success: false,\n reason: 'The claude CLI is no longer on your PATH.',\n });\n\n const serverName = local ? 'posthog-local' : 'posthog';\n const url = buildMCPUrl(selectedFeatures, local);\n const args = [\n 'mcp',\n 'add',\n '--transport',\n 'http',\n '--scope',\n 'user',\n serverName,\n url,\n ];\n if (apiKey) {\n args.push('--header', `Authorization: Bearer ${apiKey}`);\n }\n\n try {\n execSync(`${binary} ${args.map((a) => JSON.stringify(a)).join(' ')}`, {\n stdio: 'pipe',\n });\n return Promise.resolve({ success: true });\n } catch (error) {\n // The failing command echoes back the Authorization header we passed, so\n // redact before this reaches a log, the screen, or an exception report.\n const msg = redactSecrets(\n error instanceof Error ? error.message : String(error),\n );\n if (msg.includes('already exists')) {\n return Promise.resolve({ success: true, alreadyInstalled: true });\n }\n analytics.captureException(\n new Error(`Claude Code MCP add failed: ${msg}`),\n );\n return Promise.resolve({ success: false, reason: msg });\n }\n }\n\n /** Claude Code's own login runs its OAuth and owns the token; the wizard only surfaces the command. */\n loginCommand(local?: boolean): string {\n return `claude mcp login ${local ? 'posthog-local' : 'posthog'}`;\n }\n\n /** The plugin bundles its own `posthog` server, addressed as `plugin:<plugin>:<server>`. */\n pluginLoginCommand(): string {\n return 'claude mcp login plugin:posthog:posthog';\n }\n\n async removePlugin(): Promise<PluginInstallResult> {\n const binary = this.findClaudeBinary();\n if (!binary)\n return {\n success: false,\n reason: 'The claude CLI is no longer on your PATH.',\n };\n\n // `plugin uninstall posthog` removes exactly one match, so with both\n // `posthog@posthog` and `posthog@claude-plugins-official` installed the bare\n // call leaves the other copy enabled — and `mcp remove` would be lying.\n const ids = await this.installedPluginIds(binary);\n // `installPlugin` registers the marketplace, so removal owes the user the\n // other half: a machine left with our catalog and no plugin is a state the\n // wizard created. `codex.ts` clears both on the same call.\n const marketplace = await this.ourMarketplaceRegistered(binary);\n\n const nothingInstalled =\n ids?.length === 0 || (ids === null && !(await this.isPluginInstalled()));\n if (nothingInstalled && !marketplace) {\n return { success: true, alreadyInstalled: true };\n }\n\n const targets = nothingInstalled ? [] : ids?.length ? ids : [PLUGIN_NAME];\n const failures: string[] = [];\n for (const target of targets) {\n const run = await this.runClaude(binary, ['plugin', 'uninstall', target]);\n if (!run.ok) failures.push(`${target}: ${run.output}`);\n }\n\n // Only when we know it is there: `marketplace remove` exits non-zero on a\n // marketplace that is absent, which would report a failure for a no-op.\n if (marketplace) {\n const run = await this.runClaude(binary, [\n 'plugin',\n 'marketplace',\n 'remove',\n PLUGIN_MARKETPLACE,\n ]);\n if (!run.ok) failures.push(`${PLUGIN_MARKETPLACE}: ${run.output}`);\n }\n\n if (failures.length === 0) return { success: true };\n\n const reason = failures.join('; ');\n analytics.captureException(\n new Error(`Claude Code plugin uninstall failed: ${reason}`),\n );\n return { success: false, reason };\n }\n\n removeServer(local?: boolean): Promise<InstallResult> {\n const claudeBinary = this.findClaudeBinary();\n if (!claudeBinary) {\n return Promise.resolve({\n success: false,\n reason: 'The claude CLI is no longer on your PATH.',\n });\n }\n\n const serverName = local ? 'posthog-local' : 'posthog';\n const command = `${claudeBinary} mcp remove --scope user ${serverName}`;\n\n try {\n execSync(command, { stdio: 'pipe' });\n } catch (error) {\n const reason = redactSecrets(\n error instanceof Error ? error.message : String(error),\n );\n // Removing something that isn't there is the requested end state, not a\n // failure to report.\n if (/no( such)? mcp server|not found/i.test(reason)) {\n return Promise.resolve({ success: true, alreadyInstalled: true });\n }\n analytics.captureException(\n new Error(`Failed to remove server from Claude Code: ${reason}`),\n );\n return Promise.resolve({ success: false, reason });\n }\n\n return Promise.resolve({ success: true });\n }\n\n /** The plugin ships mcp.json with the posthog server — no direct entry needed. */\n pluginBundlesMcpServer(): boolean {\n return true;\n }\n\n supportsPlugin(): boolean {\n return this.findClaudeBinary() !== null;\n }\n\n // Async on purpose: these calls clone git repos and take seconds, and\n // execSync would block the event loop and freeze the TUI spinner. Never\n // throws; the caller decides what a failure means.\n private runClaude(binary: string, args: string[]): Promise<ClaudeRun> {\n return new Promise((resolve) => {\n execFile(\n binary,\n args,\n { timeout: RUN_TIMEOUT_MS, maxBuffer: MAX_OUTPUT_BYTES },\n (error, stdout) => {\n if (!error)\n return resolve({ ok: true, output: stdout?.toString() ?? '' });\n if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER')\n return resolve({\n ok: false,\n output:\n 'claude produced more output than the wizard can hold, so it was stopped',\n });\n if (error.killed || error.signal)\n return resolve({\n ok: false,\n output: `claude did not finish within ${\n RUN_TIMEOUT_MS / 1000\n }s, so it was stopped`,\n });\n // execFile puts `Command failed: <cmd>` and stderr in the message, the\n // same text execSync produced, so failure matching is unchanged.\n resolve({\n ok: false,\n output: scrubHomePaths(redactSecrets(error.message)),\n });\n },\n );\n });\n }\n\n /** Null when `--json` is unsupported, the command failed, or output didn't parse — never \"nothing installed\". */\n private async listJson<T>(\n binary: string,\n args: string[],\n ): Promise<T[] | null> {\n const result = await this.runClaude(binary, [...args, '--json']);\n if (!result.ok) return null;\n try {\n const parsed: unknown = JSON.parse(result.output);\n return Array.isArray(parsed) ? (parsed as T[]) : null;\n } catch {\n return null;\n }\n }\n\n // Every installed copy, from any marketplace: Claude Code permits\n // `posthog@posthog` and `posthog@claude-plugins-official` at the same time.\n // Null means the probe is unavailable, which is not the same as none.\n private async installedPlugins(\n binary: string,\n ): Promise<ListedPlugin[] | null> {\n const listed = await this.listJson<ListedPlugin>(binary, [\n 'plugin',\n 'list',\n ]);\n if (!listed) return null;\n return listed.filter(\n (p): p is ListedPlugin =>\n typeof p?.id === 'string' && p.id.split('@')[0] === PLUGIN_NAME,\n );\n }\n\n private async installedPluginIds(binary: string): Promise<string[] | null> {\n const listed = await this.installedPlugins(binary);\n return listed?.map((p) => p.id) ?? null;\n }\n\n async isPluginInstalled(): Promise<boolean> {\n const binary = this.findClaudeBinary();\n if (!binary) return false;\n\n // Presence, not health: `index.ts` asks this to decide whether there is\n // anything to remove, and a disabled plugin still has to be uninstalled.\n // Whether it actually serves is `installPlugin`'s question, not this one.\n const listed = await this.installedPlugins(binary);\n if (listed) return listed.length > 0;\n\n // Older CLI without `--json`. A substring scan also matches a plugin merely\n // named `posthog-something`, which is why it's the fallback and not the path.\n const plain = await this.runClaude(binary, ['plugin', 'list']);\n return plain.ok && plain.output.toLowerCase().includes(PLUGIN_NAME);\n }\n\n async installPlugin(): Promise<PluginInstallResult> {\n const binary = this.findClaudeBinary();\n if (!binary)\n return {\n success: false,\n reason: 'The claude CLI is no longer on your PATH.',\n };\n\n // Ask before installing so a re-run reports \"already installed\" rather than\n // relying on the CLI's error text to spot the no-op.\n //\n // A plugin disabled in `/plugin` is installed but serves nothing, and\n // Claude Code takes its MCP server from the plugin. Reporting it as\n // already installed leaves the user with no server and a screen saying\n // they are done, so fall through and install: the CLI re-enables it.\n const listed = await this.installedPlugins(binary);\n const serving = listed\n ? listed.some((p) => p.enabled !== false)\n : await this.isPluginInstalled();\n if (serving) {\n return { success: true, alreadyInstalled: true };\n }\n\n const marketplaceFailure = await this.ensurePluginMarketplace(binary);\n\n let result = await this.runClaude(binary, [\n 'plugin',\n 'install',\n PLUGIN_REF,\n ]);\n\n // A registered but stale catalog still reports the plugin as missing.\n if (!result.ok && NOT_IN_A_MARKETPLACE.test(result.output)) {\n await this.runClaude(binary, [\n 'plugin',\n 'marketplace',\n 'update',\n PLUGIN_MARKETPLACE,\n ]);\n result = await this.runClaude(binary, ['plugin', 'install', PLUGIN_REF]);\n }\n\n // Last resort: let Claude Code resolve the bare name against whatever the\n // user does have, the way the wizard always did. Never worse than before.\n if (!result.ok && NOT_IN_A_MARKETPLACE.test(result.output)) {\n result = await this.runClaude(binary, ['plugin', 'install', PLUGIN_NAME]);\n }\n\n if (result.ok) return { success: true };\n\n const msg = result.output;\n if (msg.includes('already installed') || msg.includes('already exists')) {\n return { success: true, alreadyInstalled: true };\n }\n // `not found in any configured marketplace` is also what the pre-PR bug\n // produced, so without the marketplace-add failure beside it the new root\n // cause is indistinguishable from the old one in error tracking.\n const failure = new Error(`Claude Code plugin install failed: ${msg}`);\n if (marketplaceFailure) {\n analytics.captureException(failure, { marketplaceFailure });\n } else {\n analytics.captureException(failure);\n }\n return { success: false, reason: msg };\n }\n\n // Best-effort: a failure here only matters if the install also fails, since\n // the user may hold the plugin in another catalog that the fallback finds.\n /** Registered, and ours: a catalog merely named `posthog` is someone else's. */\n private async ourMarketplaceRegistered(\n binary: string,\n ): Promise<boolean | null> {\n const listed = await this.listJson<ListedMarketplace>(binary, [\n 'plugin',\n 'marketplace',\n 'list',\n ]);\n return listed ? listed.some(isOurMarketplace) : null;\n }\n\n private async ensurePluginMarketplace(\n binary: string,\n ): Promise<string | undefined> {\n const listed = await this.listJson<ListedMarketplace>(binary, [\n 'plugin',\n 'marketplace',\n 'list',\n ]);\n if (listed?.some(isOurMarketplace)) {\n debug(` Marketplace ${PLUGIN_MARKETPLACE} already registered`);\n return undefined;\n }\n\n // `marketplace add` is idempotent on Claude Code and exits 0 when the\n // marketplace is already on disk, so there's no \"already added\" text to\n // special-case here — unlike the Codex CLI, where it exits non-zero.\n const added = await this.runClaude(binary, [\n 'plugin',\n 'marketplace',\n 'add',\n PLUGIN_MARKETPLACE_SOURCE,\n ]);\n if (!added.ok) {\n debug(` Marketplace add failed: ${added.output}`);\n return added.output;\n }\n return undefined;\n }\n}\n","import { MCPClient } from '@steps/add-mcp-server-to-clients/MCPClient';\nimport type { InstallResult } from '@steps/add-mcp-server-to-clients/results';\nimport { BrowserFinishable } from '@steps/add-mcp-server-to-clients/browser-client';\nimport { openTrackedLink } from '@utils/links';\n\n/**\n * Claude Desktop / Claude.ai (web). PostHog ships here as a hosted connector,\n * not a local config — so instead of writing files we open the connector\n * directory page and let the user sign in and click \"Connect\".\n */\nexport class ClaudeWebMCPClient extends MCPClient implements BrowserFinishable {\n name = 'Claude Desktop/Web';\n connectorUrl = 'https://claude.ai/directory/connectors/posthog';\n finishInstruction = 'Sign in and click \"Connect\" to finish.';\n\n isClientSupported(): Promise<boolean> {\n // Browser-based — available on every platform.\n return Promise.resolve(true);\n }\n\n isServerInstalled(): Promise<boolean> {\n // The connector lives in the user's Claude account; nothing local to\n // inspect. Returning false also keeps it out of `mcp remove`.\n return Promise.resolve(false);\n }\n\n addServer(): Promise<InstallResult> {\n // Not a PostHog property, so no UTMs — just the tracked open.\n openTrackedLink(this.connectorUrl, 'claude-web-connector', {\n auto: true,\n skipUtm: true,\n });\n return Promise.resolve({ success: true });\n }\n\n removeServer(): Promise<InstallResult> {\n // The connector lives in the user's Claude account, so `mcp remove` can't\n // touch it. It never reaches here today (isServerInstalled is always\n // false), but say where to go if it ever does.\n return Promise.resolve({\n success: false,\n reason:\n 'This connector is managed in your Claude account — remove it at claude.ai/settings/connectors.',\n });\n }\n\n getConfigPath(): Promise<string> {\n throw new Error('Not implemented');\n }\n\n getServerPropertyName(): string {\n throw new Error('Not implemented');\n }\n}\n\nexport default ClaudeWebMCPClient;\n","import z from 'zod';\nimport * as path from 'path';\nimport * as os from 'os';\nimport {\n DefaultMCPClient,\n MCPServerConfig,\n} from '@steps/add-mcp-server-to-clients/MCPClient';\nimport { getNativeHTTPServerConfig } from '@steps/add-mcp-server-to-clients/defaults';\nimport { runtimeEnv } from '@env';\n\nexport const VisualStudioCodeMCPConfig = z\n .object({\n servers: z.record(\n z.string(),\n z.union([\n z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n }),\n z.object({\n type: z.enum(['http', 'sse']),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n }),\n ]),\n ),\n })\n .passthrough();\n\nexport type VisualStudioCodeMCPConfig = z.infer<\n typeof VisualStudioCodeMCPConfig\n>;\n\nexport class VisualStudioCodeClient extends DefaultMCPClient {\n name = 'Visual Studio Code';\n\n getServerPropertyName(): string {\n return 'servers';\n }\n\n async isClientSupported(): Promise<boolean> {\n return Promise.resolve(\n process.platform === 'darwin' ||\n process.platform === 'win32' ||\n process.platform === 'linux',\n );\n }\n\n async getConfigPath(): Promise<string> {\n const homeDir = os.homedir();\n const isWindows = process.platform === 'win32';\n const isMac = process.platform === 'darwin';\n const isLinux = process.platform === 'linux';\n\n if (isMac) {\n return Promise.resolve(\n path.join(\n homeDir,\n 'Library',\n 'Application Support',\n 'Code',\n 'User',\n 'mcp.json',\n ),\n );\n }\n\n if (isWindows) {\n return Promise.resolve(\n path.join(runtimeEnv('APPDATA') || '', 'Code', 'User', 'mcp.json'),\n );\n }\n\n if (isLinux) {\n return Promise.resolve(\n path.join(homeDir, '.config', 'Code', 'User', 'mcp.json'),\n );\n }\n\n throw new Error(`Unsupported platform: ${process.platform}`);\n }\n\n getServerConfig(\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n ): MCPServerConfig {\n return {\n type: 'http',\n ...getNativeHTTPServerConfig(apiKey, selectedFeatures, local),\n };\n }\n}\n","import z from 'zod';\nimport * as path from 'path';\nimport * as os from 'os';\nimport {\n DefaultMCPClient,\n MCPServerConfig,\n} from '@steps/add-mcp-server-to-clients/MCPClient';\nimport { getNativeHTTPServerConfig } from '@steps/add-mcp-server-to-clients/defaults';\nimport { runtimeEnv } from '@env';\n\nexport const ZedMCPConfig = z\n .object({\n context_servers: z.record(\n z.string(),\n z.union([\n z.object({\n enabled: z.boolean().optional(),\n source: z.string().optional(),\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n }),\n z.object({\n enabled: z.boolean().optional(),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n }),\n ]),\n ),\n })\n .passthrough();\n\nexport type ZedMCPConfig = z.infer<typeof ZedMCPConfig>;\n\nexport class ZedClient extends DefaultMCPClient {\n name = 'Zed';\n\n getServerPropertyName(): string {\n return 'context_servers';\n }\n\n async isClientSupported(): Promise<boolean> {\n return Promise.resolve(\n process.platform === 'darwin' || process.platform === 'linux',\n );\n }\n\n async getConfigPath(): Promise<string> {\n const homeDir = os.homedir();\n const isMac = process.platform === 'darwin';\n const isLinux = process.platform === 'linux';\n\n if (isMac) {\n return Promise.resolve(\n path.join(homeDir, '.config', 'zed', 'settings.json'),\n );\n }\n\n if (isLinux) {\n // https://zed.dev/docs/configuring-zed#settings-files\n const xdgConfigHome = runtimeEnv('XDG_CONFIG_HOME');\n if (xdgConfigHome) {\n return Promise.resolve(\n path.join(xdgConfigHome, 'zed', 'settings.json'),\n );\n }\n return Promise.resolve(\n path.join(homeDir, '.config', 'zed', 'settings.json'),\n );\n }\n\n throw new Error(`Unsupported platform: ${process.platform}`);\n }\n\n getServerConfig(\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n ): MCPServerConfig {\n return {\n enabled: true,\n ...getNativeHTTPServerConfig(apiKey, selectedFeatures, local),\n };\n }\n}\n","import { z } from 'zod';\nimport { execSync, execFile, type ExecFileException } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { LoginCapable } from '@steps/add-mcp-server-to-clients/login-client';\nimport { DefaultMCPClient } from '@steps/add-mcp-server-to-clients/MCPClient';\nimport {\n DefaultMCPClientConfig,\n buildMCPUrl,\n} from '@steps/add-mcp-server-to-clients/defaults';\nimport {\n PluginCapable,\n PluginInstallResult,\n} from '@steps/add-mcp-server-to-clients/plugin-client';\nimport {\n redactSecrets,\n scrubHomePaths,\n expectedFailureHint,\n type ExpectedFailure,\n type InstallResult,\n} from '@steps/add-mcp-server-to-clients/results';\n\nimport { analytics } from '@utils/analytics';\n\nconst PLUGIN_MARKETPLACE = 'posthog';\nconst PLUGIN_MARKETPLACE_SOURCE = 'PostHog/ai-plugin';\nconst PLUGIN_REF = `posthog@${PLUGIN_MARKETPLACE}`;\n\n/**\n * The plugin's row in `codex plugin list`, whose STATUS column reads\n * `installed, enabled` or `not installed`. Registering the marketplace only\n * publishes the catalog, so the marketplace section in config.toml says nothing\n * about whether the plugin itself is there.\n */\nconst listedAsInstalled = (stdout: string): boolean => {\n const row = new RegExp(`^${PLUGIN_REF}\\\\s+(.*)$`, 'm').exec(stdout)?.[1];\n return !!row && /\\binstalled\\b/.test(row) && !/\\bnot installed\\b/.test(row);\n};\n\n/**\n * What `reportSpawnFailure` was doing, for hint scoping and for the report.\n * A union rather than loose strings: a hint scoped to a stage nobody passes is\n * silently dead, which is how `marketplace add` lost its hint the first time.\n */\ntype CodexStage =\n | 'MCP add'\n | 'MCP remove'\n | 'plugin install'\n | 'plugin uninstall'\n | 'marketplace add';\n\nconst PLUGIN_STAGES: CodexStage[] = [\n 'plugin install',\n 'plugin uninstall',\n 'marketplace add',\n];\nconst MCP_STAGES: CodexStage[] = ['MCP add', 'MCP remove'];\nconst ALL_STAGES: CodexStage[] = [...PLUGIN_STAGES, ...MCP_STAGES];\n\n/**\n * Wording that proves the file codex choked on is the plugin's, not the user's.\n * Serde phrasing alone (\"invalid type\", \"is no longer supported\") says nothing\n * about whose file it came from, and blaming `~/.codex/config.toml` for our own\n * broken manifest sends the user to edit a file that is fine.\n */\nconst OUR_MANIFEST =\n /ai-plugin|plugin\\.toml|plugin\\.json|marketplace\\.toml|posthog@posthog/i;\n\n/**\n * Failures in the user's own environment. Reporting them files issues nobody\n * can action, so hand back a hint instead. Drawn from what the codex install\n * path actually produced in the field.\n *\n * Every entry is scoped: a hint that fires on the wrong stage tells the user to\n * run a command that has nothing to do with what just failed.\n */\nconst EXPECTED_FAILURES: ExpectedFailure[] = [\n {\n // `plugin`/`marketplace` subcommands a pre-plugin codex doesn't have.\n match:\n /unexpected argument|unknown (command|option|argument)|Missing option/i,\n stages: PLUGIN_STAGES,\n hint: 'your codex CLI is too old for plugins — update codex, then run `codex plugin marketplace add PostHog/ai-plugin`',\n },\n {\n // The same wording during `mcp add` is a flag we passed, not a plugin\n // subcommand, so the plugin advice above would send the user nowhere.\n match:\n /unexpected argument|unknown (command|option|argument)|Missing option/i,\n stages: MCP_STAGES,\n hint: 'your codex CLI is too old for this install — update codex, then run `npx @posthog/wizard mcp add` again',\n },\n {\n match: /ENOENT|not found in PATH|spawn .* ENOENT/i,\n stages: ALL_STAGES,\n hint: 'your codex install looks broken — reinstall codex, then run `codex plugin marketplace add PostHog/ai-plugin`',\n },\n {\n // Either codex says outright that it could not load its configuration, or\n // serde phrasing appears alongside `config.toml`. Bare serde wording cannot\n // tell the user's file from the plugin's, and `config.toml` on its own\n // silences any failure that merely mentions the path — both send someone to\n // edit a file that is fine, so the ambiguous case stays reportable.\n match:\n /failed to load (bootstrap )?configuration|OPENAI_API_KEY|Missing OpenAI API key|(?=[\\s\\S]*config\\.toml)[\\s\\S]*(invalid type|unknown field|is no longer supported|must contain at least one)/i,\n stages: ALL_STAGES,\n unless: OUR_MANIFEST,\n hint: 'codex could not read its own config — fix what it reports in ~/.codex/config.toml, then retry',\n },\n {\n match:\n /EACCES|EPERM|permission denied|read-only file system|not permitted/i,\n stages: ALL_STAGES,\n // git says `Permission denied (publickey)` when an SSH clone of the\n // marketplace cannot authenticate to GitHub. Nothing on ~/.codex is wrong\n // there, so the hint below would send the user to chmod a directory that\n // is already fine while the real cause goes unreported.\n unless: /permission denied \\((?:publickey|password|gssapi)/i,\n hint: 'codex could not write to its config — fix the permissions on ~/.codex, then retry',\n },\n {\n match: /ENOSPC|no space left/i,\n stages: ALL_STAGES,\n hint: 'the disk is full — free some space, then retry',\n },\n {\n // A clone that fails because the repo is missing is our publishing problem,\n // not the user's network, and telling them to check their connection buries\n // a renamed or deleted PostHog/ai-plugin where nobody will see it.\n match:\n /git clone .* failed|ENOTFOUND|ETIMEDOUT|ECONNREFUSED|could not resolve host/i,\n stages: ALL_STAGES,\n unless:\n /repository not found|not found|404|does not exist|authentication failed/i,\n hint: 'codex could not reach GitHub to download the plugin — check your network, then retry',\n },\n];\n\ninterface CodexRun {\n ok: boolean;\n /** Never blank on a failure: the cause, or the exit status when there is none. */\n output: string;\n}\n\n/**\n * A clone of the plugin marketplace is the slowest thing we run. Past this the\n * command is not slow, it is stuck, and the wizard should say so rather than\n * wait forever.\n */\nconst RUN_TIMEOUT_MS = 120_000;\n\n/**\n * `execFile` kills the child once output passes this. The default is 1 MB,\n * which a verbose clone can reach, and the kill then looks like codex rejecting\n * the install rather than us cutting it off.\n */\nconst MAX_OUTPUT_BYTES = 16 * 1024 * 1024;\n\n/**\n * What `execFile` puts in `error.message` when the command itself never spoke:\n * the full binary path and arguments. That path contains the user's home\n * directory, so reporting it fingerprints one root cause per machine — the\n * failure this reporting was built to stop. Never send it onward.\n */\nconst COMMAND_FAILED_PREAMBLE = /^Command failed:.*$/m;\n\n/**\n * The cause when neither stream carried one. `execFile` always sets a message,\n * so there is no \"empty error\" case to fall through — the message just isn't\n * usable as it stands.\n */\nconst describeExecFailure = (error: ExecFileException): string => {\n if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER')\n return 'codex produced more output than the wizard can hold, so it was stopped';\n if (error.killed)\n return `codex did not finish within ${\n RUN_TIMEOUT_MS / 1000\n }s, so it was stopped`;\n if (error.signal) return `codex was killed by ${error.signal}`;\n\n const said = error.message.replace(COMMAND_FAILED_PREAMBLE, '').trim();\n if (said) return said;\n if (typeof error.code === 'number')\n return `codex exited with code ${error.code}`;\n if (error.code) return `codex failed with ${error.code}`;\n return 'codex failed without reporting a reason';\n};\n\n/**\n * One codex invocation. Async on purpose: `plugin marketplace add` and\n * `plugin add` clone git repositories and take seconds, and a synchronous child\n * process blocks the event loop, which freezes the TUI spinner on its first\n * frame. Never throws; the caller decides what a failure means.\n *\n * A failure's cause is split across the error and the two streams, and none of\n * them carries it when the process merely exits non-zero — reading one alone\n * reported a blank reason and filed an exception carrying nothing.\n */\nconst runCodex = (\n binary: string,\n args: string[],\n env?: NodeJS.ProcessEnv,\n): Promise<CodexRun> =>\n new Promise((resolve) => {\n const child = execFile(\n binary,\n args,\n { env, timeout: RUN_TIMEOUT_MS, maxBuffer: MAX_OUTPUT_BYTES },\n (error, stdout, stderr) => {\n if (!error) return resolve({ ok: true, output: stdout ?? '' });\n // stderr first: it carries what codex actually said, and the TUI shows\n // the first line. `error.message` leads with our own invocation, so\n // preferring it would show the user the command instead of the cause.\n const said = [stderr, stdout]\n .map((p) => (p ? String(p).trim() : ''))\n .filter(Boolean)\n .join('\\n');\n // A kill is ours, not codex's, so it outranks whatever the streams got\n // out before we cut them off.\n const stopped =\n error.killed ||\n !!error.signal ||\n error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';\n resolve({\n ok: false,\n output: redactSecrets(\n stopped || !said ? describeExecFailure(error) : said,\n ),\n });\n },\n );\n // codex asks for nothing on stdin here. Leaving the pipe open means a\n // command that does ask waits on input that never comes, and the promise\n // never settles. `spawnSync` closed it for us.\n child.stdin?.end();\n });\n\n/**\n * Turn a failed spawn into a result: an expected local failure becomes a hint,\n * anything else is reported under a constant message so one root cause stays\n * one issue, with the varying detail in properties.\n */\nconst reportSpawnFailure = (\n stage: CodexStage,\n details: string,\n): InstallResult => {\n const hint = expectedFailureHint(details, EXPECTED_FAILURES, stage);\n if (hint) {\n // Hinting takes a failure out of error tracking, so without this the only\n // evidence a pattern has started over-matching is that our exception count\n // fell, which reads as the fix working. An event keeps the count.\n analytics.wizardCapture('mcp expected failure hinted', {\n client: 'Codex',\n stage,\n hint,\n details: scrubHomePaths(details),\n });\n return { success: false, reason: hint };\n }\n analytics.captureException(new Error(`Codex ${stage} failed`), {\n stage,\n details: scrubHomePaths(details),\n });\n return { success: false, reason: details };\n};\n\n/** Wording codex uses when the thing we're adding is already registered. */\nconst ALREADY_INSTALLED_PATTERN =\n /already (installed|exists|added|registered)/i;\n/** Wording that means the opposite: a cache entry exists but the plugin doesn't. */\nconst STALE_MARKETPLACE_CACHE = /already added from a different source/i;\n\n/**\n * Codex allows servers 10s to finish the MCP initialize handshake, and a remote\n * OAuth handshake can exceed that on a cold start — the \"servers were not\n * initialized\" warning.\n */\nconst STARTUP_TIMEOUT_SEC = 30;\n\n/**\n * The server's table header. TOML accepts a bare or quoted key for the same\n * table, and matching only the bare form means we append a second definition of\n * a table that already exists — a duplicate-key error that takes the user's\n * whole config down, not just PostHog's entry.\n */\nconst sectionHeader = (serverName: string): RegExp =>\n new RegExp(\n `^\\\\[mcp_servers\\\\.(?:${serverName}|\"${serverName}\")\\\\][ \\\\t]*$`,\n 'm',\n );\n\n/**\n * Set `key = value` inside a section body, inserting it when absent. Scans the\n * whole body rather than the line after the header: TOML does not care about\n * key order, and assuming it does means silently matching nothing.\n */\nconst setKey = (body: string, key: string, value: string): string => {\n const existing = new RegExp(`^[ \\\\t]*${key}[ \\\\t]*=.*$`, 'm');\n return existing.test(body)\n ? body.replace(existing, `${key} = ${value}`)\n : `\\n${key} = ${value}${body}`;\n};\n\nexport const CodexMCPConfig = DefaultMCPClientConfig;\n\nexport type CodexMCPConfig = z.infer<typeof DefaultMCPClientConfig>;\n\nexport class CodexMCPClient\n extends DefaultMCPClient\n implements PluginCapable, LoginCapable\n{\n name = 'Codex';\n private codexBinaryPath: string | null = null;\n\n constructor() {\n super();\n }\n\n private findCodexBinary(): string | null {\n if (this.codexBinaryPath) return this.codexBinaryPath;\n try {\n const resolved = execSync('command -v codex', { stdio: 'pipe' })\n .toString()\n .trim();\n if (resolved) {\n this.codexBinaryPath = resolved;\n return resolved;\n }\n } catch {\n // not in PATH\n }\n return null;\n }\n\n private configPath(): string {\n return path.join(os.homedir(), '.codex', 'config.toml');\n }\n\n isClientSupported(): Promise<boolean> {\n return Promise.resolve(this.findCodexBinary() !== null);\n }\n\n getConfigPath(): Promise<string> {\n throw new Error('Not implemented');\n }\n\n isServerInstalled(local?: boolean): Promise<boolean> {\n const serverName = local ? 'posthog-local' : 'posthog';\n // The `[mcp_servers.<name>]` section in config.toml — both the CLI and the\n // desktop app read (and `codex mcp add` writes) this file, and a substring\n // scan of `mcp list` matches unrelated posthog-ish servers.\n try {\n const contents = fs.readFileSync(this.configPath(), 'utf-8');\n return Promise.resolve(sectionHeader(serverName).test(contents));\n } catch {\n return Promise.resolve(false);\n }\n }\n\n async addServer(\n apiKey?: string,\n selectedFeatures?: string[],\n local?: boolean,\n ): Promise<InstallResult> {\n const serverName = local ? 'posthog-local' : 'posthog';\n const url = buildMCPUrl(selectedFeatures, local);\n\n // Api-key installs go through the CLI for its bearer-token env wiring.\n if (apiKey) {\n const binary = this.findCodexBinary();\n if (!binary)\n return {\n success: false,\n reason: 'An API-key install into Codex needs the codex CLI.',\n };\n const args = [\n 'mcp',\n 'add',\n serverName,\n '--url',\n url,\n '--bearer-token-env-var',\n 'POSTHOG_AUTH_HEADER',\n ];\n const env = { ...process.env, POSTHOG_AUTH_HEADER: `Bearer ${apiKey}` };\n const result = await runCodex(binary, args, env);\n if (!result.ok) {\n if (ALREADY_INSTALLED_PATTERN.test(result.output)) {\n return { success: true, alreadyInstalled: true };\n }\n return reportSpawnFailure('MCP add', result.output);\n }\n return { success: true };\n }\n\n // OAuth installs write config.toml directly: running `codex mcp add` here\n // would hang the wizard on its built-in OAuth browser wait. Codex nags\n // about the unauthenticated server until the surfaced `codex mcp login`\n // runs — the same interim state as Claude Code's \"needs authentication\".\n return this.writeServerSection(serverName, url);\n }\n\n /**\n * Create or update the `[mcp_servers.<name>]` section in config.toml, editing\n * in place so the user's other servers, key order, and comments survive.\n */\n private writeServerSection(serverName: string, url: string): InstallResult {\n const configPath = this.configPath();\n try {\n // A machine that has codex installed but has never run it has no\n // ~/.codex at all — `codex mcp add` used to create it for us.\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const contents = fs.existsSync(configPath)\n ? fs.readFileSync(configPath, 'utf-8')\n : '';\n\n const header = sectionHeader(serverName).exec(contents);\n if (!header) {\n const gap = contents === '' || contents.endsWith('\\n\\n') ? '' : '\\n';\n const pad = contents === '' || contents.endsWith('\\n') ? '' : '\\n';\n this.write(\n configPath,\n `${contents}${pad}${gap}[mcp_servers.${serverName}]\\n` +\n `url = \"${url}\"\\nstartup_timeout_sec = ${STARTUP_TIMEOUT_SEC}\\n`,\n );\n return { success: true };\n }\n\n // Everything up to the next table header belongs to this server.\n const start = header.index + header[0].length;\n const rest = contents.slice(start);\n const next = /^\\[/m.exec(rest);\n const end = next ? start + next.index : contents.length;\n\n const body = contents.slice(start, end);\n const updated = setKey(\n setKey(body, 'url', `\"${url}\"`),\n 'startup_timeout_sec',\n String(STARTUP_TIMEOUT_SEC),\n );\n if (updated === body) return { success: true, alreadyInstalled: true };\n\n this.write(\n configPath,\n contents.slice(0, start) + updated + contents.slice(end),\n );\n return { success: true };\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n // Constant message, varying detail in properties: the reason carries the\n // config path, so interpolating it filed one issue per user.\n analytics.captureException(new Error('Codex config.toml write failed'), {\n details: scrubHomePaths(reason),\n });\n return { success: false, reason };\n }\n }\n\n /** Write via a sibling temp file: a crash mid-write must not truncate the config. */\n private write(configPath: string, contents: string): void {\n const tmp = `${configPath}.wizard-tmp`;\n fs.writeFileSync(tmp, contents);\n fs.renameSync(tmp, configPath);\n }\n\n /** Codex's own login runs its OAuth and owns the token; the wizard only surfaces the command. */\n loginCommand(local?: boolean): string | null {\n if (!this.findCodexBinary()) return null;\n return `codex mcp login ${local ? 'posthog-local' : 'posthog'}`;\n }\n\n async removeServer(local?: boolean): Promise<InstallResult> {\n const binary = this.findCodexBinary();\n if (!binary)\n return {\n success: false,\n reason: 'The codex CLI is no longer on your PATH.',\n };\n\n // `local` was ignored here, so `mcp remove --local` reported success while\n // leaving the posthog-local server in place.\n const serverName = local ? 'posthog-local' : 'posthog';\n const result = await runCodex(binary, ['mcp', 'remove', serverName]);\n\n if (!result.ok) {\n return reportSpawnFailure('MCP remove', result.output);\n }\n\n return { success: true };\n }\n\n /** The codex marketplace plugin ships skills only — the MCP server needs its own entry. */\n pluginBundlesMcpServer(): boolean {\n return false;\n }\n\n supportsPlugin(): boolean {\n return this.findCodexBinary() !== null;\n }\n\n /** The plugin itself, as the CLI reports it. The install decision's question. */\n private async isPluginPresent(): Promise<boolean> {\n const binary = this.findCodexBinary();\n if (!binary) return false;\n const result = await runCodex(binary, [\n 'plugin',\n 'list',\n '-m',\n PLUGIN_MARKETPLACE,\n ]);\n return result.ok && listedAsInstalled(result.output);\n }\n\n /**\n * Anything of ours left on the machine, which is what `index.ts` asks before\n * offering removal. A registered marketplace with no plugin is a state the\n * wizard created — and the state this branch exists to correct — so removal\n * has to see it, or `removePlugin` never runs and the catalog stays forever.\n */\n async isPluginInstalled(): Promise<boolean> {\n return (await this.isPluginPresent()) || this.isMarketplaceRegistered();\n }\n\n /** The catalog, which the plugin is installed *from* — not the plugin. */\n private isMarketplaceRegistered(): boolean {\n try {\n const contents = fs.readFileSync(this.configPath(), 'utf-8');\n return contents\n .toLowerCase()\n .includes(`[marketplaces.${PLUGIN_MARKETPLACE}]`);\n } catch {\n return false;\n }\n }\n\n async removePlugin(): Promise<PluginInstallResult> {\n const binary = this.findCodexBinary();\n if (!binary)\n return {\n success: false,\n reason: 'The codex CLI is no longer on your PATH.',\n };\n\n // Both halves are removed, and either alone is enough to act on: a user\n // left with a registered marketplace and no plugin still needs it cleared.\n const steps: string[][] = [];\n if (await this.isPluginPresent())\n steps.push(['plugin', 'remove', PLUGIN_REF]);\n if (this.isMarketplaceRegistered())\n steps.push(['plugin', 'marketplace', 'remove', PLUGIN_MARKETPLACE]);\n\n if (steps.length === 0) return { success: true, alreadyInstalled: true };\n\n for (const args of steps) {\n const result = await runCodex(binary, args);\n if (!result.ok) {\n return reportSpawnFailure('plugin uninstall', result.output);\n }\n }\n return { success: true };\n }\n\n async installPlugin(): Promise<PluginInstallResult> {\n const binary = this.findCodexBinary();\n if (!binary)\n return {\n success: false,\n reason: 'The codex CLI is no longer on your PATH.',\n };\n\n // The plugin, not the catalog: a registered marketplace with no plugin is\n // exactly the broken state this installs over, so asking the wider question\n // here would skip the install and leave it broken.\n if (await this.isPluginPresent()) {\n return { success: true, alreadyInstalled: true };\n }\n\n // `codex plugin marketplace add` exits non-zero once the marketplace is\n // registered, so ask config.toml first. Without this the second run of\n // `mcp add` looked like a failure and reported nothing at all.\n const marketplace = this.isMarketplaceRegistered()\n ? null\n : await this.registerMarketplace(binary);\n if (marketplace && !marketplace.success) return marketplace;\n\n // Registering the catalog does not install from it. Skipping this left the\n // user with a marketplace, no skills, and a wizard reporting success.\n const result = await runCodex(binary, ['plugin', 'add', PLUGIN_REF]);\n\n if (!result.ok) {\n // The listing above can report \"not installed\" when it merely failed to\n // run, and every other spawn here reads codex's own wording rather than\n // turning a no-op into a reported failure.\n if (ALREADY_INSTALLED_PATTERN.test(result.output)) {\n return { success: true, alreadyInstalled: true };\n }\n return reportSpawnFailure('plugin install', result.output);\n }\n\n return { success: true };\n }\n\n /**\n * Add the catalog the plugin is published in. A stale cache directory with no\n * config.toml entry reports the marketplace as added from a different source;\n * clear it and retry once.\n */\n private async registerMarketplace(\n binary: string,\n ): Promise<PluginInstallResult> {\n const run = () =>\n runCodex(binary, [\n 'plugin',\n 'marketplace',\n 'add',\n PLUGIN_MARKETPLACE_SOURCE,\n ]);\n\n let result = await run();\n\n if (!result.ok && STALE_MARKETPLACE_CACHE.test(result.output)) {\n const staleDir = path.join(\n os.homedir(),\n '.codex',\n '.tmp',\n 'marketplaces',\n PLUGIN_MARKETPLACE,\n );\n try {\n fs.rmSync(staleDir, { recursive: true, force: true });\n } catch {\n // ignore — retry anyway\n }\n result = await run();\n }\n\n if (!result.ok) {\n const details = result.output;\n // Registered by something other than us — the plugin add below can still\n // resolve against it. The stale-cache wording is excluded: that one means\n // the marketplace is NOT usable, and only reaches here if the retry failed.\n if (\n ALREADY_INSTALLED_PATTERN.test(details) &&\n !STALE_MARKETPLACE_CACHE.test(details)\n ) {\n return { success: true };\n }\n return reportSpawnFailure('marketplace add', details);\n }\n return { success: true };\n }\n}\n\nexport default CodexMCPClient;\n","import z from 'zod';\nimport * as path from 'path';\nimport * as os from 'os';\nimport * as fs from 'fs';\nimport { execSync } from 'node:child_process';\nimport {\n DefaultMCPClient,\n MCPServerConfig,\n} from '@steps/add-mcp-server-to-clients/MCPClient';\nimport { getNativeHTTPServerConfig } from '@steps/add-mcp-server-to-clients/defaults';\nimport { runtimeEnv } from '@env';\n\nexport const OpenCodeMCPConfig = z\n .object({\n mcp: z.record(\n z.string(),\n z.object({\n type: z.literal('remote'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n }),\n ),\n })\n .passthrough();\n\nexport type OpenCodeMCPConfig = z.infer<typeof OpenCodeMCPConfig>;\n\nexport class OpenCodeMCPClient extends DefaultMCPClient {\n name = 'OpenCode';\n private opencodeBinaryPath: string | null = null;\n\n override getServerPropertyName(): string {\n return 'mcp';\n }\n\n private findOpenCodeBinary(): string | null {\n if (this.opencodeBinaryPath) return this.opencodeBinaryPath;\n try {\n const resolved = execSync('command -v opencode', { stdio: 'pipe' })\n .toString()\n .trim();\n if (resolved) {\n this.opencodeBinaryPath = resolved;\n return resolved;\n }\n } catch {\n // not in PATH\n }\n return null;\n }\n\n isClientSupported(): Promise<boolean> {\n return Promise.resolve(this.findOpenCodeBinary() !== null);\n }\n\n getConfigPath(): Promise<string> {\n const configDir = this.resolveConfigDir();\n const jsoncPath = path.join(configDir, 'opencode.jsonc');\n const jsonPath = path.join(configDir, 'opencode.json');\n if (fs.existsSync(jsoncPath)) return Promise.resolve(jsoncPath);\n return Promise.resolve(jsonPath);\n }\n\n private resolveConfigDir(): string {\n const envDir = runtimeEnv('OPENCODE_CONFIG_DIR');\n if (envDir) return envDir;\n\n const xdgHome = runtimeEnv('XDG_CONFIG_HOME');\n if (xdgHome) return path.join(xdgHome, 'opencode');\n\n return path.join(os.homedir(), '.config', 'opencode');\n }\n\n override getServerConfig(\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n ): MCPServerConfig {\n return {\n type: 'remote',\n ...getNativeHTTPServerConfig(apiKey, selectedFeatures, local),\n };\n }\n}\n","import type { InstallResult } from './results';\n\nexport type PluginInstallResult = InstallResult;\n\nexport interface PluginCapable {\n supportsPlugin(): boolean;\n isPluginInstalled(): Promise<boolean>;\n installPlugin(): Promise<PluginInstallResult>;\n /** Uninstall the plugin. Absent when the client CLI has no uninstall surface. */\n removePlugin?(): Promise<PluginInstallResult>;\n /** True when the plugin ships its own posthog MCP server, making a direct entry redundant. */\n pluginBundlesMcpServer(): boolean;\n}\n\nexport function isPluginCapable<T>(client: T): client is T & PluginCapable {\n return (\n typeof client === 'object' &&\n client !== null &&\n 'supportsPlugin' in client &&\n 'installPlugin' in client\n );\n}\n","import type { Integration } from '@shared/constants';\nimport type { CloudRegion } from '@utils/types';\nimport { withProgress } from '@utils/telemetry';\nimport { analytics } from '@utils/analytics';\nimport { getUI } from '@ui';\nimport { MCPClient } from './MCPClient';\nimport { CursorMCPClient } from './clients/cursor';\nimport { ClaudeCodeMCPClient } from './clients/claude-code';\nimport { ClaudeWebMCPClient } from './clients/claude-web';\nimport { VisualStudioCodeClient } from './clients/visual-studio-code';\nimport { ZedClient } from './clients/zed';\nimport { CodexMCPClient } from './clients/codex';\nimport { OpenCodeMCPClient } from './clients/opencode';\nimport { ALL_FEATURE_VALUES } from './defaults';\nimport { debug } from '@utils/debug';\nimport { isPluginCapable, PluginCapable } from './plugin-client';\nimport {\n McpClientStatus,\n namesWithStatus,\n toClientResult,\n type McpClientResult,\n} from './results';\n\nexport const getSupportedClients = async (): Promise<MCPClient[]> => {\n const allClients = [\n new ClaudeCodeMCPClient(),\n new ClaudeWebMCPClient(),\n new CodexMCPClient(),\n new CursorMCPClient(),\n new VisualStudioCodeClient(),\n new ZedClient(),\n new OpenCodeMCPClient(),\n ];\n const supportedClients: MCPClient[] = [];\n\n debug('Checking for supported MCP clients...');\n for (const client of allClients) {\n const isSupported = await client.isClientSupported();\n debug(`${client.name}: ${isSupported ? '✓ supported' : '✗ not supported'}`);\n if (isSupported) {\n supportedClients.push(client);\n }\n }\n debug(\n `Found ${supportedClients.length} supported client(s): ${supportedClients\n .map((c) => c.name)\n .join(', ')}`,\n );\n\n return supportedClients;\n};\n\n/** Per-client outcome, so a scripted caller can turn a failure into an exit code. */\nexport interface McpStepOutcome {\n /** Clients that ended up with the server, whether we wrote it or it was already there. */\n installed: string[];\n failed: string[];\n}\n\n/**\n * Add MCP server to clients. No prompts — pure orchestration.\n * Prompts are handled by McpScreen (TUI) or auto-accepted (CI).\n */\nexport const addMCPServerToClientsStep = async ({\n integration,\n local = false,\n ci = false,\n cloudRegion: _cloudRegion,\n features,\n apiKey,\n}: {\n integration?: Integration;\n local?: boolean;\n ci?: boolean;\n cloudRegion?: CloudRegion;\n features?: string[];\n apiKey?: string;\n}): Promise<McpStepOutcome> => {\n const ui = getUI();\n\n // CI mode: skip MCP installation entirely\n if (ci) {\n ui.log.info('Skipping MCP installation (CI mode)');\n return { installed: [], failed: [] };\n }\n\n const supportedClients = await getSupportedClients();\n\n if (supportedClients.length === 0) {\n ui.log.info(\n 'No supported MCP clients detected. Skipping MCP installation.',\n );\n return { installed: [], failed: [] };\n }\n\n // Auto-install to all supported clients\n const results = await withProgress('adding mcp servers', () =>\n addMCPServer(\n supportedClients,\n apiKey,\n features ?? [...ALL_FEATURE_VALUES],\n local,\n ),\n );\n\n const installed = namesWithStatus(results, McpClientStatus.Changed);\n const already = namesWithStatus(results, McpClientStatus.Unchanged);\n const failed = results.filter((r) => r.status === McpClientStatus.Failed);\n\n // Report each outcome on its own — a blanket \"Added the MCP server to: ...\"\n // hid both the no-op re-runs and the outright failures.\n if (installed.length > 0) {\n ui.log.success(`Added the MCP server to:\\n${bulletList(installed)}`);\n }\n if (already.length > 0) {\n ui.log.info(\n `The PostHog MCP server was already installed, so nothing changed for:\\n${bulletList(\n already,\n )}`,\n );\n }\n if (failed.length > 0) {\n ui.log.warn(\n `Couldn't add the MCP server to:\\n${bulletList(\n failed.map((r) => (r.detail ? `${r.name} — ${r.detail}` : r.name)),\n )}`,\n );\n }\n\n const withServer = [...installed, ...already];\n\n analytics.wizardCapture('mcp servers added', {\n // `clients` stays \"every client that ended up with the MCP server\", which is\n // what it meant before — the new properties break that down.\n clients: withServer,\n already_installed_clients: already,\n failed_clients: failed.map((r) => r.name),\n attempted_clients: supportedClients.map((c) => c.name),\n integration,\n });\n\n return { installed: withServer, failed: failed.map((r) => r.name) };\n};\n\nconst bulletList = (items: string[]): string =>\n items.map((item) => ` - ${item}`).join('\\n');\n\nexport const removeMCPServerFromClientsStep = async ({\n integration,\n local = false,\n}: {\n integration?: Integration;\n local?: boolean;\n}): Promise<string[]> => {\n const ui = getUI();\n const installedClients = await getInstalledClients(local);\n if (installedClients.length === 0) {\n ui.log.info(\n 'The PostHog MCP server is not installed for any supported client. Nothing to remove.',\n );\n analytics.wizardCapture('mcp no servers to remove', {\n integration,\n });\n return [];\n }\n\n // Auto-remove from all installed clients\n const results = await withProgress('removing mcp servers', () =>\n removeMCPServer(installedClients, local),\n );\n\n const removed = namesWithStatus(results, McpClientStatus.Changed);\n const nothingToDo = namesWithStatus(results, McpClientStatus.Unchanged);\n const failed = results.filter((r) => r.status === McpClientStatus.Failed);\n\n // This step used to print nothing at all, so a non-TTY `mcp remove` gave no\n // hint whether anything happened — let alone whether a client failed.\n if (removed.length > 0) {\n ui.log.success(`Removed the MCP server from:\\n${bulletList(removed)}`);\n }\n if (nothingToDo.length > 0) {\n ui.log.info(\n `No PostHog MCP entry left to remove for:\\n${bulletList(nothingToDo)}`,\n );\n }\n if (failed.length > 0) {\n ui.log.warn(\n `Couldn't remove the MCP server from:\\n${bulletList(\n failed.map((r) => (r.detail ? `${r.name} — ${r.detail}` : r.name)),\n )}`,\n );\n }\n\n analytics.wizardCapture('mcp servers removed', {\n clients: removed,\n nothing_to_remove_clients: nothingToDo,\n failed_clients: failed.map((r) => r.name),\n attempted_clients: installedClients.map((c) => c.name),\n integration,\n });\n\n return removed;\n};\n\nexport const getInstalledClients = async (\n local?: boolean,\n): Promise<MCPClient[]> => {\n const clients = await getSupportedClients();\n const installedClients: MCPClient[] = [];\n\n for (const client of clients) {\n // The plugin bundles its own posthog MCP server, so for removal purposes a\n // plugin install counts as installed even with no config entry (`--local`\n // targets only the local-dev entry and leaves the plugin alone).\n const pluginInstalled =\n !local && isPluginCapable(client) && (await client.isPluginInstalled());\n if ((await client.isServerInstalled(local)) || pluginInstalled) {\n installedClients.push(client);\n }\n }\n\n return installedClients;\n};\n\nexport const addMCPServer = async (\n clients: MCPClient[],\n personalApiKey?: string,\n selectedFeatures?: string[],\n local?: boolean,\n): Promise<McpClientResult[]> => {\n const results: McpClientResult[] = [];\n for (const client of clients) {\n try {\n const result = await client.addServer(\n personalApiKey,\n selectedFeatures,\n local,\n );\n results.push(toClientResult(client.name, result));\n } catch (err) {\n debug(`[addMCPServer] addServer threw for ${client.name}: ${err}`);\n results.push(\n toClientResult(client.name, {\n success: false,\n reason: err instanceof Error ? err.message : String(err),\n }),\n );\n }\n }\n return results;\n};\n\nexport const getSupportedPluginClients = (\n clients: MCPClient[],\n): Array<MCPClient & PluginCapable> => {\n return clients.filter(isPluginCapable).filter((c) => c.supportsPlugin());\n};\n\nexport const installPlugins = async (\n clients: Array<MCPClient & PluginCapable>,\n): Promise<McpClientResult[]> => {\n const results: McpClientResult[] = [];\n for (const client of clients) {\n try {\n results.push(toClientResult(client.name, await client.installPlugin()));\n } catch (err) {\n debug(`[installPlugins] installPlugin threw for ${client.name}: ${err}`);\n results.push(\n toClientResult(client.name, {\n success: false,\n reason: err instanceof Error ? err.message : String(err),\n }),\n );\n }\n }\n return results;\n};\n\nexport const removeMCPServer = async (\n clients: MCPClient[],\n local?: boolean,\n): Promise<McpClientResult[]> => {\n const results: McpClientResult[] = [];\n for (const client of clients) {\n try {\n let result = await client.removeServer(local);\n // The plugin bundles its own posthog server — leaving it installed makes\n // the removal a lie (`--local` never touches the plugin).\n if (!local && isPluginCapable(client) && client.removePlugin) {\n const plugin = await client.removePlugin();\n result =\n !result.success || !plugin.success\n ? {\n success: false,\n reason: [result.reason, plugin.reason]\n .filter(Boolean)\n .join('; '),\n }\n : {\n success: true,\n ...(result.alreadyInstalled && plugin.alreadyInstalled\n ? { alreadyInstalled: true }\n : {}),\n };\n }\n results.push(toClientResult(client.name, result));\n } catch (err) {\n debug(`[removeMCPServer] removeServer threw for ${client.name}: ${err}`);\n results.push(\n toClientResult(client.name, {\n success: false,\n reason: err instanceof Error ? err.message : String(err),\n }),\n );\n }\n }\n return results;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAQA,IAAsB,YAAtB,MAAgC;CAC9B;;AAaF,IAAsB,mBAAtB,cAA+C,UAAU;CACvD,OAAO;CAEP,cAAc;AACZ,SAAO;;CAGT,wBAAgC;AAC9B,SAAO;;CAGT,gBACE,QACA,kBACA,OACiB;AACjB,SAAO,uBAAuB,QAAQ,kBAAkB,MAAM;;CAGhE,MAAM,kBAAkB,OAAmC;AACzD,MAAI;GACF,MAAM,aAAa,MAAM,KAAK,eAAe;AAE7C,OAAI,CAACA,KAAG,WAAW,WAAW,CAC5B,QAAO;GAGT,MAAM,gBAAgB,MAAMA,KAAG,SAAS,SAAS,YAAY,OAAO;GACpE,MAAM,SAAS,MAAM,MAAM,cAAc;GACzC,MAAM,qBAAqB,KAAK,uBAAuB;GACvD,MAAM,aAAa,QAAQ,kBAAkB;AAE7C,UACE,sBAAsB,UAAU,cAAc,OAAO;UAEjD;AACN,UAAO;;;CAIX,MAAM,UACJ,QACA,kBACA,OACwB;AACxB,MAAI;GACF,MAAM,aAAa,MAAM,KAAK,eAAe;GAC7C,MAAM,YAAYC,OAAK,QAAQ,WAAW;AAE1C,SAAMD,KAAG,SAAS,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;GAEvD,MAAM,qBAAqB,KAAK,uBAAuB;GACvD,IAAI,gBAAgB;GACpB,IAAI,iBAAiB,EAAE;AAEvB,OAAIA,KAAG,WAAW,WAAW,EAAE;AAC7B,oBAAgB,MAAMA,KAAG,SAAS,SAAS,YAAY,OAAO;AAC9D,qBAAiB,MAAM,MAAM,cAAc,IAAI,EAAE;;GAGnD,MAAM,kBAAkB,KAAK,gBAC3B,QACA,kBACA,MACD;GACD,MAAM,cAAc;AACpB,OAAI,CAAC,YAAY,oBACf,aAAY,sBAAsB,EAAE;GAEtC,MAAM,aAAa,QAAQ,kBAAkB;GAM7C,MAAM,uBAAuB,YAAY,oBAAoB;AAC7D,OACE,yBAAyB,KAAA,KACzB,KAAK,UAAU,qBAAqB,KAAK,KAAK,UAAU,gBAAgB,CAExE,QAAO;IAAE,SAAS;IAAM,kBAAkB;IAAM;AAGlD,eAAY,oBAAoB,cAAc;GAE9C,MAAM,QAAQ,MAAM,OAClB,eACA,CAAC,oBAAoB,WAAW,EAChC,iBACA,EACE,mBAAmB;IACjB,SAAS;IACT,cAAc;IACf,EACF,CACF;GAED,MAAM,kBAAkB,MAAM,WAAW,eAAe,MAAM;AAE9D,SAAMA,KAAG,SAAS,UAAU,YAAY,iBAAiB,OAAO;AAEhE,UAAO,EAAE,SAAS,MAAM;WACjB,OAAO;AACd,UAAO;IACL,SAAS;IACT,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC/D;;;CAIL,MAAM,aAAa,OAAyC;EAC1D,IAAI,aAAa;AACjB,MAAI;AACF,gBAAa,MAAM,KAAK,eAAe;AAEvC,OAAI,CAACA,KAAG,WAAW,WAAW,CAC5B,QAAO;IAAE,SAAS;IAAM,kBAAkB;IAAM;GAGlD,MAAM,gBAAgB,MAAMA,KAAG,SAAS,SAAS,YAAY,OAAO;GACpE,MAAM,SAAS,MAAM,MAAM,cAAc;GACzC,MAAM,qBAAqB,KAAK,uBAAuB;GAEvD,MAAM,aAAa,QAAQ,kBAAkB;AAE7C,OACE,sBAAsB,UACtB,cAAc,OAAO,qBACrB;IACA,MAAM,QAAQ,MAAM,OAClB,eACA,CAAC,oBAAoB,WAAW,EAChC,KAAA,GACA,EACE,mBAAmB;KACjB,SAAS;KACT,cAAc;KACf,EACF,CACF;IAED,MAAM,kBAAkB,MAAM,WAAW,eAAe,MAAM;AAE9D,UAAMA,KAAG,SAAS,UAAU,YAAY,iBAAiB,OAAO;AAEhE,WAAO,EAAE,SAAS,MAAM;;AAI1B,UAAO;IAAE,SAAS;IAAM,kBAAkB;IAAM;WACzC,OAAO;AACd,UAAO;IACL,SAAS;IACT,QAAQ,GAAG,WAAW,IACpB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAEzD;;;;;;AClKP,IAAa,kBAAb,cAAqC,iBAAiB;CACpD,OAAO;CAEP,cAAc;AACZ,SAAO;;CAGT,MAAM,oBAAsC;AAC1C,SAAO,QAAQ,QACb,QAAQ,aAAa,YAAY,QAAQ,aAAa,QACvD;;CAGH,MAAM,gBAAiC;AACrC,SAAO,QAAQ,QAAQE,OAAK,KAAKC,KAAG,SAAS,EAAE,WAAW,WAAW,CAAC;;CAGxE,gBACE,QACA,kBACA,OACiB;AACjB,SAAO,0BAA0B,QAAQ,kBAAkB,MAAM;;;;;ACNrE,MAAM,cAAc;AACpB,MAAMC,uBAAqB;AAC3B,MAAMC,8BAA4B;AAClC,MAAMC,eAAa,GAAG,YAAY,GAAGF;;AAGrC,MAAM,uBAAuB;;;;;;AA+B7B,MAAMG,mBAAiB;;;;;;AAOvB,MAAMC,qBAAmB,KAAK,OAAO;;;;;;;AAQrC,MAAM,oBAAoB,MACxB,GAAG,SAASJ,yBACX,EAAE,SAAS,KAAA,KACV,EAAE,KAAK,aAAa,KAAKC,4BAA0B,aAAa;AAEpE,IAAa,sBAAb,cACU,iBAEV;CACE,OAAO;CACP,mBAA0C;CAE1C,cAAc;AACZ,SAAO;;CAGT,mBAA0C;AACxC,MAAI,KAAK,iBACP,QAAO,KAAK;EAId,MAAM,gBAAgB;GACpBI,OAAK,KAAKC,KAAG,SAAS,EAAE,WAAW,SAAS,SAAS;GACrD;GACA;GACD;AAED,OAAK,MAAM,cAAc,cACvB,KAAIC,KAAG,WAAW,WAAW,EAAE;AAC7B,SAAM,6BAA6B,aAAa;AAChD,QAAK,mBAAmB;AACxB,UAAO;;AAKX,MAAI;AACF,cAAS,qBAAqB,EAAE,OAAO,QAAQ,CAAC;AAChD,SAAM,yBAAyB;AAC/B,QAAK,mBAAmB;AACxB,UAAO;UACD;AAIR,SAAO;;CAGT,oBAAsC;AACpC,MAAI;AACF,SAAM,gCAAgC;GACtC,MAAM,eAAe,KAAK,kBAAkB;AAE5C,OAAI,CAAC,cAAc;AACjB,UAAM,uDAAuD;AAC7D,UAAM,SAASF,OAAK,KAAKC,KAAG,SAAS,EAAE,WAAW,SAAS,SAAS,GAAG;AACvE,UAAM,8BAA8B;AACpC,UAAM,iCAAiC;AACvC,UAAM,aAAa;AACnB,WAAO,QAAQ,QAAQ,MAAM;;AAK/B,SAAM,2BAFSE,WAAS,GAAG,aAAa,aAAa,EAAE,OAAO,QAAQ,CAAC,CAChD,UAAU,CAAC,MAAM,GACG;AAC3C,UAAO,QAAQ,QAAQ,KAAK;WACrB,OAAO;AACd,SACE,+BACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAEzD;AACD,UAAO,QAAQ,QAAQ,MAAM;;;CAIjC,kBAAkB,OAAmC;EACnD,MAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OAAQ,QAAO,QAAQ,QAAQ,MAAM;EAC1C,MAAM,aAAa,QAAQ,kBAAkB;AAI7C,MAAI;AACF,cAAS,GAAG,OAAO,WAAW,cAAc,EAAE,OAAO,QAAQ,CAAC;AAC9D,UAAO,QAAQ,QAAQ,KAAK;UACtB;AACN,UAAO,QAAQ,QAAQ,MAAM;;;CAIjC,gBAAiC;AAC/B,QAAM,IAAI,MAAM,kBAAkB;;CAGpC,UACE,QACA,kBACA,OACwB;EACxB,MAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OACH,QAAO,QAAQ,QAAQ;GACrB,SAAS;GACT,QAAQ;GACT,CAAC;EAIJ,MAAM,OAAO;GACX;GACA;GACA;GACA;GACA;GACA;GARiB,QAAQ,kBAAkB;GACjC,YAAY,kBAAkB,MAAM;GAU/C;AACD,MAAI,OACF,MAAK,KAAK,YAAY,yBAAyB,SAAS;AAG1D,MAAI;AACF,cAAS,GAAG,OAAO,GAAG,KAAK,KAAK,MAAM,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,EACpE,OAAO,QACR,CAAC;AACF,UAAO,QAAQ,QAAQ,EAAE,SAAS,MAAM,CAAC;WAClC,OAAO;GAGd,MAAM,MAAM,cACV,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;AACD,OAAI,IAAI,SAAS,iBAAiB,CAChC,QAAO,QAAQ,QAAQ;IAAE,SAAS;IAAM,kBAAkB;IAAM,CAAC;AAEnE,aAAU,iCACR,IAAI,MAAM,+BAA+B,MAAM,CAChD;AACD,UAAO,QAAQ,QAAQ;IAAE,SAAS;IAAO,QAAQ;IAAK,CAAC;;;;CAK3D,aAAa,OAAyB;AACpC,SAAO,oBAAoB,QAAQ,kBAAkB;;;CAIvD,qBAA6B;AAC3B,SAAO;;CAGT,MAAM,eAA6C;EACjD,MAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,QAAQ;GACT;EAKH,MAAM,MAAM,MAAM,KAAK,mBAAmB,OAAO;EAIjD,MAAM,cAAc,MAAM,KAAK,yBAAyB,OAAO;EAE/D,MAAM,mBACJ,KAAK,WAAW,KAAM,QAAQ,QAAQ,CAAE,MAAM,KAAK,mBAAmB;AACxE,MAAI,oBAAoB,CAAC,YACvB,QAAO;GAAE,SAAS;GAAM,kBAAkB;GAAM;EAGlD,MAAM,UAAU,mBAAmB,EAAE,GAAG,KAAK,SAAS,MAAM,CAAC,YAAY;EACzE,MAAM,WAAqB,EAAE;AAC7B,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,MAAM,MAAM,KAAK,UAAU,QAAQ;IAAC;IAAU;IAAa;IAAO,CAAC;AACzE,OAAI,CAAC,IAAI,GAAI,UAAS,KAAK,GAAG,OAAO,IAAI,IAAI,SAAS;;AAKxD,MAAI,aAAa;GACf,MAAM,MAAM,MAAM,KAAK,UAAU,QAAQ;IACvC;IACA;IACA;IACAR;IACD,CAAC;AACF,OAAI,CAAC,IAAI,GAAI,UAAS,KAAK,GAAGA,qBAAmB,IAAI,IAAI,SAAS;;AAGpE,MAAI,SAAS,WAAW,EAAG,QAAO,EAAE,SAAS,MAAM;EAEnD,MAAM,SAAS,SAAS,KAAK,KAAK;AAClC,YAAU,iCACR,IAAI,MAAM,wCAAwC,SAAS,CAC5D;AACD,SAAO;GAAE,SAAS;GAAO;GAAQ;;CAGnC,aAAa,OAAyC;EACpD,MAAM,eAAe,KAAK,kBAAkB;AAC5C,MAAI,CAAC,aACH,QAAO,QAAQ,QAAQ;GACrB,SAAS;GACT,QAAQ;GACT,CAAC;EAIJ,MAAM,UAAU,GAAG,aAAa,2BADb,QAAQ,kBAAkB;AAG7C,MAAI;AACF,cAAS,SAAS,EAAE,OAAO,QAAQ,CAAC;WAC7B,OAAO;GACd,MAAM,SAAS,cACb,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;AAGD,OAAI,mCAAmC,KAAK,OAAO,CACjD,QAAO,QAAQ,QAAQ;IAAE,SAAS;IAAM,kBAAkB;IAAM,CAAC;AAEnE,aAAU,iCACR,IAAI,MAAM,6CAA6C,SAAS,CACjE;AACD,UAAO,QAAQ,QAAQ;IAAE,SAAS;IAAO;IAAQ,CAAC;;AAGpD,SAAO,QAAQ,QAAQ,EAAE,SAAS,MAAM,CAAC;;;CAI3C,yBAAkC;AAChC,SAAO;;CAGT,iBAA0B;AACxB,SAAO,KAAK,kBAAkB,KAAK;;CAMrC,UAAkB,QAAgB,MAAoC;AACpE,SAAO,IAAI,SAAS,YAAY;AAC9B,cACE,QACA,MACA;IAAE,SAASG;IAAgB,WAAWC;IAAkB,GACvD,OAAO,WAAW;AACjB,QAAI,CAAC,MACH,QAAO,QAAQ;KAAE,IAAI;KAAM,QAAQ,QAAQ,UAAU,IAAI;KAAI,CAAC;AAChE,QAAI,MAAM,SAAS,oCACjB,QAAO,QAAQ;KACb,IAAI;KACJ,QACE;KACH,CAAC;AACJ,QAAI,MAAM,UAAU,MAAM,OACxB,QAAO,QAAQ;KACb,IAAI;KACJ,QAAQ,gCACND,mBAAiB,IAClB;KACF,CAAC;AAGJ,YAAQ;KACN,IAAI;KACJ,QAAQ,eAAe,cAAc,MAAM,QAAQ,CAAC;KACrD,CAAC;KAEL;IACD;;;CAIJ,MAAc,SACZ,QACA,MACqB;EACrB,MAAM,SAAS,MAAM,KAAK,UAAU,QAAQ,CAAC,GAAG,MAAM,SAAS,CAAC;AAChE,MAAI,CAAC,OAAO,GAAI,QAAO;AACvB,MAAI;GACF,MAAM,SAAkB,KAAK,MAAM,OAAO,OAAO;AACjD,UAAO,MAAM,QAAQ,OAAO,GAAI,SAAiB;UAC3C;AACN,UAAO;;;CAOX,MAAc,iBACZ,QACgC;EAChC,MAAM,SAAS,MAAM,KAAK,SAAuB,QAAQ,CACvD,UACA,OACD,CAAC;AACF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,QACX,MACC,OAAO,GAAG,OAAO,YAAY,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,YACvD;;CAGH,MAAc,mBAAmB,QAA0C;AAEzE,UADe,MAAM,KAAK,iBAAiB,OAAO,GACnC,KAAK,MAAM,EAAE,GAAG,IAAI;;CAGrC,MAAM,oBAAsC;EAC1C,MAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OAAQ,QAAO;EAKpB,MAAM,SAAS,MAAM,KAAK,iBAAiB,OAAO;AAClD,MAAI,OAAQ,QAAO,OAAO,SAAS;EAInC,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,CAAC,UAAU,OAAO,CAAC;AAC9D,SAAO,MAAM,MAAM,MAAM,OAAO,aAAa,CAAC,SAAS,YAAY;;CAGrE,MAAM,gBAA8C;EAClD,MAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,QAAQ;GACT;EASH,MAAM,SAAS,MAAM,KAAK,iBAAiB,OAAO;AAIlD,MAHgB,SACZ,OAAO,MAAM,MAAM,EAAE,YAAY,MAAM,GACvC,MAAM,KAAK,mBAAmB,CAEhC,QAAO;GAAE,SAAS;GAAM,kBAAkB;GAAM;EAGlD,MAAM,qBAAqB,MAAM,KAAK,wBAAwB,OAAO;EAErE,IAAI,SAAS,MAAM,KAAK,UAAU,QAAQ;GACxC;GACA;GACAD;GACD,CAAC;AAGF,MAAI,CAAC,OAAO,MAAM,qBAAqB,KAAK,OAAO,OAAO,EAAE;AAC1D,SAAM,KAAK,UAAU,QAAQ;IAC3B;IACA;IACA;IACAF;IACD,CAAC;AACF,YAAS,MAAM,KAAK,UAAU,QAAQ;IAAC;IAAU;IAAWE;IAAW,CAAC;;AAK1E,MAAI,CAAC,OAAO,MAAM,qBAAqB,KAAK,OAAO,OAAO,CACxD,UAAS,MAAM,KAAK,UAAU,QAAQ;GAAC;GAAU;GAAW;GAAY,CAAC;AAG3E,MAAI,OAAO,GAAI,QAAO,EAAE,SAAS,MAAM;EAEvC,MAAM,MAAM,OAAO;AACnB,MAAI,IAAI,SAAS,oBAAoB,IAAI,IAAI,SAAS,iBAAiB,CACrE,QAAO;GAAE,SAAS;GAAM,kBAAkB;GAAM;EAKlD,MAAM,0BAAU,IAAI,MAAM,sCAAsC,MAAM;AACtE,MAAI,mBACF,WAAU,iBAAiB,SAAS,EAAE,oBAAoB,CAAC;MAE3D,WAAU,iBAAiB,QAAQ;AAErC,SAAO;GAAE,SAAS;GAAO,QAAQ;GAAK;;;CAMxC,MAAc,yBACZ,QACyB;EACzB,MAAM,SAAS,MAAM,KAAK,SAA4B,QAAQ;GAC5D;GACA;GACA;GACD,CAAC;AACF,SAAO,SAAS,OAAO,KAAK,iBAAiB,GAAG;;CAGlD,MAAc,wBACZ,QAC6B;AAM7B,OALe,MAAM,KAAK,SAA4B,QAAQ;GAC5D;GACA;GACA;GACD,CAAC,GACU,KAAK,iBAAiB,EAAE;AAClC,SAAM,iBAAiBF,qBAAmB,qBAAqB;AAC/D;;EAMF,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ;GACzC;GACA;GACA;GACAC;GACD,CAAC;AACF,MAAI,CAAC,MAAM,IAAI;AACb,SAAM,6BAA6B,MAAM,SAAS;AAClD,UAAO,MAAM;;;;;;;;;;;AClgBnB,IAAa,qBAAb,cAAwC,UAAuC;CAC7E,OAAO;CACP,eAAe;CACf,oBAAoB;CAEpB,oBAAsC;AAEpC,SAAO,QAAQ,QAAQ,KAAK;;CAG9B,oBAAsC;AAGpC,SAAO,QAAQ,QAAQ,MAAM;;CAG/B,YAAoC;AAElC,kBAAgB,KAAK,cAAc,wBAAwB;GACzD,MAAM;GACN,SAAS;GACV,CAAC;AACF,SAAO,QAAQ,QAAQ,EAAE,SAAS,MAAM,CAAC;;CAG3C,eAAuC;AAIrC,SAAO,QAAQ,QAAQ;GACrB,SAAS;GACT,QACE;GACH,CAAC;;CAGJ,gBAAiC;AAC/B,QAAM,IAAI,MAAM,kBAAkB;;CAGpC,wBAAgC;AAC9B,QAAM,IAAI,MAAM,kBAAkB;;;ACzCGQ,IACtC,OAAO,EACN,SAASA,IAAE,OACTA,IAAE,QAAQ,EACVA,IAAE,MAAM,CACNA,IAAE,OAAO;CACP,SAASA,IAAE,QAAQ,CAAC,UAAU;CAC9B,MAAMA,IAAE,MAAMA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACpC,KAAKA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACjD,CAAC,EACFA,IAAE,OAAO;CACP,MAAMA,IAAE,KAAK,CAAC,QAAQ,MAAM,CAAC;CAC7B,KAAKA,IAAE,QAAQ;CACf,SAASA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACrD,CAAC,CACH,CAAC,CACH,EACF,CAAC,CACD,aAAa;AAMhB,IAAa,yBAAb,cAA4C,iBAAiB;CAC3D,OAAO;CAEP,wBAAgC;AAC9B,SAAO;;CAGT,MAAM,oBAAsC;AAC1C,SAAO,QAAQ,QACb,QAAQ,aAAa,YACnB,QAAQ,aAAa,WACrB,QAAQ,aAAa,QACxB;;CAGH,MAAM,gBAAiC;EACrC,MAAM,UAAUC,KAAG,SAAS;EAC5B,MAAM,YAAY,QAAQ,aAAa;EACvC,MAAM,QAAQ,QAAQ,aAAa;EACnC,MAAM,UAAU,QAAQ,aAAa;AAErC,MAAI,MACF,QAAO,QAAQ,QACbC,OAAK,KACH,SACA,WACA,uBACA,QACA,QACA,WACD,CACF;AAGH,MAAI,UACF,QAAO,QAAQ,QACbA,OAAK,KAAK,WAAW,UAAU,IAAI,IAAI,QAAQ,QAAQ,WAAW,CACnE;AAGH,MAAI,QACF,QAAO,QAAQ,QACbA,OAAK,KAAK,SAAS,WAAW,QAAQ,QAAQ,WAAW,CAC1D;AAGH,QAAM,IAAI,MAAM,yBAAyB,QAAQ,WAAW;;CAG9D,gBACE,QACA,kBACA,OACiB;AACjB,SAAO;GACL,MAAM;GACN,GAAG,0BAA0B,QAAQ,kBAAkB,MAAM;GAC9D;;;ACjFuBC,IACzB,OAAO,EACN,iBAAiBA,IAAE,OACjBA,IAAE,QAAQ,EACVA,IAAE,MAAM,CACNA,IAAE,OAAO;CACP,SAASA,IAAE,SAAS,CAAC,UAAU;CAC/B,QAAQA,IAAE,QAAQ,CAAC,UAAU;CAC7B,SAASA,IAAE,QAAQ,CAAC,UAAU;CAC9B,MAAMA,IAAE,MAAMA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACpC,KAAKA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACjD,CAAC,EACFA,IAAE,OAAO;CACP,SAASA,IAAE,SAAS,CAAC,UAAU;CAC/B,KAAKA,IAAE,QAAQ;CACf,SAASA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACrD,CAAC,CACH,CAAC,CACH,EACF,CAAC,CACD,aAAa;AAIhB,IAAa,YAAb,cAA+B,iBAAiB;CAC9C,OAAO;CAEP,wBAAgC;AAC9B,SAAO;;CAGT,MAAM,oBAAsC;AAC1C,SAAO,QAAQ,QACb,QAAQ,aAAa,YAAY,QAAQ,aAAa,QACvD;;CAGH,MAAM,gBAAiC;EACrC,MAAM,UAAUC,KAAG,SAAS;EAC5B,MAAM,QAAQ,QAAQ,aAAa;EACnC,MAAM,UAAU,QAAQ,aAAa;AAErC,MAAI,MACF,QAAO,QAAQ,QACbC,OAAK,KAAK,SAAS,WAAW,OAAO,gBAAgB,CACtD;AAGH,MAAI,SAAS;GAEX,MAAM,gBAAgB,WAAW,kBAAkB;AACnD,OAAI,cACF,QAAO,QAAQ,QACbA,OAAK,KAAK,eAAe,OAAO,gBAAgB,CACjD;AAEH,UAAO,QAAQ,QACbA,OAAK,KAAK,SAAS,WAAW,OAAO,gBAAgB,CACtD;;AAGH,QAAM,IAAI,MAAM,yBAAyB,QAAQ,WAAW;;CAG9D,gBACE,QACA,kBACA,OACiB;AACjB,SAAO;GACL,SAAS;GACT,GAAG,0BAA0B,QAAQ,kBAAkB,MAAM;GAC9D;;;;;ACxDL,MAAM,qBAAqB;AAC3B,MAAM,4BAA4B;AAClC,MAAM,aAAa,WAAW;;;;;;;AAQ9B,MAAM,qBAAqB,WAA4B;CACrD,MAAM,MAAM,IAAI,OAAO,IAAI,WAAW,YAAY,IAAI,CAAC,KAAK,OAAO,GAAG;AACtE,QAAO,CAAC,CAAC,OAAO,gBAAgB,KAAK,IAAI,IAAI,CAAC,oBAAoB,KAAK,IAAI;;AAe7E,MAAM,gBAA8B;CAClC;CACA;CACA;CACD;AACD,MAAM,aAA2B,CAAC,WAAW,aAAa;AAC1D,MAAM,aAA2B,CAAC,GAAG,eAAe,GAAG,WAAW;;;;;;;;;AAmBlE,MAAM,oBAAuC;CAC3C;EAEE,OACE;EACF,QAAQ;EACR,MAAM;EACP;CACD;EAGE,OACE;EACF,QAAQ;EACR,MAAM;EACP;CACD;EACE,OAAO;EACP,QAAQ;EACR,MAAM;EACP;CACD;EAME,OACE;EACF,QAAQ;EACR,QAxCF;EAyCE,MAAM;EACP;CACD;EACE,OACE;EACF,QAAQ;EAKR,QAAQ;EACR,MAAM;EACP;CACD;EACE,OAAO;EACP,QAAQ;EACR,MAAM;EACP;CACD;EAIE,OACE;EACF,QAAQ;EACR,QACE;EACF,MAAM;EACP;CACF;;;;;;AAaD,MAAM,iBAAiB;;;;;;AAOvB,MAAM,mBAAmB,KAAK,OAAO;;;;;;;AAQrC,MAAM,0BAA0B;;;;;;AAOhC,MAAM,uBAAuB,UAAqC;AAChE,KAAI,MAAM,SAAS,oCACjB,QAAO;AACT,KAAI,MAAM,OACR,QAAO,+BACL,iBAAiB,IAClB;AACH,KAAI,MAAM,OAAQ,QAAO,uBAAuB,MAAM;CAEtD,MAAM,OAAO,MAAM,QAAQ,QAAQ,yBAAyB,GAAG,CAAC,MAAM;AACtE,KAAI,KAAM,QAAO;AACjB,KAAI,OAAO,MAAM,SAAS,SACxB,QAAO,0BAA0B,MAAM;AACzC,KAAI,MAAM,KAAM,QAAO,qBAAqB,MAAM;AAClD,QAAO;;;;;;;;;;;;AAaT,MAAM,YACJ,QACA,MACA,QAEA,IAAI,SAAS,YAAY;AACT,UACZ,QACA,MACA;EAAE;EAAK,SAAS;EAAgB,WAAW;EAAkB,GAC5D,OAAO,QAAQ,WAAW;AACzB,MAAI,CAAC,MAAO,QAAO,QAAQ;GAAE,IAAI;GAAM,QAAQ,UAAU;GAAI,CAAC;EAI9D,MAAM,OAAO,CAAC,QAAQ,OAAO,CAC1B,KAAK,MAAO,IAAI,OAAO,EAAE,CAAC,MAAM,GAAG,GAAI,CACvC,OAAO,QAAQ,CACf,KAAK,KAAK;AAOb,UAAQ;GACN,IAAI;GACJ,QAAQ,cALR,MAAM,UACN,CAAC,CAAC,MAAM,UACR,MAAM,SAAS,uCAIF,CAAC,OAAO,oBAAoB,MAAM,GAAG,KACjD;GACF,CAAC;GAEL,CAIK,OAAO,KAAK;EAClB;;;;;;AAOJ,MAAM,sBACJ,OACA,YACkB;CAClB,MAAM,OAAO,oBAAoB,SAAS,mBAAmB,MAAM;AACnE,KAAI,MAAM;AAIR,YAAU,cAAc,+BAA+B;GACrD,QAAQ;GACR;GACA;GACA,SAAS,eAAe,QAAQ;GACjC,CAAC;AACF,SAAO;GAAE,SAAS;GAAO,QAAQ;GAAM;;AAEzC,WAAU,iCAAiB,IAAI,MAAM,SAAS,MAAM,SAAS,EAAE;EAC7D;EACA,SAAS,eAAe,QAAQ;EACjC,CAAC;AACF,QAAO;EAAE,SAAS;EAAO,QAAQ;EAAS;;;AAI5C,MAAM,4BACJ;;AAEF,MAAM,0BAA0B;;;;;;AAOhC,MAAM,sBAAsB;;;;;;;AAQ5B,MAAM,iBAAiB,eACrB,IAAI,OACF,wBAAwB,WAAW,IAAI,WAAW,gBAClD,IACD;;;;;;AAOH,MAAM,UAAU,MAAc,KAAa,UAA0B;CACnE,MAAM,WAAW,IAAI,OAAO,WAAW,IAAI,cAAc,IAAI;AAC7D,QAAO,SAAS,KAAK,KAAK,GACtB,KAAK,QAAQ,UAAU,GAAG,IAAI,KAAK,QAAQ,GAC3C,KAAK,IAAI,KAAK,QAAQ;;AAO5B,IAAa,iBAAb,cACU,iBAEV;CACE,OAAO;CACP,kBAAyC;CAEzC,cAAc;AACZ,SAAO;;CAGT,kBAAyC;AACvC,MAAI,KAAK,gBAAiB,QAAO,KAAK;AACtC,MAAI;GACF,MAAM,WAAW,SAAS,oBAAoB,EAAE,OAAO,QAAQ,CAAC,CAC7D,UAAU,CACV,MAAM;AACT,OAAI,UAAU;AACZ,SAAK,kBAAkB;AACvB,WAAO;;UAEH;AAGR,SAAO;;CAGT,aAA6B;AAC3B,SAAOC,OAAK,KAAK,GAAG,SAAS,EAAE,UAAU,cAAc;;CAGzD,oBAAsC;AACpC,SAAO,QAAQ,QAAQ,KAAK,iBAAiB,KAAK,KAAK;;CAGzD,gBAAiC;AAC/B,QAAM,IAAI,MAAM,kBAAkB;;CAGpC,kBAAkB,OAAmC;EACnD,MAAM,aAAa,QAAQ,kBAAkB;AAI7C,MAAI;GACF,MAAM,WAAWC,KAAG,aAAa,KAAK,YAAY,EAAE,QAAQ;AAC5D,UAAO,QAAQ,QAAQ,cAAc,WAAW,CAAC,KAAK,SAAS,CAAC;UAC1D;AACN,UAAO,QAAQ,QAAQ,MAAM;;;CAIjC,MAAM,UACJ,QACA,kBACA,OACwB;EACxB,MAAM,aAAa,QAAQ,kBAAkB;EAC7C,MAAM,MAAM,YAAY,kBAAkB,MAAM;AAGhD,MAAI,QAAQ;GACV,MAAM,SAAS,KAAK,iBAAiB;AACrC,OAAI,CAAC,OACH,QAAO;IACL,SAAS;IACT,QAAQ;IACT;GAWH,MAAM,SAAS,MAAM,SAAS,QAVjB;IACX;IACA;IACA;IACA;IACA;IACA;IACA;IACD,EACW;IAAE,GAAG,QAAQ;IAAK,qBAAqB,UAAU;IAAU,CACvB;AAChD,OAAI,CAAC,OAAO,IAAI;AACd,QAAI,0BAA0B,KAAK,OAAO,OAAO,CAC/C,QAAO;KAAE,SAAS;KAAM,kBAAkB;KAAM;AAElD,WAAO,mBAAmB,WAAW,OAAO,OAAO;;AAErD,UAAO,EAAE,SAAS,MAAM;;AAO1B,SAAO,KAAK,mBAAmB,YAAY,IAAI;;;;;;CAOjD,mBAA2B,YAAoB,KAA4B;EACzE,MAAM,aAAa,KAAK,YAAY;AACpC,MAAI;AAGF,QAAG,UAAUD,OAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;GAC3D,MAAM,WAAWC,KAAG,WAAW,WAAW,GACtCA,KAAG,aAAa,YAAY,QAAQ,GACpC;GAEJ,MAAM,SAAS,cAAc,WAAW,CAAC,KAAK,SAAS;AACvD,OAAI,CAAC,QAAQ;IACX,MAAM,MAAM,aAAa,MAAM,SAAS,SAAS,OAAO,GAAG,KAAK;IAChE,MAAM,MAAM,aAAa,MAAM,SAAS,SAAS,KAAK,GAAG,KAAK;AAC9D,SAAK,MACH,YACA,GAAG,WAAW,MAAM,IAAI,eAAe,WAAW,YACtC,IAAI,2BAA2B,oBAAoB,IAChE;AACD,WAAO,EAAE,SAAS,MAAM;;GAI1B,MAAM,QAAQ,OAAO,QAAQ,OAAO,GAAG;GACvC,MAAM,OAAO,SAAS,MAAM,MAAM;GAClC,MAAM,OAAO,OAAO,KAAK,KAAK;GAC9B,MAAM,MAAM,OAAO,QAAQ,KAAK,QAAQ,SAAS;GAEjD,MAAM,OAAO,SAAS,MAAM,OAAO,IAAI;GACvC,MAAM,UAAU,OACd,OAAO,MAAM,OAAO,IAAI,IAAI,GAAG,EAC/B,uBACA,OAAO,oBAAoB,CAC5B;AACD,OAAI,YAAY,KAAM,QAAO;IAAE,SAAS;IAAM,kBAAkB;IAAM;AAEtE,QAAK,MACH,YACA,SAAS,MAAM,GAAG,MAAM,GAAG,UAAU,SAAS,MAAM,IAAI,CACzD;AACD,UAAO,EAAE,SAAS,MAAM;WACjB,OAAO;GACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAGrE,aAAU,iCAAiB,IAAI,MAAM,iCAAiC,EAAE,EACtE,SAAS,eAAe,OAAO,EAChC,CAAC;AACF,UAAO;IAAE,SAAS;IAAO;IAAQ;;;;CAKrC,MAAc,YAAoB,UAAwB;EACxD,MAAM,MAAM,GAAG,WAAW;AAC1B,OAAG,cAAc,KAAK,SAAS;AAC/B,OAAG,WAAW,KAAK,WAAW;;;CAIhC,aAAa,OAAgC;AAC3C,MAAI,CAAC,KAAK,iBAAiB,CAAE,QAAO;AACpC,SAAO,mBAAmB,QAAQ,kBAAkB;;CAGtD,MAAM,aAAa,OAAyC;EAC1D,MAAM,SAAS,KAAK,iBAAiB;AACrC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,QAAQ;GACT;EAKH,MAAM,SAAS,MAAM,SAAS,QAAQ;GAAC;GAAO;GAD3B,QAAQ,kBAAkB;GACsB,CAAC;AAEpE,MAAI,CAAC,OAAO,GACV,QAAO,mBAAmB,cAAc,OAAO,OAAO;AAGxD,SAAO,EAAE,SAAS,MAAM;;;CAI1B,yBAAkC;AAChC,SAAO;;CAGT,iBAA0B;AACxB,SAAO,KAAK,iBAAiB,KAAK;;;CAIpC,MAAc,kBAAoC;EAChD,MAAM,SAAS,KAAK,iBAAiB;AACrC,MAAI,CAAC,OAAQ,QAAO;EACpB,MAAM,SAAS,MAAM,SAAS,QAAQ;GACpC;GACA;GACA;GACA;GACD,CAAC;AACF,SAAO,OAAO,MAAM,kBAAkB,OAAO,OAAO;;;;;;;;CAStD,MAAM,oBAAsC;AAC1C,SAAQ,MAAM,KAAK,iBAAiB,IAAK,KAAK,yBAAyB;;;CAIzE,0BAA2C;AACzC,MAAI;AAEF,UADiBA,KAAG,aAAa,KAAK,YAAY,EAAE,QAAQ,CAEzD,aAAa,CACb,SAAS,iBAAiB,mBAAmB,GAAG;UAC7C;AACN,UAAO;;;CAIX,MAAM,eAA6C;EACjD,MAAM,SAAS,KAAK,iBAAiB;AACrC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,QAAQ;GACT;EAIH,MAAM,QAAoB,EAAE;AAC5B,MAAI,MAAM,KAAK,iBAAiB,CAC9B,OAAM,KAAK;GAAC;GAAU;GAAU;GAAW,CAAC;AAC9C,MAAI,KAAK,yBAAyB,CAChC,OAAM,KAAK;GAAC;GAAU;GAAe;GAAU;GAAmB,CAAC;AAErE,MAAI,MAAM,WAAW,EAAG,QAAO;GAAE,SAAS;GAAM,kBAAkB;GAAM;AAExE,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,MAAM,SAAS,QAAQ,KAAK;AAC3C,OAAI,CAAC,OAAO,GACV,QAAO,mBAAmB,oBAAoB,OAAO,OAAO;;AAGhE,SAAO,EAAE,SAAS,MAAM;;CAG1B,MAAM,gBAA8C;EAClD,MAAM,SAAS,KAAK,iBAAiB;AACrC,MAAI,CAAC,OACH,QAAO;GACL,SAAS;GACT,QAAQ;GACT;AAKH,MAAI,MAAM,KAAK,iBAAiB,CAC9B,QAAO;GAAE,SAAS;GAAM,kBAAkB;GAAM;EAMlD,MAAM,cAAc,KAAK,yBAAyB,GAC9C,OACA,MAAM,KAAK,oBAAoB,OAAO;AAC1C,MAAI,eAAe,CAAC,YAAY,QAAS,QAAO;EAIhD,MAAM,SAAS,MAAM,SAAS,QAAQ;GAAC;GAAU;GAAO;GAAW,CAAC;AAEpE,MAAI,CAAC,OAAO,IAAI;AAId,OAAI,0BAA0B,KAAK,OAAO,OAAO,CAC/C,QAAO;IAAE,SAAS;IAAM,kBAAkB;IAAM;AAElD,UAAO,mBAAmB,kBAAkB,OAAO,OAAO;;AAG5D,SAAO,EAAE,SAAS,MAAM;;;;;;;CAQ1B,MAAc,oBACZ,QAC8B;EAC9B,MAAM,YACJ,SAAS,QAAQ;GACf;GACA;GACA;GACA;GACD,CAAC;EAEJ,IAAI,SAAS,MAAM,KAAK;AAExB,MAAI,CAAC,OAAO,MAAM,wBAAwB,KAAK,OAAO,OAAO,EAAE;GAC7D,MAAM,WAAWD,OAAK,KACpB,GAAG,SAAS,EACZ,UACA,QACA,gBACA,mBACD;AACD,OAAI;AACF,SAAG,OAAO,UAAU;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;WAC/C;AAGR,YAAS,MAAM,KAAK;;AAGtB,MAAI,CAAC,OAAO,IAAI;GACd,MAAM,UAAU,OAAO;AAIvB,OACE,0BAA0B,KAAK,QAAQ,IACvC,CAAC,wBAAwB,KAAK,QAAQ,CAEtC,QAAO,EAAE,SAAS,MAAM;AAE1B,UAAO,mBAAmB,mBAAmB,QAAQ;;AAEvD,SAAO,EAAE,SAAS,MAAM;;;AC/nBKE,IAC9B,OAAO,EACN,KAAKA,IAAE,OACLA,IAAE,QAAQ,EACVA,IAAE,OAAO;CACP,MAAMA,IAAE,QAAQ,SAAS;CACzB,KAAKA,IAAE,QAAQ;CACf,SAASA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACrD,CAAC,CACH,EACF,CAAC,CACD,aAAa;AAIhB,IAAa,oBAAb,cAAuC,iBAAiB;CACtD,OAAO;CACP,qBAA4C;CAE5C,wBAAyC;AACvC,SAAO;;CAGT,qBAA4C;AAC1C,MAAI,KAAK,mBAAoB,QAAO,KAAK;AACzC,MAAI;GACF,MAAM,WAAW,SAAS,uBAAuB,EAAE,OAAO,QAAQ,CAAC,CAChE,UAAU,CACV,MAAM;AACT,OAAI,UAAU;AACZ,SAAK,qBAAqB;AAC1B,WAAO;;UAEH;AAGR,SAAO;;CAGT,oBAAsC;AACpC,SAAO,QAAQ,QAAQ,KAAK,oBAAoB,KAAK,KAAK;;CAG5D,gBAAiC;EAC/B,MAAM,YAAY,KAAK,kBAAkB;EACzC,MAAM,YAAYC,OAAK,KAAK,WAAW,iBAAiB;EACxD,MAAM,WAAWA,OAAK,KAAK,WAAW,gBAAgB;AACtD,MAAIC,KAAG,WAAW,UAAU,CAAE,QAAO,QAAQ,QAAQ,UAAU;AAC/D,SAAO,QAAQ,QAAQ,SAAS;;CAGlC,mBAAmC;EACjC,MAAM,SAAS,WAAW,sBAAsB;AAChD,MAAI,OAAQ,QAAO;EAEnB,MAAM,UAAU,WAAW,kBAAkB;AAC7C,MAAI,QAAS,QAAOD,OAAK,KAAK,SAAS,WAAW;AAElD,SAAOA,OAAK,KAAKE,KAAG,SAAS,EAAE,WAAW,WAAW;;CAGvD,gBACE,QACA,kBACA,OACiB;AACjB,SAAO;GACL,MAAM;GACN,GAAG,0BAA0B,QAAQ,kBAAkB,MAAM;GAC9D;;;;;ACnEL,SAAgB,gBAAmB,QAAwC;AACzE,QACE,OAAO,WAAW,YAClB,WAAW,QACX,oBAAoB,UACpB,mBAAmB;;;;ACIvB,MAAa,sBAAsB,YAAkC;CACnE,MAAM,aAAa;EACjB,IAAI,qBAAqB;EACzB,IAAI,oBAAoB;EACxB,IAAI,gBAAgB;EACpB,IAAI,iBAAiB;EACrB,IAAI,wBAAwB;EAC5B,IAAI,WAAW;EACf,IAAI,mBAAmB;EACxB;CACD,MAAM,mBAAgC,EAAE;AAExC,OAAM,wCAAwC;AAC9C,MAAK,MAAM,UAAU,YAAY;EAC/B,MAAM,cAAc,MAAM,OAAO,mBAAmB;AACpD,QAAM,GAAG,OAAO,KAAK,IAAI,cAAc,gBAAgB,oBAAoB;AAC3E,MAAI,YACF,kBAAiB,KAAK,OAAO;;AAGjC,OACE,SAAS,iBAAiB,OAAO,wBAAwB,iBACtD,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,KAAK,GACd;AAED,QAAO;;;;;;AAcT,MAAa,4BAA4B,OAAO,EAC9C,aACA,QAAQ,OACR,KAAK,OACL,aAAa,cACb,UACA,aAQ6B;CAC7B,MAAM,KAAK,OAAO;AAGlB,KAAI,IAAI;AACN,KAAG,IAAI,KAAK,sCAAsC;AAClD,SAAO;GAAE,WAAW,EAAE;GAAE,QAAQ,EAAE;GAAE;;CAGtC,MAAM,mBAAmB,MAAM,qBAAqB;AAEpD,KAAI,iBAAiB,WAAW,GAAG;AACjC,KAAG,IAAI,KACL,gEACD;AACD,SAAO;GAAE,WAAW,EAAE;GAAE,QAAQ,EAAE;GAAE;;CAItC,MAAM,UAAU,MAAM,aAAa,4BACjC,aACE,kBACA,QACA,YAAY,CAAC,GAAG,mBAAmB,EACnC,MACD,CACF;CAED,MAAM,YAAY,gBAAgB,SAAA,UAAiC;CACnE,MAAM,UAAU,gBAAgB,SAAA,YAAmC;CACnE,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAA,SAAkC;AAIzE,KAAI,UAAU,SAAS,EACrB,IAAG,IAAI,QAAQ,6BAA6B,WAAW,UAAU,GAAG;AAEtE,KAAI,QAAQ,SAAS,EACnB,IAAG,IAAI,KACL,0EAA0E,WACxE,QACD,GACF;AAEH,KAAI,OAAO,SAAS,EAClB,IAAG,IAAI,KACL,oCAAoC,WAClC,OAAO,KAAK,MAAO,EAAE,SAAS,GAAG,EAAE,KAAK,KAAK,EAAE,WAAW,EAAE,KAAM,CACnE,GACF;CAGH,MAAM,aAAa,CAAC,GAAG,WAAW,GAAG,QAAQ;AAE7C,WAAU,cAAc,qBAAqB;EAG3C,SAAS;EACT,2BAA2B;EAC3B,gBAAgB,OAAO,KAAK,MAAM,EAAE,KAAK;EACzC,mBAAmB,iBAAiB,KAAK,MAAM,EAAE,KAAK;EACtD;EACD,CAAC;AAEF,QAAO;EAAE,WAAW;EAAY,QAAQ,OAAO,KAAK,MAAM,EAAE,KAAK;EAAE;;AAGrE,MAAM,cAAc,UAClB,MAAM,KAAK,SAAS,OAAO,OAAO,CAAC,KAAK,KAAK;AAE/C,MAAa,iCAAiC,OAAO,EACnD,aACA,QAAQ,YAIe;CACvB,MAAM,KAAK,OAAO;CAClB,MAAM,mBAAmB,MAAM,oBAAoB,MAAM;AACzD,KAAI,iBAAiB,WAAW,GAAG;AACjC,KAAG,IAAI,KACL,uFACD;AACD,YAAU,cAAc,4BAA4B,EAClD,aACD,CAAC;AACF,SAAO,EAAE;;CAIX,MAAM,UAAU,MAAM,aAAa,8BACjC,gBAAgB,kBAAkB,MAAM,CACzC;CAED,MAAM,UAAU,gBAAgB,SAAA,UAAiC;CACjE,MAAM,cAAc,gBAAgB,SAAA,YAAmC;CACvE,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAA,SAAkC;AAIzE,KAAI,QAAQ,SAAS,EACnB,IAAG,IAAI,QAAQ,iCAAiC,WAAW,QAAQ,GAAG;AAExE,KAAI,YAAY,SAAS,EACvB,IAAG,IAAI,KACL,6CAA6C,WAAW,YAAY,GACrE;AAEH,KAAI,OAAO,SAAS,EAClB,IAAG,IAAI,KACL,yCAAyC,WACvC,OAAO,KAAK,MAAO,EAAE,SAAS,GAAG,EAAE,KAAK,KAAK,EAAE,WAAW,EAAE,KAAM,CACnE,GACF;AAGH,WAAU,cAAc,uBAAuB;EAC7C,SAAS;EACT,2BAA2B;EAC3B,gBAAgB,OAAO,KAAK,MAAM,EAAE,KAAK;EACzC,mBAAmB,iBAAiB,KAAK,MAAM,EAAE,KAAK;EACtD;EACD,CAAC;AAEF,QAAO;;AAGT,MAAa,sBAAsB,OACjC,UACyB;CACzB,MAAM,UAAU,MAAM,qBAAqB;CAC3C,MAAM,mBAAgC,EAAE;AAExC,MAAK,MAAM,UAAU,SAAS;EAI5B,MAAM,kBACJ,CAAC,SAAS,gBAAgB,OAAO,IAAK,MAAM,OAAO,mBAAmB;AACxE,MAAK,MAAM,OAAO,kBAAkB,MAAM,IAAK,gBAC7C,kBAAiB,KAAK,OAAO;;AAIjC,QAAO;;AAGT,MAAa,eAAe,OAC1B,SACA,gBACA,kBACA,UAC+B;CAC/B,MAAM,UAA6B,EAAE;AACrC,MAAK,MAAM,UAAU,QACnB,KAAI;EACF,MAAM,SAAS,MAAM,OAAO,UAC1B,gBACA,kBACA,MACD;AACD,UAAQ,KAAK,eAAe,OAAO,MAAM,OAAO,CAAC;UAC1C,KAAK;AACZ,QAAM,sCAAsC,OAAO,KAAK,IAAI,MAAM;AAClE,UAAQ,KACN,eAAe,OAAO,MAAM;GAC1B,SAAS;GACT,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACzD,CAAC,CACH;;AAGL,QAAO;;AAGT,MAAa,6BACX,YACqC;AACrC,QAAO,QAAQ,OAAO,gBAAgB,CAAC,QAAQ,MAAM,EAAE,gBAAgB,CAAC;;AAG1E,MAAa,iBAAiB,OAC5B,YAC+B;CAC/B,MAAM,UAA6B,EAAE;AACrC,MAAK,MAAM,UAAU,QACnB,KAAI;AACF,UAAQ,KAAK,eAAe,OAAO,MAAM,MAAM,OAAO,eAAe,CAAC,CAAC;UAChE,KAAK;AACZ,QAAM,4CAA4C,OAAO,KAAK,IAAI,MAAM;AACxE,UAAQ,KACN,eAAe,OAAO,MAAM;GAC1B,SAAS;GACT,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACzD,CAAC,CACH;;AAGL,QAAO;;AAGT,MAAa,kBAAkB,OAC7B,SACA,UAC+B;CAC/B,MAAM,UAA6B,EAAE;AACrC,MAAK,MAAM,UAAU,QACnB,KAAI;EACF,IAAI,SAAS,MAAM,OAAO,aAAa,MAAM;AAG7C,MAAI,CAAC,SAAS,gBAAgB,OAAO,IAAI,OAAO,cAAc;GAC5D,MAAM,SAAS,MAAM,OAAO,cAAc;AAC1C,YACE,CAAC,OAAO,WAAW,CAAC,OAAO,UACvB;IACE,SAAS;IACT,QAAQ,CAAC,OAAO,QAAQ,OAAO,OAAO,CACnC,OAAO,QAAQ,CACf,KAAK,KAAK;IACd,GACD;IACE,SAAS;IACT,GAAI,OAAO,oBAAoB,OAAO,mBAClC,EAAE,kBAAkB,MAAM,GAC1B,EAAE;IACP;;AAET,UAAQ,KAAK,eAAe,OAAO,MAAM,OAAO,CAAC;UAC1C,KAAK;AACZ,QAAM,4CAA4C,OAAO,KAAK,IAAI,MAAM;AACxE,UAAQ,KACN,eAAe,OAAO,MAAM;GAC1B,SAAS;GACT,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GACzD,CAAC,CACH;;AAGL,QAAO"}
@@ -1,2 +0,0 @@
1
- import { c as removeMCPServerFromClientsStep, n as addMCPServerToClientsStep } from "./add-mcp-server-to-clients-DIaTRWMY.js";
2
- export { addMCPServerToClientsStep, removeMCPServerFromClientsStep };
@@ -1,6 +0,0 @@
1
- import "./agent-interface-FkbaBJwz.js";
2
- import { d as configureGatewayFromCIEnvironment } from "./gateway-C9-GlLO9.js";
3
- import { t as runMcpPromptViaSdk } from "./agent-BWFhuLgo.js";
4
- import "./yara-hooks-BnxejtXK.js";
5
- import "./queue-tools-C8mXOh7e.js";
6
- export { configureGatewayFromCIEnvironment, runMcpPromptViaSdk };