@posthog/wizard 2.76.0 → 2.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/README.md +16 -15
  2. package/dist/{add-mcp-server-to-clients-Exddicp2.js → add-mcp-server-to-clients-BnRyE4pw.js} +428 -114
  3. package/dist/add-mcp-server-to-clients-BnRyE4pw.js.map +1 -0
  4. package/dist/{agent-interface-CZWRgZMc.js → agent-interface-Avtr_5M5.js} +34 -10
  5. package/dist/agent-interface-Avtr_5M5.js.map +1 -0
  6. package/dist/{agent-prompt-loader-BobuLDIg.js → agent-prompt-loader-CHixNPgY.js} +23 -7
  7. package/dist/agent-prompt-loader-CHixNPgY.js.map +1 -0
  8. package/dist/{agent-runner-C_R1X_UT.js → agent-runner-COOIUJCP.js} +171 -83
  9. package/dist/agent-runner-COOIUJCP.js.map +1 -0
  10. package/dist/{analytics-C_RyE8fz.js → analytics-C67F2vee.js} +2 -2
  11. package/dist/{analytics-C_RyE8fz.js.map → analytics-C67F2vee.js.map} +1 -1
  12. package/dist/analytics-CJA1uLlk.js +2 -0
  13. package/dist/{api-k5aGQpIZ.js → api-Dj1yabTj.js} +3 -3
  14. package/dist/{api-k5aGQpIZ.js.map → api-Dj1yabTj.js.map} +1 -1
  15. package/dist/bin.js +41 -41
  16. package/dist/bin.js.map +1 -1
  17. package/dist/{bounded-fs-BHLy83N9.js → bounded-fs-CWRYuLAn.js} +3 -3
  18. package/dist/bounded-fs-CWRYuLAn.js.map +1 -0
  19. package/dist/{ci-install-CXt4c41q.js → ci-install-CvXL5h13.js} +5 -5
  20. package/dist/{ci-install-CXt4c41q.js.map → ci-install-CvXL5h13.js.map} +1 -1
  21. package/dist/{debug-C5Zz8hmm.js → debug-B4kzIZPi.js} +12 -9
  22. package/dist/debug-B4kzIZPi.js.map +1 -0
  23. package/dist/{debug-BxMMpepn.js → debug-CziFS0cA.js} +1 -1
  24. package/dist/{defaults-DulzMJpN.js → defaults-4c4YwTmW.js} +26 -3
  25. package/dist/defaults-4c4YwTmW.js.map +1 -0
  26. package/dist/{env-api-key-Ca-CrkgV.js → env-api-key-BZ5wE3Y4.js} +2 -2
  27. package/dist/{env-api-key-Ca-CrkgV.js.map → env-api-key-BZ5wE3Y4.js.map} +1 -1
  28. package/dist/{environment-B0uTamFf.js → environment-BEvsey6i.js} +4 -4
  29. package/dist/{environment-B0uTamFf.js.map → environment-BEvsey6i.js.map} +1 -1
  30. package/dist/{errors--OqLxmWR.js → errors-BoJvrdw8.js} +4 -2
  31. package/dist/errors-BoJvrdw8.js.map +1 -0
  32. package/dist/{file-CNstcW8F.js → file-BoD3REGB.js} +2 -2
  33. package/dist/{file-CNstcW8F.js.map → file-BoD3REGB.js.map} +1 -1
  34. package/dist/{file-watcher-CiT-oZpu.js → file-watcher-BPFazvG_.js} +2 -2
  35. package/dist/{file-watcher-CiT-oZpu.js.map → file-watcher-BPFazvG_.js.map} +1 -1
  36. package/dist/{gateway-session-BIVN_Xfr.js → gateway-session-C118uACz.js} +1 -1
  37. package/dist/{gateway-session-C3Uqwc4R.js → gateway-session-xPFv6p5c.js} +5 -5
  38. package/dist/{gateway-session-C3Uqwc4R.js.map → gateway-session-xPFv6p5c.js.map} +1 -1
  39. package/dist/{headless-ui-TpSp-F43.js → headless-ui-CsmQrDIl.js} +2 -2
  40. package/dist/{headless-ui-TpSp-F43.js.map → headless-ui-CsmQrDIl.js.map} +1 -1
  41. package/dist/{interactive-B72L-sO5.js → interactive-Dej-sxvR.js} +2 -2
  42. package/dist/{interactive-B72L-sO5.js.map → interactive-Dej-sxvR.js.map} +1 -1
  43. package/dist/{mcp-CNc6CLK0.js → mcp-Cbuw_xss.js} +2 -2
  44. package/dist/{mcp-CNc6CLK0.js.map → mcp-Cbuw_xss.js.map} +1 -1
  45. package/dist/{mcp-prompt-streaming-BvBNkzjv.js → mcp-prompt-streaming-BU_kuur_.js} +5 -5
  46. package/dist/{mcp-prompt-streaming-BvBNkzjv.js.map → mcp-prompt-streaming-BU_kuur_.js.map} +1 -1
  47. package/dist/{mint-failure-CZ5FZ0Jd.js → mint-failure-BrleGqMd.js} +72 -39
  48. package/dist/mint-failure-BrleGqMd.js.map +1 -0
  49. package/dist/{non-interactive-UeQlnU0U.js → non-interactive-DcOjH2fw.js} +3 -3
  50. package/dist/{non-interactive-UeQlnU0U.js.map → non-interactive-DcOjH2fw.js.map} +1 -1
  51. package/dist/{orchestrator-tools-CLADku26.js → orchestrator-tools-DXwr8Sp3.js} +4 -4
  52. package/dist/orchestrator-tools-DXwr8Sp3.js.map +1 -0
  53. package/dist/{package-manager-CppzeIgu.js → package-manager-DR23m5Jg.js} +2 -2
  54. package/dist/{package-manager-CppzeIgu.js.map → package-manager-DR23m5Jg.js.map} +1 -1
  55. package/dist/{pi-Bs1Tbt0C.js → pi-mY8I2XkG.js} +13 -13
  56. package/dist/{pi-Bs1Tbt0C.js.map → pi-mY8I2XkG.js.map} +1 -1
  57. package/dist/{playground-CZgZX4j0.js → playground-CDUUKLkC.js} +13 -9
  58. package/dist/playground-CDUUKLkC.js.map +1 -0
  59. package/dist/{posthog-BZ3NNCyh.js → posthog-Hc_-9wrr.js} +2 -2
  60. package/dist/{posthog-BZ3NNCyh.js.map → posthog-Hc_-9wrr.js.map} +1 -1
  61. package/dist/{provisioning-Ba7GWKzK.js → provisioning-xfdjhFtA.js} +3 -3
  62. package/dist/{provisioning-Ba7GWKzK.js.map → provisioning-xfdjhFtA.js.map} +1 -1
  63. package/dist/{queue-tools-D09PNS0X.js → queue-tools-C8y_xZJi.js} +8 -4
  64. package/dist/queue-tools-C8y_xZJi.js.map +1 -0
  65. package/dist/{registry-COcpEsMD.js → registry-DTmREJ-f.js} +5 -5
  66. package/dist/{registry-COcpEsMD.js.map → registry-DTmREJ-f.js.map} +1 -1
  67. package/dist/{security-CpAT0fZa.js → security-Dw2oFJjP.js} +5 -5
  68. package/dist/{security-CpAT0fZa.js.map → security-Dw2oFJjP.js.map} +1 -1
  69. package/dist/{setup-utils-BUAoRjw3.js → setup-utils-fjJTBhaB.js} +8 -8
  70. package/dist/setup-utils-fjJTBhaB.js.map +1 -0
  71. package/dist/{start-tui-Bz23_yym.js → start-tui-UNe2xQ0U.js} +25 -25
  72. package/dist/start-tui-UNe2xQ0U.js.map +1 -0
  73. package/dist/{steps-DbsDish5.js → steps-C2yqOk_h.js} +7 -7
  74. package/dist/{steps-DbsDish5.js.map → steps-C2yqOk_h.js.map} +1 -1
  75. package/dist/{store-D5ROpUje.js → store-Bic4Cr3L.js} +5 -32
  76. package/dist/store-Bic4Cr3L.js.map +1 -0
  77. package/dist/{subagent-CSBZnhgK.js → subagent-Dz41vV9j.js} +2 -2
  78. package/dist/{subagent-CSBZnhgK.js.map → subagent-Dz41vV9j.js.map} +1 -1
  79. package/dist/{task-D8sTR7jZ.js → task-5-fw_Vw3.js} +14 -14
  80. package/dist/{task-D8sTR7jZ.js.map → task-5-fw_Vw3.js.map} +1 -1
  81. package/dist/{task-stream-Dl0XEsZ9.js → task-stream-dVzl6b_q.js} +6 -6
  82. package/dist/{task-stream-Dl0XEsZ9.js.map → task-stream-dVzl6b_q.js.map} +1 -1
  83. package/dist/{tasks-BDt427kT.js → tasks-D1c2z1aw.js} +2 -2
  84. package/dist/{tasks-BDt427kT.js.map → tasks-D1c2z1aw.js.map} +1 -1
  85. package/dist/{telemetry-Dp6zjZBj.js → telemetry-BRnxBRWK.js} +3 -3
  86. package/dist/{telemetry-Dp6zjZBj.js.map → telemetry-BRnxBRWK.js.map} +1 -1
  87. package/dist/{terminal-pInlleVD.js → terminal-9bvRa4Qt.js} +38 -34
  88. package/dist/terminal-9bvRa4Qt.js.map +1 -0
  89. package/dist/{tools-MkyMOjQI.js → tools-Dp6i9k70.js} +8 -8
  90. package/dist/{tools-MkyMOjQI.js.map → tools-Dp6i9k70.js.map} +1 -1
  91. package/dist/{wizard-abort-BHLKVKLN.js → wizard-abort-CAcInAxg.js} +1 -1
  92. package/dist/{wizard-abort-BCnUgT9z.js → wizard-abort-lE6umflt.js} +4 -4
  93. package/dist/{wizard-abort-BCnUgT9z.js.map → wizard-abort-lE6umflt.js.map} +1 -1
  94. package/dist/{yara-hooks-BJWVgTpU.js → yara-hooks-BTNJ6MGW.js} +4 -4
  95. package/dist/{yara-hooks-BJWVgTpU.js.map → yara-hooks-BTNJ6MGW.js.map} +1 -1
  96. package/dist/yara-hooks-CFY4pGcW.js +2 -0
  97. package/package.json +8 -1
  98. package/dist/add-mcp-server-to-clients-Exddicp2.js.map +0 -1
  99. package/dist/agent-interface-CZWRgZMc.js.map +0 -1
  100. package/dist/agent-prompt-loader-BobuLDIg.js.map +0 -1
  101. package/dist/agent-runner-C_R1X_UT.js.map +0 -1
  102. package/dist/analytics-DXvdQqyp.js +0 -2
  103. package/dist/bounded-fs-BHLy83N9.js.map +0 -1
  104. package/dist/debug-C5Zz8hmm.js.map +0 -1
  105. package/dist/defaults-DulzMJpN.js.map +0 -1
  106. package/dist/errors--OqLxmWR.js.map +0 -1
  107. package/dist/mint-failure-CZ5FZ0Jd.js.map +0 -1
  108. package/dist/orchestrator-tools-CLADku26.js.map +0 -1
  109. package/dist/playground-CZgZX4j0.js.map +0 -1
  110. package/dist/queue-tools-D09PNS0X.js.map +0 -1
  111. package/dist/setup-utils-BUAoRjw3.js.map +0 -1
  112. package/dist/start-tui-Bz23_yym.js.map +0 -1
  113. package/dist/store-D5ROpUje.js.map +0 -1
  114. package/dist/terminal-pInlleVD.js.map +0 -1
  115. package/dist/yara-hooks-Dp82xJGd.js +0 -2
@@ -1,18 +1,18 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { p as getUI, r as debug, xt as runtimeEnv } from "./debug-C5Zz8hmm.js";
3
- import { n as analytics } from "./analytics-C_RyE8fz.js";
4
- import { n as openTrackedLink, t as withProgress } from "./telemetry-Dp6zjZBj.js";
5
- import { a as getNativeHTTPServerConfig, c as namesWithStatus, d as toClientResult, i as getDefaultServerConfig, l as redactSecrets, r as buildMCPUrl, t as ALL_FEATURE_VALUES } from "./defaults-DulzMJpN.js";
2
+ import { Ct as runtimeEnv, p as getUI, r as debug } from "./debug-B4kzIZPi.js";
3
+ import { n as analytics } from "./analytics-C67F2vee.js";
4
+ import { n as openTrackedLink, t as withProgress } from "./telemetry-BRnxBRWK.js";
5
+ import { a as getNativeHTTPServerConfig, d as scrubHomePaths, i as getDefaultServerConfig, l as namesWithStatus, p as toClientResult, r as buildMCPUrl, s as expectedFailureHint, t as ALL_FEATURE_VALUES, u as redactSecrets } from "./defaults-4c4YwTmW.js";
6
6
  import * as fs$2 from "fs";
7
7
  import * as path$3 from "path";
8
8
  import * as os from "node:os";
9
9
  import * as path$2 from "node:path";
10
- import { execSync, spawnSync } from "node:child_process";
10
+ import { execFile, execSync } from "node:child_process";
11
11
  import * as fs$1 from "node:fs";
12
12
  import z$1 from "zod";
13
13
  import * as os$1 from "os";
14
14
  import * as jsonc from "jsonc-parser";
15
- import { execSync as execSync$1 } from "child_process";
15
+ import { execFile as execFile$1, execSync as execSync$1 } from "child_process";
16
16
  //#region src/steps/add-mcp-server-to-clients/MCPClient.ts
17
17
  var MCPClient = class {
18
18
  name;
@@ -129,6 +129,31 @@ var CursorMCPClient = class extends DefaultMCPClient {
129
129
  };
130
130
  //#endregion
131
131
  //#region src/steps/add-mcp-server-to-clients/clients/claude-code.ts
132
+ const PLUGIN_NAME = "posthog";
133
+ const PLUGIN_MARKETPLACE$1 = "posthog";
134
+ const PLUGIN_MARKETPLACE_SOURCE$1 = "PostHog/ai-plugin";
135
+ const PLUGIN_REF$1 = `${PLUGIN_NAME}@${PLUGIN_MARKETPLACE$1}`;
136
+ /** The plugin is missing from every catalog the CLI can currently see. */
137
+ const NOT_IN_A_MARKETPLACE = /not found in/i;
138
+ /**
139
+ * `plugin marketplace add` clones a git repository, the slowest thing we run.
140
+ * Past this the command is not slow, it is stuck, and a clone waiting on a
141
+ * credential prompt would otherwise hang the wizard with the spinner frozen.
142
+ */
143
+ const RUN_TIMEOUT_MS$1 = 12e4;
144
+ /**
145
+ * `execFile` kills the child once output passes this. The default is 1 MB,
146
+ * which a verbose clone can reach, and the kill then looks like Claude Code
147
+ * rejecting the install rather than us cutting it off.
148
+ */
149
+ const MAX_OUTPUT_BYTES$1 = 16 * 1024 * 1024;
150
+ /**
151
+ * Our catalog, by name *and* repository. A marketplace merely called `posthog`
152
+ * may be someone else's, and neither installing from it nor removing it is
153
+ * ours to do. `repo` is absent on CLIs predating it, where the name is all
154
+ * there is.
155
+ */
156
+ const isOurMarketplace = (m) => m?.name === PLUGIN_MARKETPLACE$1 && (m.repo === void 0 || m.repo.toLowerCase() === PLUGIN_MARKETPLACE_SOURCE$1.toLowerCase());
132
157
  var ClaudeCodeMCPClient = class extends DefaultMCPClient {
133
158
  name = "Claude Code";
134
159
  claudeBinaryPath = null;
@@ -235,21 +260,39 @@ var ClaudeCodeMCPClient = class extends DefaultMCPClient {
235
260
  success: false,
236
261
  reason: "The claude CLI is no longer on your PATH."
237
262
  };
238
- if (!await this.isPluginInstalled()) return {
263
+ const ids = await this.installedPluginIds(binary);
264
+ const marketplace = await this.ourMarketplaceRegistered(binary);
265
+ const nothingInstalled = ids?.length === 0 || ids === null && !await this.isPluginInstalled();
266
+ if (nothingInstalled && !marketplace) return {
239
267
  success: true,
240
268
  alreadyInstalled: true
241
269
  };
242
- try {
243
- execSync$1(`${binary} plugin uninstall posthog`, { stdio: "pipe" });
244
- return { success: true };
245
- } catch (error) {
246
- const msg = error instanceof Error ? error.message : String(error);
247
- analytics.captureException(/* @__PURE__ */ new Error(`Claude Code plugin uninstall failed: ${msg}`));
248
- return {
249
- success: false,
250
- reason: msg
251
- };
270
+ const targets = nothingInstalled ? [] : ids?.length ? ids : [PLUGIN_NAME];
271
+ const failures = [];
272
+ for (const target of targets) {
273
+ const run = await this.runClaude(binary, [
274
+ "plugin",
275
+ "uninstall",
276
+ target
277
+ ]);
278
+ if (!run.ok) failures.push(`${target}: ${run.output}`);
252
279
  }
280
+ if (marketplace) {
281
+ const run = await this.runClaude(binary, [
282
+ "plugin",
283
+ "marketplace",
284
+ "remove",
285
+ PLUGIN_MARKETPLACE$1
286
+ ]);
287
+ if (!run.ok) failures.push(`${PLUGIN_MARKETPLACE$1}: ${run.output}`);
288
+ }
289
+ if (failures.length === 0) return { success: true };
290
+ const reason = failures.join("; ");
291
+ analytics.captureException(/* @__PURE__ */ new Error(`Claude Code plugin uninstall failed: ${reason}`));
292
+ return {
293
+ success: false,
294
+ reason
295
+ };
253
296
  }
254
297
  removeServer(local) {
255
298
  const claudeBinary = this.findClaudeBinary();
@@ -281,40 +324,134 @@ var ClaudeCodeMCPClient = class extends DefaultMCPClient {
281
324
  supportsPlugin() {
282
325
  return this.findClaudeBinary() !== null;
283
326
  }
284
- isPluginInstalled() {
285
- const binary = this.findClaudeBinary();
286
- if (!binary) return Promise.resolve(false);
327
+ runClaude(binary, args) {
328
+ return new Promise((resolve) => {
329
+ execFile$1(binary, args, {
330
+ timeout: RUN_TIMEOUT_MS$1,
331
+ maxBuffer: MAX_OUTPUT_BYTES$1
332
+ }, (error, stdout) => {
333
+ if (!error) return resolve({
334
+ ok: true,
335
+ output: stdout?.toString() ?? ""
336
+ });
337
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return resolve({
338
+ ok: false,
339
+ output: "claude produced more output than the wizard can hold, so it was stopped"
340
+ });
341
+ if (error.killed || error.signal) return resolve({
342
+ ok: false,
343
+ output: `claude did not finish within ${RUN_TIMEOUT_MS$1 / 1e3}s, so it was stopped`
344
+ });
345
+ resolve({
346
+ ok: false,
347
+ output: scrubHomePaths(redactSecrets(error.message))
348
+ });
349
+ });
350
+ });
351
+ }
352
+ /** Null when `--json` is unsupported, the command failed, or output didn't parse — never "nothing installed". */
353
+ async listJson(binary, args) {
354
+ const result = await this.runClaude(binary, [...args, "--json"]);
355
+ if (!result.ok) return null;
287
356
  try {
288
- const output = execSync$1(`${binary} plugin list`, { stdio: "pipe" }).toString();
289
- return Promise.resolve(output.toLowerCase().includes("posthog"));
357
+ const parsed = JSON.parse(result.output);
358
+ return Array.isArray(parsed) ? parsed : null;
290
359
  } catch {
291
- return Promise.resolve(false);
360
+ return null;
292
361
  }
293
362
  }
363
+ async installedPlugins(binary) {
364
+ const listed = await this.listJson(binary, ["plugin", "list"]);
365
+ if (!listed) return null;
366
+ return listed.filter((p) => typeof p?.id === "string" && p.id.split("@")[0] === PLUGIN_NAME);
367
+ }
368
+ async installedPluginIds(binary) {
369
+ return (await this.installedPlugins(binary))?.map((p) => p.id) ?? null;
370
+ }
371
+ async isPluginInstalled() {
372
+ const binary = this.findClaudeBinary();
373
+ if (!binary) return false;
374
+ const listed = await this.installedPlugins(binary);
375
+ if (listed) return listed.length > 0;
376
+ const plain = await this.runClaude(binary, ["plugin", "list"]);
377
+ return plain.ok && plain.output.toLowerCase().includes(PLUGIN_NAME);
378
+ }
294
379
  async installPlugin() {
295
380
  const binary = this.findClaudeBinary();
296
381
  if (!binary) return {
297
382
  success: false,
298
383
  reason: "The claude CLI is no longer on your PATH."
299
384
  };
300
- if (await this.isPluginInstalled()) return {
385
+ const listed = await this.installedPlugins(binary);
386
+ if (listed ? listed.some((p) => p.enabled !== false) : await this.isPluginInstalled()) return {
301
387
  success: true,
302
388
  alreadyInstalled: true
303
389
  };
304
- try {
305
- execSync$1(`${binary} plugin install posthog`, { stdio: "pipe" });
306
- return { success: true };
307
- } catch (error) {
308
- const msg = error instanceof Error ? error.message : String(error);
309
- if (msg.includes("already installed") || msg.includes("already exists")) return {
310
- success: true,
311
- alreadyInstalled: true
312
- };
313
- analytics.captureException(/* @__PURE__ */ new Error(`Claude Code plugin install failed: ${msg}`));
314
- return {
315
- success: false,
316
- reason: msg
317
- };
390
+ const marketplaceFailure = await this.ensurePluginMarketplace(binary);
391
+ let result = await this.runClaude(binary, [
392
+ "plugin",
393
+ "install",
394
+ PLUGIN_REF$1
395
+ ]);
396
+ if (!result.ok && NOT_IN_A_MARKETPLACE.test(result.output)) {
397
+ await this.runClaude(binary, [
398
+ "plugin",
399
+ "marketplace",
400
+ "update",
401
+ PLUGIN_MARKETPLACE$1
402
+ ]);
403
+ result = await this.runClaude(binary, [
404
+ "plugin",
405
+ "install",
406
+ PLUGIN_REF$1
407
+ ]);
408
+ }
409
+ if (!result.ok && NOT_IN_A_MARKETPLACE.test(result.output)) result = await this.runClaude(binary, [
410
+ "plugin",
411
+ "install",
412
+ PLUGIN_NAME
413
+ ]);
414
+ if (result.ok) return { success: true };
415
+ const msg = result.output;
416
+ if (msg.includes("already installed") || msg.includes("already exists")) return {
417
+ success: true,
418
+ alreadyInstalled: true
419
+ };
420
+ const failure = /* @__PURE__ */ new Error(`Claude Code plugin install failed: ${msg}`);
421
+ if (marketplaceFailure) analytics.captureException(failure, { marketplaceFailure });
422
+ else analytics.captureException(failure);
423
+ return {
424
+ success: false,
425
+ reason: msg
426
+ };
427
+ }
428
+ /** Registered, and ours: a catalog merely named `posthog` is someone else's. */
429
+ async ourMarketplaceRegistered(binary) {
430
+ const listed = await this.listJson(binary, [
431
+ "plugin",
432
+ "marketplace",
433
+ "list"
434
+ ]);
435
+ return listed ? listed.some(isOurMarketplace) : null;
436
+ }
437
+ async ensurePluginMarketplace(binary) {
438
+ if ((await this.listJson(binary, [
439
+ "plugin",
440
+ "marketplace",
441
+ "list"
442
+ ]))?.some(isOurMarketplace)) {
443
+ debug(` Marketplace ${PLUGIN_MARKETPLACE$1} already registered`);
444
+ return;
445
+ }
446
+ const added = await this.runClaude(binary, [
447
+ "plugin",
448
+ "marketplace",
449
+ "add",
450
+ PLUGIN_MARKETPLACE_SOURCE$1
451
+ ]);
452
+ if (!added.ok) {
453
+ debug(` Marketplace add failed: ${added.output}`);
454
+ return added.output;
318
455
  }
319
456
  }
320
457
  };
@@ -429,6 +566,163 @@ var ZedClient = class extends DefaultMCPClient {
429
566
  };
430
567
  //#endregion
431
568
  //#region src/steps/add-mcp-server-to-clients/clients/codex.ts
569
+ const PLUGIN_MARKETPLACE = "posthog";
570
+ const PLUGIN_MARKETPLACE_SOURCE = "PostHog/ai-plugin";
571
+ const PLUGIN_REF = `posthog@${PLUGIN_MARKETPLACE}`;
572
+ /**
573
+ * The plugin's row in `codex plugin list`, whose STATUS column reads
574
+ * `installed, enabled` or `not installed`. Registering the marketplace only
575
+ * publishes the catalog, so the marketplace section in config.toml says nothing
576
+ * about whether the plugin itself is there.
577
+ */
578
+ const listedAsInstalled = (stdout) => {
579
+ const row = new RegExp(`^${PLUGIN_REF}\\s+(.*)$`, "m").exec(stdout)?.[1];
580
+ return !!row && /\binstalled\b/.test(row) && !/\bnot installed\b/.test(row);
581
+ };
582
+ const PLUGIN_STAGES = [
583
+ "plugin install",
584
+ "plugin uninstall",
585
+ "marketplace add"
586
+ ];
587
+ const MCP_STAGES = ["MCP add", "MCP remove"];
588
+ const ALL_STAGES = [...PLUGIN_STAGES, ...MCP_STAGES];
589
+ /**
590
+ * Failures in the user's own environment. Reporting them files issues nobody
591
+ * can action, so hand back a hint instead. Drawn from what the codex install
592
+ * path actually produced in the field.
593
+ *
594
+ * Every entry is scoped: a hint that fires on the wrong stage tells the user to
595
+ * run a command that has nothing to do with what just failed.
596
+ */
597
+ const EXPECTED_FAILURES = [
598
+ {
599
+ match: /unexpected argument|unknown (command|option|argument)|Missing option/i,
600
+ stages: PLUGIN_STAGES,
601
+ hint: "your codex CLI is too old for plugins — update codex, then run `codex plugin marketplace add PostHog/ai-plugin`"
602
+ },
603
+ {
604
+ match: /unexpected argument|unknown (command|option|argument)|Missing option/i,
605
+ stages: MCP_STAGES,
606
+ hint: "your codex CLI is too old for this install — update codex, then run `npx @posthog/wizard mcp add` again"
607
+ },
608
+ {
609
+ match: /ENOENT|not found in PATH|spawn .* ENOENT/i,
610
+ stages: ALL_STAGES,
611
+ hint: "your codex install looks broken — reinstall codex, then run `codex plugin marketplace add PostHog/ai-plugin`"
612
+ },
613
+ {
614
+ match: /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,
615
+ stages: ALL_STAGES,
616
+ unless: /ai-plugin|plugin\.toml|plugin\.json|marketplace\.toml|posthog@posthog/i,
617
+ hint: "codex could not read its own config — fix what it reports in ~/.codex/config.toml, then retry"
618
+ },
619
+ {
620
+ match: /EACCES|EPERM|permission denied|read-only file system|not permitted/i,
621
+ stages: ALL_STAGES,
622
+ unless: /permission denied \((?:publickey|password|gssapi)/i,
623
+ hint: "codex could not write to its config — fix the permissions on ~/.codex, then retry"
624
+ },
625
+ {
626
+ match: /ENOSPC|no space left/i,
627
+ stages: ALL_STAGES,
628
+ hint: "the disk is full — free some space, then retry"
629
+ },
630
+ {
631
+ match: /git clone .* failed|ENOTFOUND|ETIMEDOUT|ECONNREFUSED|could not resolve host/i,
632
+ stages: ALL_STAGES,
633
+ unless: /repository not found|not found|404|does not exist|authentication failed/i,
634
+ hint: "codex could not reach GitHub to download the plugin — check your network, then retry"
635
+ }
636
+ ];
637
+ /**
638
+ * A clone of the plugin marketplace is the slowest thing we run. Past this the
639
+ * command is not slow, it is stuck, and the wizard should say so rather than
640
+ * wait forever.
641
+ */
642
+ const RUN_TIMEOUT_MS = 12e4;
643
+ /**
644
+ * `execFile` kills the child once output passes this. The default is 1 MB,
645
+ * which a verbose clone can reach, and the kill then looks like codex rejecting
646
+ * the install rather than us cutting it off.
647
+ */
648
+ const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
649
+ /**
650
+ * What `execFile` puts in `error.message` when the command itself never spoke:
651
+ * the full binary path and arguments. That path contains the user's home
652
+ * directory, so reporting it fingerprints one root cause per machine — the
653
+ * failure this reporting was built to stop. Never send it onward.
654
+ */
655
+ const COMMAND_FAILED_PREAMBLE = /^Command failed:.*$/m;
656
+ /**
657
+ * The cause when neither stream carried one. `execFile` always sets a message,
658
+ * so there is no "empty error" case to fall through — the message just isn't
659
+ * usable as it stands.
660
+ */
661
+ const describeExecFailure = (error) => {
662
+ if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return "codex produced more output than the wizard can hold, so it was stopped";
663
+ if (error.killed) return `codex did not finish within ${RUN_TIMEOUT_MS / 1e3}s, so it was stopped`;
664
+ if (error.signal) return `codex was killed by ${error.signal}`;
665
+ const said = error.message.replace(COMMAND_FAILED_PREAMBLE, "").trim();
666
+ if (said) return said;
667
+ if (typeof error.code === "number") return `codex exited with code ${error.code}`;
668
+ if (error.code) return `codex failed with ${error.code}`;
669
+ return "codex failed without reporting a reason";
670
+ };
671
+ /**
672
+ * One codex invocation. Async on purpose: `plugin marketplace add` and
673
+ * `plugin add` clone git repositories and take seconds, and a synchronous child
674
+ * process blocks the event loop, which freezes the TUI spinner on its first
675
+ * frame. Never throws; the caller decides what a failure means.
676
+ *
677
+ * A failure's cause is split across the error and the two streams, and none of
678
+ * them carries it when the process merely exits non-zero — reading one alone
679
+ * reported a blank reason and filed an exception carrying nothing.
680
+ */
681
+ const runCodex = (binary, args, env) => new Promise((resolve) => {
682
+ execFile(binary, args, {
683
+ env,
684
+ timeout: RUN_TIMEOUT_MS,
685
+ maxBuffer: MAX_OUTPUT_BYTES
686
+ }, (error, stdout, stderr) => {
687
+ if (!error) return resolve({
688
+ ok: true,
689
+ output: stdout ?? ""
690
+ });
691
+ const said = [stderr, stdout].map((p) => p ? String(p).trim() : "").filter(Boolean).join("\n");
692
+ resolve({
693
+ ok: false,
694
+ output: redactSecrets(error.killed || !!error.signal || error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || !said ? describeExecFailure(error) : said)
695
+ });
696
+ }).stdin?.end();
697
+ });
698
+ /**
699
+ * Turn a failed spawn into a result: an expected local failure becomes a hint,
700
+ * anything else is reported under a constant message so one root cause stays
701
+ * one issue, with the varying detail in properties.
702
+ */
703
+ const reportSpawnFailure = (stage, details) => {
704
+ const hint = expectedFailureHint(details, EXPECTED_FAILURES, stage);
705
+ if (hint) {
706
+ analytics.wizardCapture("mcp expected failure hinted", {
707
+ client: "Codex",
708
+ stage,
709
+ hint,
710
+ details: scrubHomePaths(details)
711
+ });
712
+ return {
713
+ success: false,
714
+ reason: hint
715
+ };
716
+ }
717
+ analytics.captureException(/* @__PURE__ */ new Error(`Codex ${stage} failed`), {
718
+ stage,
719
+ details: scrubHomePaths(details)
720
+ });
721
+ return {
722
+ success: false,
723
+ reason: details
724
+ };
725
+ };
432
726
  /** Wording codex uses when the thing we're adding is already registered. */
433
727
  const ALREADY_INSTALLED_PATTERN = /already (installed|exists|added|registered)/i;
434
728
  /** Wording that means the opposite: a cache entry exists but the plugin doesn't. */
@@ -490,16 +784,16 @@ var CodexMCPClient = class extends DefaultMCPClient {
490
784
  return Promise.resolve(false);
491
785
  }
492
786
  }
493
- addServer(apiKey, selectedFeatures, local) {
787
+ async addServer(apiKey, selectedFeatures, local) {
494
788
  const serverName = local ? "posthog-local" : "posthog";
495
789
  const url = buildMCPUrl(selectedFeatures, local);
496
790
  if (apiKey) {
497
791
  const binary = this.findCodexBinary();
498
- if (!binary) return Promise.resolve({
792
+ if (!binary) return {
499
793
  success: false,
500
794
  reason: "An API-key install into Codex needs the codex CLI."
501
- });
502
- const result = spawnSync(binary, [
795
+ };
796
+ const result = await runCodex(binary, [
503
797
  "mcp",
504
798
  "add",
505
799
  serverName,
@@ -508,28 +802,19 @@ var CodexMCPClient = class extends DefaultMCPClient {
508
802
  "--bearer-token-env-var",
509
803
  "POSTHOG_AUTH_HEADER"
510
804
  ], {
511
- encoding: "utf-8",
512
- env: {
513
- ...process.env,
514
- POSTHOG_AUTH_HEADER: `Bearer ${apiKey}`
515
- }
805
+ ...process.env,
806
+ POSTHOG_AUTH_HEADER: `Bearer ${apiKey}`
516
807
  });
517
- if (result.status !== 0) {
518
- const stderr = result.stderr ?? "";
519
- if (ALREADY_INSTALLED_PATTERN.test(stderr)) return Promise.resolve({
808
+ if (!result.ok) {
809
+ if (ALREADY_INSTALLED_PATTERN.test(result.output)) return {
520
810
  success: true,
521
811
  alreadyInstalled: true
522
- });
523
- const reason = redactSecrets(stderr);
524
- analytics.captureException(/* @__PURE__ */ new Error(`Codex MCP add failed: ${reason}`));
525
- return Promise.resolve({
526
- success: false,
527
- reason
528
- });
812
+ };
813
+ return reportSpawnFailure("MCP add", result.output);
529
814
  }
530
- return Promise.resolve({ success: true });
815
+ return { success: true };
531
816
  }
532
- return Promise.resolve(this.writeServerSection(serverName, url));
817
+ return this.writeServerSection(serverName, url);
533
818
  }
534
819
  /**
535
820
  * Create or update the `[mcp_servers.<name>]` section in config.toml, editing
@@ -561,7 +846,7 @@ var CodexMCPClient = class extends DefaultMCPClient {
561
846
  return { success: true };
562
847
  } catch (error) {
563
848
  const reason = error instanceof Error ? error.message : String(error);
564
- analytics.captureException(/* @__PURE__ */ new Error(`Codex config.toml write failed: ${reason}`));
849
+ analytics.captureException(/* @__PURE__ */ new Error("Codex config.toml write failed"), { details: scrubHomePaths(reason) });
565
850
  return {
566
851
  success: false,
567
852
  reason
@@ -579,26 +864,19 @@ var CodexMCPClient = class extends DefaultMCPClient {
579
864
  if (!this.findCodexBinary()) return null;
580
865
  return `codex mcp login ${local ? "posthog-local" : "posthog"}`;
581
866
  }
582
- removeServer(local) {
867
+ async removeServer(local) {
583
868
  const binary = this.findCodexBinary();
584
- if (!binary) return Promise.resolve({
869
+ if (!binary) return {
585
870
  success: false,
586
871
  reason: "The codex CLI is no longer on your PATH."
587
- });
588
- const result = spawnSync(binary, [
872
+ };
873
+ const result = await runCodex(binary, [
589
874
  "mcp",
590
875
  "remove",
591
876
  local ? "posthog-local" : "posthog"
592
- ], { encoding: "utf-8" });
593
- if (result.error || result.status !== 0) {
594
- const reason = redactSecrets(result.error?.message ?? result.stderr ?? "codex mcp remove failed");
595
- analytics.captureException(/* @__PURE__ */ new Error(`Failed to remove server from Codex CLI: ${reason}`));
596
- return Promise.resolve({
597
- success: false,
598
- reason
599
- });
600
- }
601
- return Promise.resolve({ success: true });
877
+ ]);
878
+ if (!result.ok) return reportSpawnFailure("MCP remove", result.output);
879
+ return { success: true };
602
880
  }
603
881
  /** The codex marketplace plugin ships skills only — the MCP server needs its own entry. */
604
882
  pluginBundlesMcpServer() {
@@ -607,13 +885,33 @@ var CodexMCPClient = class extends DefaultMCPClient {
607
885
  supportsPlugin() {
608
886
  return this.findCodexBinary() !== null;
609
887
  }
610
- isPluginInstalled() {
611
- const configPath = path$2.join(os.homedir(), ".codex", "config.toml");
888
+ /** The plugin itself, as the CLI reports it. The install decision's question. */
889
+ async isPluginPresent() {
890
+ const binary = this.findCodexBinary();
891
+ if (!binary) return false;
892
+ const result = await runCodex(binary, [
893
+ "plugin",
894
+ "list",
895
+ "-m",
896
+ PLUGIN_MARKETPLACE
897
+ ]);
898
+ return result.ok && listedAsInstalled(result.output);
899
+ }
900
+ /**
901
+ * Anything of ours left on the machine, which is what `index.ts` asks before
902
+ * offering removal. A registered marketplace with no plugin is a state the
903
+ * wizard created — and the state this branch exists to correct — so removal
904
+ * has to see it, or `removePlugin` never runs and the catalog stays forever.
905
+ */
906
+ async isPluginInstalled() {
907
+ return await this.isPluginPresent() || this.isMarketplaceRegistered();
908
+ }
909
+ /** The catalog, which the plugin is installed *from* — not the plugin. */
910
+ isMarketplaceRegistered() {
612
911
  try {
613
- const contents = fs$1.readFileSync(configPath, "utf-8");
614
- return Promise.resolve(contents.toLowerCase().includes("[marketplaces.posthog]"));
912
+ return fs$1.readFileSync(this.configPath(), "utf-8").toLowerCase().includes(`[marketplaces.${PLUGIN_MARKETPLACE}]`);
615
913
  } catch {
616
- return Promise.resolve(false);
914
+ return false;
617
915
  }
618
916
  }
619
917
  async removePlugin() {
@@ -622,23 +920,25 @@ var CodexMCPClient = class extends DefaultMCPClient {
622
920
  success: false,
623
921
  reason: "The codex CLI is no longer on your PATH."
624
922
  };
625
- if (!await this.isPluginInstalled()) return {
626
- success: true,
627
- alreadyInstalled: true
628
- };
629
- const result = spawnSync(binary, [
923
+ const steps = [];
924
+ if (await this.isPluginPresent()) steps.push([
925
+ "plugin",
926
+ "remove",
927
+ PLUGIN_REF
928
+ ]);
929
+ if (this.isMarketplaceRegistered()) steps.push([
630
930
  "plugin",
631
931
  "marketplace",
632
932
  "remove",
633
- "posthog"
634
- ], { encoding: "utf-8" });
635
- if (result.status !== 0) {
636
- const reason = redactSecrets(result.stderr ?? "codex plugin marketplace remove failed");
637
- analytics.captureException(/* @__PURE__ */ new Error(`Codex plugin uninstall failed: ${reason}`));
638
- return {
639
- success: false,
640
- reason
641
- };
933
+ PLUGIN_MARKETPLACE
934
+ ]);
935
+ if (steps.length === 0) return {
936
+ success: true,
937
+ alreadyInstalled: true
938
+ };
939
+ for (const args of steps) {
940
+ const result = await runCodex(binary, args);
941
+ if (!result.ok) return reportSpawnFailure("plugin uninstall", result.output);
642
942
  }
643
943
  return { success: true };
644
944
  }
@@ -648,39 +948,53 @@ var CodexMCPClient = class extends DefaultMCPClient {
648
948
  success: false,
649
949
  reason: "The codex CLI is no longer on your PATH."
650
950
  };
651
- if (await this.isPluginInstalled()) return {
951
+ if (await this.isPluginPresent()) return {
652
952
  success: true,
653
953
  alreadyInstalled: true
654
954
  };
655
- const run = () => spawnSync(binary, [
955
+ const marketplace = this.isMarketplaceRegistered() ? null : await this.registerMarketplace(binary);
956
+ if (marketplace && !marketplace.success) return marketplace;
957
+ const result = await runCodex(binary, [
958
+ "plugin",
959
+ "add",
960
+ PLUGIN_REF
961
+ ]);
962
+ if (!result.ok) {
963
+ if (ALREADY_INSTALLED_PATTERN.test(result.output)) return {
964
+ success: true,
965
+ alreadyInstalled: true
966
+ };
967
+ return reportSpawnFailure("plugin install", result.output);
968
+ }
969
+ return { success: true };
970
+ }
971
+ /**
972
+ * Add the catalog the plugin is published in. A stale cache directory with no
973
+ * config.toml entry reports the marketplace as added from a different source;
974
+ * clear it and retry once.
975
+ */
976
+ async registerMarketplace(binary) {
977
+ const run = () => runCodex(binary, [
656
978
  "plugin",
657
979
  "marketplace",
658
980
  "add",
659
- "PostHog/ai-plugin"
660
- ], { encoding: "utf-8" });
661
- let result = run();
662
- if (result.status !== 0 && STALE_MARKETPLACE_CACHE.test(result.stderr ?? "")) {
663
- const staleDir = path$2.join(os.homedir(), ".codex", ".tmp", "marketplaces", "posthog");
981
+ PLUGIN_MARKETPLACE_SOURCE
982
+ ]);
983
+ let result = await run();
984
+ if (!result.ok && STALE_MARKETPLACE_CACHE.test(result.output)) {
985
+ const staleDir = path$2.join(os.homedir(), ".codex", ".tmp", "marketplaces", PLUGIN_MARKETPLACE);
664
986
  try {
665
987
  fs$1.rmSync(staleDir, {
666
988
  recursive: true,
667
989
  force: true
668
990
  });
669
991
  } catch {}
670
- result = run();
992
+ result = await run();
671
993
  }
672
- if (result.status !== 0) {
673
- const stderr = result.stderr ?? "";
674
- if (ALREADY_INSTALLED_PATTERN.test(stderr) && !STALE_MARKETPLACE_CACHE.test(stderr)) return {
675
- success: true,
676
- alreadyInstalled: true
677
- };
678
- const reason = redactSecrets(stderr);
679
- analytics.captureException(/* @__PURE__ */ new Error(`Codex plugin install failed: ${reason}`));
680
- return {
681
- success: false,
682
- reason
683
- };
994
+ if (!result.ok) {
995
+ const details = result.output;
996
+ if (ALREADY_INSTALLED_PATTERN.test(details) && !STALE_MARKETPLACE_CACHE.test(details)) return { success: true };
997
+ return reportSpawnFailure("marketplace add", details);
684
998
  }
685
999
  return { success: true };
686
1000
  }
@@ -900,4 +1214,4 @@ const removeMCPServer = async (clients, local) => {
900
1214
  //#endregion
901
1215
  export { getSupportedClients as a, removeMCPServer as c, getInstalledClients as i, removeMCPServerFromClientsStep as l, addMCPServerToClientsStep as n, getSupportedPluginClients as o, add_mcp_server_to_clients_exports as r, installPlugins as s, addMCPServer as t, isPluginCapable as u };
902
1216
 
903
- //# sourceMappingURL=add-mcp-server-to-clients-Exddicp2.js.map
1217
+ //# sourceMappingURL=add-mcp-server-to-clients-BnRyE4pw.js.map