@posthog/wizard 2.60.0 → 2.61.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 (106) hide show
  1. package/dist/{add-mcp-server-to-clients-CM30npkB.js → add-mcp-server-to-clients-KNIHRN_m.js} +190 -60
  2. package/dist/add-mcp-server-to-clients-KNIHRN_m.js.map +1 -0
  3. package/dist/{agent-interface-CybBFwnk.js → agent-interface-Bm4ZNGGV.js} +7 -7
  4. package/dist/{agent-interface-CybBFwnk.js.map → agent-interface-Bm4ZNGGV.js.map} +1 -1
  5. package/dist/{agent-prompt-loader-DL32ioJX.js → agent-prompt-loader-CsHNQXsY.js} +38 -11
  6. package/dist/agent-prompt-loader-CsHNQXsY.js.map +1 -0
  7. package/dist/{agent-runner-DUyxA1TQ.js → agent-runner-DLAvB67P.js} +119 -19
  8. package/dist/agent-runner-DLAvB67P.js.map +1 -0
  9. package/dist/{analytics-B9V7x1Ma.js → analytics-CkIyBHfd.js} +2 -2
  10. package/dist/{analytics-B9V7x1Ma.js.map → analytics-CkIyBHfd.js.map} +1 -1
  11. package/dist/analytics-kxgLEv7U.js +2 -0
  12. package/dist/{api-OHLXhKTy.js → api-XHQTOfIi.js} +3 -3
  13. package/dist/{api-OHLXhKTy.js.map → api-XHQTOfIi.js.map} +1 -1
  14. package/dist/bin.js +288 -134
  15. package/dist/bin.js.map +1 -1
  16. package/dist/{bounded-fs-xkhfF3JS.js → bounded-fs-B-qlKB0h.js} +3 -3
  17. package/dist/{bounded-fs-xkhfF3JS.js.map → bounded-fs-B-qlKB0h.js.map} +1 -1
  18. package/dist/{ci-install-B31ciXo4.js → ci-install-C9BAoO2n.js} +4 -4
  19. package/dist/{ci-install-B31ciXo4.js.map → ci-install-C9BAoO2n.js.map} +1 -1
  20. package/dist/{debug-D3Ucw2y1.js → debug-8-IIHr5f.js} +6 -3
  21. package/dist/debug-8-IIHr5f.js.map +1 -0
  22. package/dist/{debug-Cx9Delbc.js → debug-npp6DzBK.js} +1 -1
  23. package/dist/{defaults-BWLIffh5.js → defaults-mrAre0xM.js} +28 -2
  24. package/dist/defaults-mrAre0xM.js.map +1 -0
  25. package/dist/{environment-BN8ROaHv.js → environment-BffaZJRI.js} +4 -4
  26. package/dist/{environment-BN8ROaHv.js.map → environment-BffaZJRI.js.map} +1 -1
  27. package/dist/{file-watcher-CzBnjben.js → file-watcher-BreVxzh0.js} +2 -2
  28. package/dist/{file-watcher-CzBnjben.js.map → file-watcher-BreVxzh0.js.map} +1 -1
  29. package/dist/{headless-ui-DoQlIoeh.js → headless-ui-Dy_l8hCT.js} +2 -2
  30. package/dist/{headless-ui-DoQlIoeh.js.map → headless-ui-Dy_l8hCT.js.map} +1 -1
  31. package/dist/{interactive-DeFFwXME.js → interactive-C9vepji1.js} +2 -2
  32. package/dist/{interactive-DeFFwXME.js.map → interactive-C9vepji1.js.map} +1 -1
  33. package/dist/{mcp-CBskrEsK.js → mcp-C5Zheplw.js} +2 -2
  34. package/dist/{mcp-CBskrEsK.js.map → mcp-C5Zheplw.js.map} +1 -1
  35. package/dist/{mcp-prompt-streaming-B1upRE9N.js → mcp-prompt-streaming-DEerU5M3.js} +3 -3
  36. package/dist/{mcp-prompt-streaming-B1upRE9N.js.map → mcp-prompt-streaming-DEerU5M3.js.map} +1 -1
  37. package/dist/{non-interactive-BUmW9f9P.js → non-interactive-C9vQaJF2.js} +2 -2
  38. package/dist/{non-interactive-BUmW9f9P.js.map → non-interactive-C9vQaJF2.js.map} +1 -1
  39. package/dist/{orchestrator-tools-bM9GOq3c.js → orchestrator-tools-Bw97ahf6.js} +3 -3
  40. package/dist/{orchestrator-tools-bM9GOq3c.js.map → orchestrator-tools-Bw97ahf6.js.map} +1 -1
  41. package/dist/{package-manager-ByVEmFPR.js → package-manager-Cm5vbUDR.js} +2 -2
  42. package/dist/{package-manager-ByVEmFPR.js.map → package-manager-Cm5vbUDR.js.map} +1 -1
  43. package/dist/{pi-C7wbP2z-.js → pi-BocAqBQZ.js} +11 -11
  44. package/dist/{pi-C7wbP2z-.js.map → pi-BocAqBQZ.js.map} +1 -1
  45. package/dist/{playground-Df_ZzalF.js → playground-DmGHAiON.js} +25 -788
  46. package/dist/playground-DmGHAiON.js.map +1 -0
  47. package/dist/{posthog-DLklk2yT.js → posthog-BitL59Wc.js} +2 -2
  48. package/dist/{posthog-DLklk2yT.js.map → posthog-BitL59Wc.js.map} +1 -1
  49. package/dist/{posthog-integration-CxIj49T4.js → posthog-integration-DbZ2oza2.js} +110 -33
  50. package/dist/posthog-integration-DbZ2oza2.js.map +1 -0
  51. package/dist/{provisioning-BpqrbURn.js → provisioning-CzRepu4H.js} +3 -3
  52. package/dist/{provisioning-BpqrbURn.js.map → provisioning-CzRepu4H.js.map} +1 -1
  53. package/dist/{queue-tools-QVKYMX_C.js → queue-tools-OynU-m2F.js} +37 -7
  54. package/dist/queue-tools-OynU-m2F.js.map +1 -0
  55. package/dist/{registry-CboWk-Ny.js → registry-eXzDlt51.js} +5 -5
  56. package/dist/{registry-CboWk-Ny.js.map → registry-eXzDlt51.js.map} +1 -1
  57. package/dist/{security-aWRzd8V1.js → security-DqByFEpl.js} +5 -5
  58. package/dist/{security-aWRzd8V1.js.map → security-DqByFEpl.js.map} +1 -1
  59. package/dist/{setup-utils-VtxhrSx8.js → setup-utils-C2rGcUFv.js} +9 -9
  60. package/dist/setup-utils-C2rGcUFv.js.map +1 -0
  61. package/dist/{start-tui-CzMiitZJ.js → start-tui-BYTQwgvK.js} +137 -42
  62. package/dist/start-tui-BYTQwgvK.js.map +1 -0
  63. package/dist/{steps-9PddZ-BV.js → steps-CMIyEySI.js} +7 -7
  64. package/dist/{steps-9PddZ-BV.js.map → steps-CMIyEySI.js.map} +1 -1
  65. package/dist/{store-V71P5KpB.js → store-pBYMaEuj.js} +25 -5
  66. package/dist/store-pBYMaEuj.js.map +1 -0
  67. package/dist/{subagent-CcBKBSR-.js → subagent-ZDD6XCmm.js} +2 -2
  68. package/dist/{subagent-CcBKBSR-.js.map → subagent-ZDD6XCmm.js.map} +1 -1
  69. package/dist/{task-BRktXvCV.js → task-1KHCdViK.js} +36 -23
  70. package/dist/task-1KHCdViK.js.map +1 -0
  71. package/dist/{task-stream-CFAXlJLv.js → task-stream-DAU6Z6u6.js} +5 -5
  72. package/dist/{task-stream-CFAXlJLv.js.map → task-stream-DAU6Z6u6.js.map} +1 -1
  73. package/dist/{tasks-CyLSHCmC.js → tasks-BBo0_AZG.js} +2 -2
  74. package/dist/{tasks-CyLSHCmC.js.map → tasks-BBo0_AZG.js.map} +1 -1
  75. package/dist/{telemetry-mi4dua6_.js → telemetry-DSzPCPtQ.js} +3 -3
  76. package/dist/{telemetry-mi4dua6_.js.map → telemetry-DSzPCPtQ.js.map} +1 -1
  77. package/dist/{terminal-B-37htZb.js → terminal-DW8x0UNT.js} +978 -71
  78. package/dist/terminal-DW8x0UNT.js.map +1 -0
  79. package/dist/{tools-BWaoKBAO.js → tools-DCH7Ji4V.js} +6 -6
  80. package/dist/{tools-BWaoKBAO.js.map → tools-DCH7Ji4V.js.map} +1 -1
  81. package/dist/{wizard-abort-D0HFMYCV.js → wizard-abort-B_Sa9K3g.js} +1 -1
  82. package/dist/{wizard-abort-Bgx_JOfw.js → wizard-abort-Cz916Qq5.js} +4 -4
  83. package/dist/{wizard-abort-Bgx_JOfw.js.map → wizard-abort-Cz916Qq5.js.map} +1 -1
  84. package/dist/{wizard-session-CwgR5y3-.js → wizard-session-BUhlJ1b8.js} +2 -1
  85. package/dist/wizard-session-BUhlJ1b8.js.map +1 -0
  86. package/dist/{wizard-session-DsK16SGs.js → wizard-session-DZUDtS5d.js} +1 -1
  87. package/dist/yara-hooks-BVEBOdMu.js +2 -0
  88. package/dist/{yara-hooks-Cj8x2YV4.js → yara-hooks-DgScuNrT.js} +4 -4
  89. package/dist/{yara-hooks-Cj8x2YV4.js.map → yara-hooks-DgScuNrT.js.map} +1 -1
  90. package/package.json +2 -1
  91. package/dist/add-mcp-server-to-clients-CM30npkB.js.map +0 -1
  92. package/dist/agent-prompt-loader-DL32ioJX.js.map +0 -1
  93. package/dist/agent-runner-DUyxA1TQ.js.map +0 -1
  94. package/dist/analytics-HdW5e8_o.js +0 -2
  95. package/dist/debug-D3Ucw2y1.js.map +0 -1
  96. package/dist/defaults-BWLIffh5.js.map +0 -1
  97. package/dist/playground-Df_ZzalF.js.map +0 -1
  98. package/dist/posthog-integration-CxIj49T4.js.map +0 -1
  99. package/dist/queue-tools-QVKYMX_C.js.map +0 -1
  100. package/dist/setup-utils-VtxhrSx8.js.map +0 -1
  101. package/dist/start-tui-CzMiitZJ.js.map +0 -1
  102. package/dist/store-V71P5KpB.js.map +0 -1
  103. package/dist/task-BRktXvCV.js.map +0 -1
  104. package/dist/terminal-B-37htZb.js.map +0 -1
  105. package/dist/wizard-session-CwgR5y3-.js.map +0 -1
  106. package/dist/yara-hooks-CIQRywtg.js +0 -2
package/dist/bin.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { Ct as regionOption, L as Integration, St as isHeadless, U as POSTHOG_DOCS_URL, a as getLogFilePath, f as getUI, ft as WIZARD_USER_AGENT, ht as VERSION, m as LoggingUI, p as setUI, pt as getSkillsBaseUrl, r as debug, s as logToFile, xt as headlessOption } from "./debug-D3Ucw2y1.js";
3
- import { n as analytics } from "./analytics-B9V7x1Ma.js";
4
- import { r as setEntryCommand } from "./telemetry-mi4dua6_.js";
5
- import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-xkhfF3JS.js";
6
- import { n as isUsingTypeScript } from "./setup-utils-VtxhrSx8.js";
7
- import { o as handleApiError } from "./api-OHLXhKTy.js";
8
- import "./wizard-session-CwgR5y3-.js";
9
- import { r as runCleanups } from "./wizard-abort-Bgx_JOfw.js";
10
- import { n as isNonInteractiveEnvironment } from "./environment-BN8ROaHv.js";
11
- import { n as runAgent, o as authenticate } from "./agent-runner-DUyxA1TQ.js";
12
- import { u as writeJsonAtomic } from "./queue-tools-QVKYMX_C.js";
13
- import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-CybBFwnk.js";
14
- import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-Cj8x2YV4.js";
15
- import { i as SPINNER_MESSAGE } from "./registry-CboWk-Ny.js";
16
- import { C as resolveProjectDir, _ as WAREHOUSE_ABORT_CASES, a as LINE_CHART_BLOCK, b as detectWarehouseSources, d as isClearBlock, g as HEALTH_CHECK_STEP, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as detectWarehousePrerequisites, w as gatherFrameworkContext, x as detectIntegrationProjects, y as getDetectedWarehouseSources } from "./posthog-integration-CxIj49T4.js";
2
+ import { Ct as regionOption, L as Integration, St as isHeadless, U as POSTHOG_DOCS_URL, a as getLogFilePath, f as getUI, ft as WIZARD_USER_AGENT, ht as VERSION, m as LoggingUI, p as setUI, pt as getSkillsBaseUrl, r as debug, s as logToFile, xt as headlessOption } from "./debug-8-IIHr5f.js";
3
+ import { n as analytics } from "./analytics-CkIyBHfd.js";
4
+ import { r as setEntryCommand } from "./telemetry-DSzPCPtQ.js";
5
+ import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-B-qlKB0h.js";
6
+ import { n as isUsingTypeScript } from "./setup-utils-C2rGcUFv.js";
7
+ import { o as handleApiError } from "./api-XHQTOfIi.js";
8
+ import "./wizard-session-BUhlJ1b8.js";
9
+ import { r as runCleanups } from "./wizard-abort-Cz916Qq5.js";
10
+ import { n as isNonInteractiveEnvironment } from "./environment-BffaZJRI.js";
11
+ import { n as runAgent, s as authenticate } from "./agent-runner-DLAvB67P.js";
12
+ import { d as writeJsonAtomic } from "./queue-tools-OynU-m2F.js";
13
+ import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-Bm4ZNGGV.js";
14
+ import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-DgScuNrT.js";
15
+ import { i as SPINNER_MESSAGE } from "./registry-eXzDlt51.js";
16
+ import { C as resolveProjectDir, _ as WAREHOUSE_ABORT_CASES, a as LINE_CHART_BLOCK, b as detectWarehouseSources, d as isClearBlock, g as HEALTH_CHECK_STEP, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as detectWarehousePrerequisites, w as gatherFrameworkContext, x as detectIntegrationProjects, y as getDetectedWarehouseSources } from "./posthog-integration-DbZ2oza2.js";
17
17
  import { n as readApiKeyFromEnv } from "./env-api-key-cjD4yfGT.js";
18
18
  import { satisfies } from "semver";
19
19
  import yargs from "yargs";
@@ -28,9 +28,10 @@ import * as fs$1 from "node:fs";
28
28
  import axios from "axios";
29
29
  import { z } from "zod";
30
30
  import { Box, Text, render, useInput, useStdout } from "ink";
31
- import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
31
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
32
32
  import { jsx, jsxs } from "react/jsx-runtime";
33
33
  import { access, rm } from "node:fs/promises";
34
+ import Fuse from "fuse.js";
34
35
  import * as readline from "node:readline/promises";
35
36
  //#region src/commands/command.ts
36
37
  /** Extract the bare command word(s) from a yargs name spec, dropping positionals and aliases' arg syntax. */
@@ -201,7 +202,7 @@ function runProvision(argv) {
201
202
  }
202
203
  async function provision({ email, region, name, baseUrl, jsonMode }) {
203
204
  try {
204
- const { provisionNewAccount } = await import("./provisioning-BpqrbURn.js").then((n) => n.r);
205
+ const { provisionNewAccount } = await import("./provisioning-CzRepu4H.js").then((n) => n.r);
205
206
  if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
206
207
  emitResult(await provisionNewAccount(email, name, region, { baseUrl }), jsonMode);
207
208
  process.exit(0);
@@ -268,22 +269,22 @@ const basicIntegrationCommand = {
268
269
  setEntryCommand("integrate");
269
270
  (async () => {
270
271
  if (isHeadless(argv)) {
271
- const { runHeadlessInstall } = await import("./ci-install-B31ciXo4.js");
272
+ const { runHeadlessInstall } = await import("./ci-install-C9BAoO2n.js");
272
273
  return runHeadlessInstall(argv);
273
274
  }
274
275
  if (argv.ci) {
275
- const { runCIInstall } = await import("./ci-install-B31ciXo4.js");
276
+ const { runCIInstall } = await import("./ci-install-C9BAoO2n.js");
276
277
  return runCIInstall(argv);
277
278
  }
278
279
  if (isNonInteractiveEnvironment()) {
279
- const { failNonInteractive } = await import("./non-interactive-BUmW9f9P.js");
280
+ const { failNonInteractive } = await import("./non-interactive-C9vQaJF2.js");
280
281
  return failNonInteractive();
281
282
  }
282
283
  if (argv.playground) {
283
- const { runPlayground } = await import("./playground-Df_ZzalF.js");
284
+ const { runPlayground } = await import("./playground-DmGHAiON.js");
284
285
  return runPlayground();
285
286
  }
286
- const { runInteractive } = await import("./interactive-DeFFwXME.js");
287
+ const { runInteractive } = await import("./interactive-C9vepji1.js");
287
288
  runInteractive(argv);
288
289
  })();
289
290
  }
@@ -3197,6 +3198,16 @@ STEP 6 — Configure the scout troop. (skill: "Scouts")
3197
3198
  whole troop, including step 6b's custom scouts, stays at or under
3198
3199
  ten enabled scouts.
3199
3200
 
3201
+ Then tell the user what the troop is, before STEP 6b asks them to add
3202
+ to it. Scouts are PostHog's built-in ones, and the user has not seen
3203
+ the list: without it, STEP 6b asks them to approve additions to a
3204
+ baseline they cannot picture. Send one short message naming each
3205
+ scout you enabled with a plain one-line description of what it
3206
+ watches, and say how many you left disabled and that they can turn
3207
+ those on later from the inbox. No wizard_ask here, it is not a
3208
+ question. Keep each line to one sentence and do not restate the
3209
+ skill's internals.
3210
+
3200
3211
  STEP 6b — Design custom scouts for this product. (skill: "Custom scouts")
3201
3212
  You are the only actor that has read this repo — turn that into
3202
3213
  coverage per the skill: a real gap analysis of the project's
@@ -3227,6 +3238,17 @@ STEP 7 — Write the report and hand off. (skill: "Report")
3227
3238
  deferred. Tell the user findings will start appearing in their inbox
3228
3239
  at ${inboxUrl} within about 30 minutes.`;
3229
3240
  }
3241
+ /** One line, for a screen that only has room to say what it costs. */
3242
+ const PRICING_SHORT = `Agents charge a flat $15 per pull request they ship.`;
3243
+ /** The same fact at length, plus the bit people are surprised by later. */
3244
+ const PRICING_LONG = `A report that ships a pull request costs a flat $15. Everything else follows your usual PostHog usage-based pricing.`;
3245
+ /**
3246
+ * Nothing caps the spend unless you set it, which is worth saying out loud. Checked against
3247
+ * billing: `custom_limits_map` starts empty and a product with no custom limit resolves to
3248
+ * `usage_limit: None`, i.e. uncapped. A free plan is capped by its own allocation instead,
3249
+ * hence the qualifier.
3250
+ */
3251
+ const NO_DEFAULT_LIMIT = "A paid plan has no monthly cap until you set one. Add a PR limit in your inbox sidebar, and agents pause when they hit it.";
3230
3252
  //#endregion
3231
3253
  //#region src/lib/programs/self-driving/content/tips.ts
3232
3254
  const SELF_DRIVING_TIPS = [
@@ -3258,7 +3280,7 @@ const SELF_DRIVING_TIPS = [
3258
3280
  {
3259
3281
  id: "pricing",
3260
3282
  title: "What it costs",
3261
- description: "Watching is free. You pay a flat $15 only when a report ships a PR."
3283
+ description: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`
3262
3284
  },
3263
3285
  {
3264
3286
  id: "work-anywhere",
@@ -3497,8 +3519,12 @@ const getContentBlocks = (store) => {
3497
3519
  pause: 6e3
3498
3520
  },
3499
3521
  {
3500
- content: "That PR is the only thing you pay for: a flat $15 per report that ships a PR. Watching is free.",
3501
- pause: 6e3
3522
+ content: PRICING_LONG,
3523
+ pause: 5e3
3524
+ },
3525
+ {
3526
+ content: NO_DEFAULT_LIMIT,
3527
+ pause: 5e3
3502
3528
  },
3503
3529
  CLEAR,
3504
3530
  {
@@ -3633,11 +3659,12 @@ const buildRun = (session) => Promise.resolve({
3633
3659
  items: [
3634
3660
  "Investigate reports with the agent",
3635
3661
  "Tag teammates to loop them in",
3636
- "Kick off a PR when you like the proposed fix ($15 flat)",
3662
+ `Kick off a PR when you like the proposed fix ($15 flat)`,
3663
+ "Cap the spend with a monthly PR limit in the sidebar",
3637
3664
  "Or work from Slack (tag @PostHog) and MCP"
3638
3665
  ]
3639
3666
  },
3640
- body: "Pricing: scouts, signals, and reports are free. You pay a flat $15 only when a report ships a PR.",
3667
+ body: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`,
3641
3668
  reportFile: REPORT_FILE
3642
3669
  };
3643
3670
  }
@@ -3929,6 +3956,17 @@ function getProgramConfig(id) {
3929
3956
  return PROGRAM_REGISTRY.find((c) => c.id === id);
3930
3957
  }
3931
3958
  //#endregion
3959
+ //#region src/commands/mcp/tui-availability.ts
3960
+ /**
3961
+ * Ink throws "Raw mode is not supported" when stdin has no TTY (piped input,
3962
+ * CI, some IDE terminals). That is the only TUI failure the mcp commands
3963
+ * degrade to LoggingUI for — any other error from the TUI path is a real bug
3964
+ * and must surface rather than be silently swallowed.
3965
+ */
3966
+ function isTUIUnavailable(error) {
3967
+ return error instanceof Error && /raw mode is not supported/i.test(error.message);
3968
+ }
3969
+ //#endregion
3932
3970
  //#region src/commands/mcp/add.ts
3933
3971
  const mcpAddCommand = {
3934
3972
  name: "add",
@@ -3958,8 +3996,8 @@ function runMcpAdd(argv) {
3958
3996
  const debug = argv.debug;
3959
3997
  const localMcp = argv.local;
3960
3998
  try {
3961
- const { startTUI } = await import("./start-tui-CzMiitZJ.js");
3962
- const { buildSession } = await import("./wizard-session-DsK16SGs.js");
3999
+ const { startTUI } = await import("./start-tui-BYTQwgvK.js");
4000
+ const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
3963
4001
  const tui = startTUI(VERSION, Program.McpAdd);
3964
4002
  tui.store.session = buildSession({
3965
4003
  debug,
@@ -3971,7 +4009,7 @@ function runMcpAdd(argv) {
3971
4009
  } catch (error) {
3972
4010
  if (!isTUIUnavailable(error)) throw error;
3973
4011
  setUI(new LoggingUI());
3974
- const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-CM30npkB.js").then((n) => n.r);
4012
+ const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-KNIHRN_m.js").then((n) => n.r);
3975
4013
  await addMCPServerToClientsStep({
3976
4014
  local: localMcp,
3977
4015
  features,
@@ -3980,15 +4018,6 @@ function runMcpAdd(argv) {
3980
4018
  }
3981
4019
  })();
3982
4020
  }
3983
- /**
3984
- * Ink throws "Raw mode is not supported" when stdin has no TTY (piped input,
3985
- * CI, some IDE terminals). That is the only TUI failure we degrade to
3986
- * LoggingUI for — any other error from the TUI path is a real bug and must
3987
- * surface rather than be silently swallowed.
3988
- */
3989
- function isTUIUnavailable(error) {
3990
- return error instanceof Error && /raw mode is not supported/i.test(error.message);
3991
- }
3992
4021
  function parseFeatures(raw) {
3993
4022
  if (typeof raw !== "string") return void 0;
3994
4023
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -4010,17 +4039,18 @@ function runMcpRemove(argv) {
4010
4039
  const debug = argv.debug;
4011
4040
  const localMcp = argv.local;
4012
4041
  try {
4013
- const { startTUI } = await import("./start-tui-CzMiitZJ.js");
4014
- const { buildSession } = await import("./wizard-session-DsK16SGs.js");
4042
+ const { startTUI } = await import("./start-tui-BYTQwgvK.js");
4043
+ const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
4015
4044
  const tui = startTUI(VERSION, Program.McpRemove);
4016
4045
  tui.store.session = buildSession({
4017
4046
  debug,
4018
4047
  localMcp,
4019
4048
  baseUrl: argv.baseUrl
4020
4049
  });
4021
- } catch {
4050
+ } catch (error) {
4051
+ if (!isTUIUnavailable(error)) throw error;
4022
4052
  setUI(new LoggingUI());
4023
- const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-CM30npkB.js").then((n) => n.r);
4053
+ const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-KNIHRN_m.js").then((n) => n.r);
4024
4054
  await removeMCPServerFromClientsStep({ local: localMcp });
4025
4055
  }
4026
4056
  })();
@@ -4042,8 +4072,8 @@ function runMcpTutorial(argv) {
4042
4072
  const debug = argv.debug;
4043
4073
  const localMcp = argv.local;
4044
4074
  try {
4045
- const { startTUI } = await import("./start-tui-CzMiitZJ.js");
4046
- const { buildSession } = await import("./wizard-session-DsK16SGs.js");
4075
+ const { startTUI } = await import("./start-tui-BYTQwgvK.js");
4076
+ const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
4047
4077
  const tui = startTUI(VERSION, Program.McpTutorial);
4048
4078
  tui.store.session = buildSession({
4049
4079
  debug,
@@ -4124,10 +4154,10 @@ function runWizard(config, options) {
4124
4154
  (async () => {
4125
4155
  try {
4126
4156
  const installDir = options.installDir || process.cwd();
4127
- const { startTUI } = await import("./start-tui-CzMiitZJ.js");
4128
- const { buildSession, RunPhase } = await import("./wizard-session-DsK16SGs.js");
4129
- const { TaskStreamPush } = await import("./task-stream-CFAXlJLv.js");
4130
- const { PostHogDestination } = await import("./posthog-DLklk2yT.js");
4157
+ const { startTUI } = await import("./start-tui-BYTQwgvK.js");
4158
+ const { buildSession, RunPhase } = await import("./wizard-session-DZUDtS5d.js");
4159
+ const { TaskStreamPush } = await import("./task-stream-DAU6Z6u6.js");
4160
+ const { PostHogDestination } = await import("./posthog-BitL59Wc.js");
4131
4161
  tui = startTUI(WIZARD_VERSION, config.id);
4132
4162
  const activeTui = tui;
4133
4163
  const session = buildSession({
@@ -4193,7 +4223,7 @@ function runWizard(config, options) {
4193
4223
  if (shown(step)) await advanceStep(step, activeTui.store, config);
4194
4224
  }
4195
4225
  else if (skipAgent) {
4196
- const { getOrAskForProjectData } = await import("./setup-utils-VtxhrSx8.js").then((n) => n.r);
4226
+ const { getOrAskForProjectData } = await import("./setup-utils-C2rGcUFv.js").then((n) => n.r);
4197
4227
  const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
4198
4228
  signup: session.signup,
4199
4229
  ci: session.ci,
@@ -4294,11 +4324,11 @@ function runNonInteractive(config, options, mode) {
4294
4324
  analytics.setTag("build", mode);
4295
4325
  (async () => {
4296
4326
  const path = await import("path");
4297
- const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-DsK16SGs.js");
4298
- const { readEnvironment } = await import("./environment-BN8ROaHv.js").then((n) => n.t);
4327
+ const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-DZUDtS5d.js");
4328
+ const { readEnvironment } = await import("./environment-BffaZJRI.js").then((n) => n.t);
4299
4329
  const { readApiKeyFromEnv } = await import("./env-api-key-cjD4yfGT.js").then((n) => n.t);
4300
- const { configureLogFileFromEnvironment, logToFile } = await import("./debug-Cx9Delbc.js");
4301
- const { wizardAbort, WizardError } = await import("./wizard-abort-D0HFMYCV.js");
4330
+ const { configureLogFileFromEnvironment, logToFile } = await import("./debug-npp6DzBK.js");
4331
+ const { wizardAbort, WizardError } = await import("./wizard-abort-B_Sa9K3g.js");
4302
4332
  configureLogFileFromEnvironment();
4303
4333
  const env = readEnvironment();
4304
4334
  const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
@@ -4331,9 +4361,9 @@ function runNonInteractive(config, options, mode) {
4331
4361
  let store = null;
4332
4362
  let taskStream = null;
4333
4363
  if (mode === "headless") {
4334
- const { WizardStore } = await import("./store-V71P5KpB.js").then((n) => n.n);
4335
- const { HeadlessUI } = await import("./headless-ui-DoQlIoeh.js");
4336
- const { TaskStreamPush, PostHogDestination } = await import("./task-stream-CFAXlJLv.js");
4364
+ const { WizardStore } = await import("./store-pBYMaEuj.js").then((n) => n.n);
4365
+ const { HeadlessUI } = await import("./headless-ui-Dy_l8hCT.js");
4366
+ const { TaskStreamPush, PostHogDestination } = await import("./task-stream-DAU6Z6u6.js");
4337
4367
  store = new WizardStore(config.id);
4338
4368
  store.session = session;
4339
4369
  setUI(new HeadlessUI(store));
@@ -4389,7 +4419,7 @@ function runNonInteractive(config, options, mode) {
4389
4419
  });
4390
4420
  }
4391
4421
  }
4392
- const { runAgent } = await import("./agent-runner-DUyxA1TQ.js").then((n) => n.t);
4422
+ const { runAgent } = await import("./agent-runner-DLAvB67P.js").then((n) => n.t);
4393
4423
  await runAgent(config, session);
4394
4424
  await settleStream(RunPhase.Completed);
4395
4425
  } catch (error) {
@@ -4667,6 +4697,81 @@ function pickerChildrenToShow(children) {
4667
4697
  return defaults.length > 0 ? [...defaults] : [...children];
4668
4698
  }
4669
4699
  //#endregion
4700
+ //#region src/ui/tui/primitives/picker-filter.ts
4701
+ /**
4702
+ * Filter-as-you-type matching for PickerMenu. Split out from the component so the
4703
+ * ranking is testable on its own — the component only decides when to call it.
4704
+ */
4705
+ /**
4706
+ * Fuzzy fallback config. `ignoreLocation` because a term matching at the end of a
4707
+ * description is as good as one at the start, and Fuse's default distance window
4708
+ * would score the late match away. 0.5 is the loosest threshold that still bottoms
4709
+ * out: below it `gthb` misses `GitHub` entirely, which is the whole point of having
4710
+ * a fuzzy pass; above it the tail grows without surfacing anything new at the top.
4711
+ */
4712
+ const FUSE_OPTIONS = {
4713
+ keys: [
4714
+ {
4715
+ name: "label",
4716
+ weight: 3
4717
+ },
4718
+ {
4719
+ name: "hint",
4720
+ weight: 1
4721
+ },
4722
+ {
4723
+ name: "description",
4724
+ weight: 1
4725
+ }
4726
+ ],
4727
+ ignoreLocation: true,
4728
+ threshold: .5,
4729
+ includeScore: true
4730
+ };
4731
+ function buildPickerIndex(options) {
4732
+ return new Fuse(options, FUSE_OPTIONS);
4733
+ }
4734
+ function haystack(option) {
4735
+ return `${option.label} ${option.hint ?? ""} ${option.description ?? ""}`.toLowerCase();
4736
+ }
4737
+ /**
4738
+ * Rank options against a query, best first, or `null` for an empty query.
4739
+ *
4740
+ * Two passes, literal before fuzzy. Anything that literally contains every
4741
+ * whitespace-separated term wins outright, in the list's own order — that covers how
4742
+ * people actually type (`git iss` → `GitHub issues`) and keeps the common case as
4743
+ * tight as a plain substring filter, which fuzzy matching on its own is not: Fuse at
4744
+ * a threshold loose enough to forgive `gthb` also lets a third of the list through
4745
+ * on `replay`.
4746
+ *
4747
+ * Only when nothing matches literally does Fuse get a turn, so a typo degrades to a
4748
+ * ranked guess instead of an empty list. Terms are AND-ed there too, each matched
4749
+ * fuzzily on its own — Fuse's bitap pass over the whole query would otherwise need
4750
+ * `git iss` to be a near-edit of the text it's matching. Scores are distances (0 is
4751
+ * exact) and sum across terms, hence the ascending sort; ties keep the order the
4752
+ * first term put them in, since the sort is stable.
4753
+ */
4754
+ function rankOptions(fuse, options, query) {
4755
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
4756
+ if (!terms.length) return null;
4757
+ const literal = options.filter((option) => terms.every((term) => haystack(option).includes(term)));
4758
+ if (literal.length) return literal;
4759
+ let surviving = null;
4760
+ for (const term of terms) {
4761
+ const hits = new Map(fuse.search(term).map(({ item, score }) => [item, score ?? 0]));
4762
+ if (surviving === null) {
4763
+ surviving = hits;
4764
+ continue;
4765
+ }
4766
+ for (const [option, total] of surviving) {
4767
+ const score = hits.get(option);
4768
+ if (score === void 0) surviving.delete(option);
4769
+ else surviving.set(option, total + score);
4770
+ }
4771
+ }
4772
+ return [...surviving ?? []].sort(([, a], [, b]) => a - b).map(([option]) => option);
4773
+ }
4774
+ //#endregion
4670
4775
  //#region src/ui/tui/primitives/PromptLabel.tsx
4671
4776
  /**
4672
4777
  * PromptLabel — Compact inline label for input prompts.
@@ -4748,6 +4853,8 @@ const DEFAULT_PRIORITY = {
4748
4853
  ["leftArrow"]: 1,
4749
4854
  ["rightArrow"]: 1,
4750
4855
  ["space"]: 10,
4856
+ ["printable"]: 12,
4857
+ ["backspace"]: 13,
4751
4858
  ["escape"]: 20,
4752
4859
  ["return"]: 21
4753
4860
  };
@@ -4765,6 +4872,8 @@ function matchesKey(m, input, key) {
4765
4872
  case "return": return !!key.return;
4766
4873
  case "escape": return !!key.escape;
4767
4874
  case "space": return input === " ";
4875
+ case "backspace": return !!key.backspace || !!key.delete;
4876
+ case "printable": return input.length === 1 && input >= " " && input !== "" && Object.entries(key).every(([flag, on]) => !on || flag === "shift");
4768
4877
  default: return input === m;
4769
4878
  }
4770
4879
  }
@@ -4957,29 +5066,70 @@ function usePickerViewport(count, rowCost, chromeBelow, enabled, focused) {
4957
5066
  pageStep: (f, dir) => (Math.floor(f / perPage) + dir + pageCount) % pageCount * perPage
4958
5067
  };
4959
5068
  }
4960
- const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
4961
- if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
4962
- message,
5069
+ /** Lists at least this long get filtering. Shorter ones fit on a page or two,
5070
+ * where a filter row costs more rows than it saves. */
5071
+ const FILTER_MIN_OPTIONS = 10;
5072
+ const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, filterable, onSelect }) => {
5073
+ const canFilter = filterable ?? options.length >= FILTER_MIN_OPTIONS;
5074
+ const [filter, setFilter] = useState("");
5075
+ const fuse = useMemo(() => canFilter ? buildPickerIndex(options) : null, [options, canFilter]);
5076
+ const visible = useMemo(() => fuse ? rankOptions(fuse, options, filter) ?? options : options, [
5077
+ fuse,
4963
5078
  options,
4964
- centered,
4965
- columns,
4966
- optionMarginBottom,
4967
- onSelect
4968
- });
4969
- return /* @__PURE__ */ jsx(SinglePickerMenu, {
5079
+ filter
5080
+ ]);
5081
+ const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
5082
+ const shared = {
4970
5083
  message,
4971
- options,
5084
+ options: visible,
4972
5085
  centered,
4973
- columns,
4974
5086
  optionMarginBottom,
4975
- onSelect
4976
- });
5087
+ onSelect,
5088
+ filter: canFilter ? filter : null,
5089
+ setFilter,
5090
+ totalCount: options.length
5091
+ };
5092
+ const key = String(filter);
5093
+ if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
5094
+ ...shared,
5095
+ columns,
5096
+ allOptions: options,
5097
+ selected,
5098
+ setSelected
5099
+ }, key);
5100
+ return /* @__PURE__ */ jsx(SinglePickerMenu, {
5101
+ ...shared,
5102
+ columns
5103
+ }, key);
4977
5104
  };
5105
+ /**
5106
+ * Backspace and free typing. Append these last: `Printable` matches almost any
5107
+ * key, and bindings resolve in order, so anything specific has to be registered
5108
+ * first to keep working. Both share a label/action so the hints bar shows one.
5109
+ */
5110
+ function filterBindings(filter, setFilter) {
5111
+ return [{
5112
+ match: "backspace",
5113
+ label: "type",
5114
+ action: "filter",
5115
+ handler: () => setFilter(filter.slice(0, -1))
5116
+ }, {
5117
+ match: "printable",
5118
+ label: "type",
5119
+ action: "filter",
5120
+ handler: (input) => setFilter(filter + input)
5121
+ }];
5122
+ }
5123
+ /** The filter row above a filterable list: what's typed, and how much it left. */
5124
+ const FilterRow = ({ filter, shown, total }) => /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, {
5125
+ dimColor: true,
5126
+ children: filter ? `Filter: ${filter} (${shown} of ${total})` : `Type to filter ${total} options`
5127
+ }) });
4978
5128
  /** Custom single-select with triangle indicator and accent highlight. */
4979
- const SinglePickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
5129
+ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, filter, setFilter, totalCount, onSelect }) => {
4980
5130
  const [focused, setFocused] = useState(() => firstEnabled(options));
4981
5131
  const rows = Math.ceil(options.length / columns);
4982
- const viewport = usePickerViewport(options.length, 1 + optionMarginBottom, 0, columns === 1, focused);
5132
+ const viewport = usePickerViewport(options.length, 1 + optionMarginBottom, filter !== null ? 1 : 0, columns === 1, focused);
4983
5133
  useEffect(() => {
4984
5134
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
4985
5135
  }, [options, focused]);
@@ -4989,22 +5139,10 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
4989
5139
  label: "↑↓",
4990
5140
  action: "navigate",
4991
5141
  handler: (_input, key) => {
4992
- const dir = key.upArrow ? -1 : 1;
4993
- if (columns === 1) {
4994
- const { start, end } = viewport;
4995
- const span = end - start;
4996
- let r = focused;
4997
- for (let i = 0; i < span; i++) {
4998
- r = start + (r - start + dir + span) % span;
4999
- if (!options[r]?.disabled) break;
5000
- }
5001
- setFocused(r);
5002
- return;
5003
- }
5004
- setFocused(stepEnabled(options, rows, focused, dir));
5142
+ setFocused(stepEnabled(options, rows, focused, key.upArrow ? -1 : 1));
5005
5143
  }
5006
5144
  },
5007
- ...viewport.needsScroll ? [{
5145
+ ...viewport.needsScroll && filter === null ? [{
5008
5146
  match: ["n", "p"],
5009
5147
  label: "n/p",
5010
5148
  action: "page",
@@ -5043,6 +5181,7 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5043
5181
  setFocused(next);
5044
5182
  }
5045
5183
  });
5184
+ if (filter !== null) bindings.push(...filterBindings(filter, setFilter));
5046
5185
  useKeyBindings("single-picker", bindings);
5047
5186
  const columnArrays = [];
5048
5187
  for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
@@ -5076,27 +5215,38 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5076
5215
  return /* @__PURE__ */ jsxs(Box, {
5077
5216
  flexDirection: "column",
5078
5217
  alignItems: align,
5079
- children: [/* @__PURE__ */ jsx(PromptLabel, { message }), viewport.needsScroll ? /* @__PURE__ */ jsxs(Box, {
5080
- flexDirection: "column",
5081
- children: [
5082
- /* @__PURE__ */ jsx(Text, {
5083
- dimColor: true,
5084
- children: viewport.hiddenAbove > 0 ? `↑ ${viewport.hiddenAbove} more [P] for previous page` : " "
5085
- }),
5086
- options.slice(viewport.start, viewport.end).map((opt, relIdx) => renderOption(opt, viewport.start + relIdx)),
5087
- /* @__PURE__ */ jsx(Text, {
5088
- dimColor: true,
5089
- children: viewport.hiddenBelow > 0 ? `↓ ${viewport.hiddenBelow} more [N] for next page` : " "
5090
- })
5091
- ]
5092
- }) : /* @__PURE__ */ jsx(Box, {
5093
- flexDirection: "row",
5094
- gap: 4,
5095
- children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
5218
+ children: [
5219
+ /* @__PURE__ */ jsx(PromptLabel, { message }),
5220
+ filter !== null && /* @__PURE__ */ jsx(FilterRow, {
5221
+ filter,
5222
+ shown: options.length,
5223
+ total: totalCount
5224
+ }),
5225
+ filter && options.length === 0 ? /* @__PURE__ */ jsx(Text, {
5226
+ dimColor: true,
5227
+ children: "No options match. Backspace to widen the filter."
5228
+ }) : viewport.needsScroll ? /* @__PURE__ */ jsxs(Box, {
5096
5229
  flexDirection: "column",
5097
- children: colOpts.map((opt, rowIdx) => renderOption(opt, colIdx * rows + rowIdx))
5098
- }, colIdx))
5099
- })]
5230
+ children: [
5231
+ /* @__PURE__ */ jsx(Text, {
5232
+ dimColor: true,
5233
+ children: viewport.hiddenAbove > 0 ? `↑ ${viewport.hiddenAbove} more${filter === null ? " [P] for previous page" : ""}` : " "
5234
+ }),
5235
+ options.slice(viewport.start, viewport.end).map((opt, relIdx) => renderOption(opt, viewport.start + relIdx)),
5236
+ /* @__PURE__ */ jsx(Text, {
5237
+ dimColor: true,
5238
+ children: viewport.hiddenBelow > 0 ? `↓ ${viewport.hiddenBelow} more${filter === null ? " [N] for next page" : ""}` : " "
5239
+ })
5240
+ ]
5241
+ }) : /* @__PURE__ */ jsx(Box, {
5242
+ flexDirection: "row",
5243
+ gap: 4,
5244
+ children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
5245
+ flexDirection: "column",
5246
+ children: colOpts.map((opt, rowIdx) => renderOption(opt, colIdx * rows + rowIdx))
5247
+ }, colIdx))
5248
+ })
5249
+ ]
5100
5250
  });
5101
5251
  };
5102
5252
  /**
@@ -5111,18 +5261,17 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5111
5261
  * - moving onto the Confirm button and pressing enter submits the
5112
5262
  * current selection.
5113
5263
  */
5114
- const MultiPickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
5264
+ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, filter, setFilter, totalCount, allOptions, selected, setSelected, onSelect }) => {
5115
5265
  const [focused, setFocused] = useState(() => firstEnabled(options));
5116
5266
  const [onButton, setOnButton] = useState(false);
5117
- const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
5118
5267
  const rows = Math.ceil(options.length / columns);
5119
5268
  const rowCost = options.reduce((max, opt) => Math.max(max, 1 + optionMarginBottom + (opt.description ? wordWrap(opt.description, DESCRIPTION_WIDTH).length : 0)), 1);
5120
- const viewport = usePickerViewport(options.length, rowCost, CONFIRM_CHROME, columns === 1, focused);
5269
+ const viewport = usePickerViewport(options.length, rowCost, CONFIRM_CHROME + (filter !== null ? 1 : 0), columns === 1, focused);
5121
5270
  useEffect(() => {
5122
5271
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
5123
5272
  }, [options, focused]);
5124
5273
  const confirm = () => {
5125
- onSelect([...selected].sort((a, b) => a - b).map((i) => options[i].value));
5274
+ onSelect(allOptions.filter((option) => selected.has(option.label)).map((option) => option.value));
5126
5275
  };
5127
5276
  const bindings = [
5128
5277
  {
@@ -5136,13 +5285,6 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5136
5285
  setFocused(dir === -1 ? lastEnabled(options, viewport.start, viewport.end) : firstEnabled(options, viewport.start, viewport.end));
5137
5286
  return;
5138
5287
  }
5139
- if (columns === 1) {
5140
- let r = focused + dir;
5141
- while (r >= viewport.start && r < viewport.end && options[r]?.disabled) r += dir;
5142
- if (r >= viewport.start && r < viewport.end) setFocused(r);
5143
- else setOnButton(true);
5144
- return;
5145
- }
5146
5288
  const col = Math.floor(focused / rows);
5147
5289
  const row = focused % rows;
5148
5290
  const colLen = Math.min(rows, options.length - col * rows);
@@ -5152,7 +5294,7 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5152
5294
  else setOnButton(true);
5153
5295
  }
5154
5296
  },
5155
- ...viewport.needsScroll ? [{
5297
+ ...viewport.needsScroll && filter === null ? [{
5156
5298
  match: ["n", "p"],
5157
5299
  label: "n/p",
5158
5300
  action: "page",
@@ -5163,7 +5305,7 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5163
5305
  }
5164
5306
  }] : [],
5165
5307
  {
5166
- match: ["space", "return"],
5308
+ match: filter !== null ? "return" : ["space", "return"],
5167
5309
  label: "enter",
5168
5310
  action: "select",
5169
5311
  handler: () => {
@@ -5171,16 +5313,18 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5171
5313
  confirm();
5172
5314
  return;
5173
5315
  }
5174
- if (options[focused]?.disabled) return;
5316
+ const option = options[focused];
5317
+ if (!option || option.disabled) return;
5318
+ const label = option.label;
5175
5319
  setSelected((prev) => {
5176
5320
  const next = new Set(prev);
5177
- if (next.has(focused)) {
5178
- next.delete(focused);
5321
+ if (next.has(label)) {
5322
+ next.delete(label);
5179
5323
  return next;
5180
5324
  }
5181
- if (options[focused]?.exclusive) return new Set([focused]);
5182
- for (const i of next) if (options[i]?.exclusive) next.delete(i);
5183
- next.add(focused);
5325
+ if (option.exclusive) return new Set([label]);
5326
+ for (const picked of next) if (allOptions.find((o) => o.label === picked)?.exclusive) next.delete(picked);
5327
+ next.add(label);
5184
5328
  return next;
5185
5329
  });
5186
5330
  }
@@ -5207,12 +5351,13 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5207
5351
  setFocused(next);
5208
5352
  }
5209
5353
  });
5354
+ if (filter !== null) bindings.push(...filterBindings(filter, setFilter));
5210
5355
  useKeyBindings("multi-picker", bindings);
5211
5356
  const columnArrays = [];
5212
5357
  for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
5213
5358
  const renderOption = (opt, flatIdx) => {
5214
5359
  const isFocused = !onButton && flatIdx === focused;
5215
- const isSelected = selected.has(flatIdx);
5360
+ const isSelected = selected.has(opt.label);
5216
5361
  const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
5217
5362
  const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
5218
5363
  return /* @__PURE__ */ jsxs(Box, {
@@ -5253,6 +5398,15 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5253
5398
  alignItems: centered ? "center" : void 0,
5254
5399
  children: [
5255
5400
  /* @__PURE__ */ jsx(PromptLabel, { message }),
5401
+ filter !== null && /* @__PURE__ */ jsx(FilterRow, {
5402
+ filter,
5403
+ shown: options.length,
5404
+ total: totalCount
5405
+ }),
5406
+ filter && options.length === 0 && /* @__PURE__ */ jsx(Text, {
5407
+ dimColor: true,
5408
+ children: "No options match. Backspace to widen the filter."
5409
+ }),
5256
5410
  viewport.needsScroll ? /* @__PURE__ */ jsxs(Box, {
5257
5411
  flexDirection: "column",
5258
5412
  marginLeft: centered ? 0 : 2,
@@ -5260,12 +5414,12 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5260
5414
  children: [
5261
5415
  /* @__PURE__ */ jsx(Text, {
5262
5416
  dimColor: true,
5263
- children: viewport.hiddenAbove > 0 ? `↑ ${viewport.hiddenAbove} more [P] for previous page` : " "
5417
+ children: viewport.hiddenAbove > 0 ? `↑ ${viewport.hiddenAbove} more${filter === null ? " [P] for previous page" : ""}` : " "
5264
5418
  }),
5265
5419
  options.slice(viewport.start, viewport.end).map((opt, relIdx) => renderOption(opt, viewport.start + relIdx)),
5266
5420
  /* @__PURE__ */ jsx(Text, {
5267
5421
  dimColor: true,
5268
- children: viewport.hiddenBelow > 0 ? `↓ ${viewport.hiddenBelow} more [N] for next page` : " "
5422
+ children: viewport.hiddenBelow > 0 ? `↓ ${viewport.hiddenBelow} more${filter === null ? " [N] for next page" : ""}` : " "
5269
5423
  })
5270
5424
  ]
5271
5425
  }) : /* @__PURE__ */ jsx(Box, {
@@ -5471,7 +5625,7 @@ async function runDoctorCI(options) {
5471
5625
  getUI().intro("Welcome to the PostHog setup wizard");
5472
5626
  getUI().log.info("Running posthog-doctor in CI mode");
5473
5627
  try {
5474
- const { getOrAskForProjectData } = await import("./setup-utils-VtxhrSx8.js").then((n) => n.r);
5628
+ const { getOrAskForProjectData } = await import("./setup-utils-C2rGcUFv.js").then((n) => n.r);
5475
5629
  const { host, accessToken, projectId } = await getOrAskForProjectData({
5476
5630
  signup: false,
5477
5631
  ci: true,
@@ -5489,7 +5643,7 @@ async function runDoctorCI(options) {
5489
5643
  for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
5490
5644
  process.exit(1);
5491
5645
  } catch (error) {
5492
- const { ApiError } = await import("./api-OHLXhKTy.js").then((n) => n.n);
5646
+ const { ApiError } = await import("./api-XHQTOfIi.js").then((n) => n.n);
5493
5647
  const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
5494
5648
  getUI().log.error(`Doctor failed: ${message}`);
5495
5649
  process.exit(1);
@@ -5571,8 +5725,8 @@ function runSlackConnect(argv) {
5571
5725
  (async () => {
5572
5726
  const debug = argv.debug;
5573
5727
  try {
5574
- const { startTUI } = await import("./start-tui-CzMiitZJ.js");
5575
- const { buildSession } = await import("./wizard-session-DsK16SGs.js");
5728
+ const { startTUI } = await import("./start-tui-BYTQwgvK.js");
5729
+ const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
5576
5730
  const tui = startTUI(VERSION, Program.SlackConnect);
5577
5731
  tui.store.session = buildSession({
5578
5732
  debug,
@@ -5904,6 +6058,6 @@ function resolveInstallDir() {
5904
6058
  }
5905
6059
  Wizard.use(basicIntegrationCommand).use(mcpCommand).use(mcpAnalyticsCommand).use(aiObservabilityCommand).use(cliCommand).use(auditCommand).use(doctorCommand).use(migrateCommand).use(revenueCommand).use(warehouseCommand).use(selfDrivingCommand).use(slackCommand).use(uploadSourcemapsCommand).use(skillCommand).init();
5906
6060
  //#endregion
5907
- export { getKindMeta as C, STRIPE_SDKS as D, POSTHOG_SDKS as E, SOURCE_MAPS_CONTEXT_KEYS as S, getContentBlocks$3 as T, DISPLAY_NAME as _, useStdoutDimensions as a, MANUAL_SDK_VARIANTS as b, runWizardHeadless as c, PROGRAM_REGISTRY as d, Program as f, AUTOMATABLE_VARIANTS as g, SELF_DRIVING_INTEGRATE_PATH_KEY as h, useKeyboardHintsContext as i, runWizardCI as l, detectSelfDrivingIntegrationProjects as m, useKeyBindings as n, ConfirmButton as o, getProgramConfig as p, KeyboardHintsProvider as r, PromptLabel as s, PickerMenu as t, runWizard as u, GO_SDK_MODULE as v, fetchHealthIssues as w, RUST_SDK_CRATE as x, MANUAL_SDK_INSTALL as y };
6061
+ export { STRIPE_SDKS as A, MANUAL_SDK_VARIANTS as C, fetchHealthIssues as D, getKindMeta as E, getContentBlocks$3 as O, MANUAL_SDK_INSTALL as S, SOURCE_MAPS_CONTEXT_KEYS as T, detectSelfDrivingIntegrationProjects as _, useStdoutDimensions as a, DISPLAY_NAME as b, runWizardHeadless as c, PROGRAM_REGISTRY as d, Program as f, PRICING_SHORT as g, PRICING_LONG as h, useKeyboardHintsContext as i, POSTHOG_SDKS as k, runWizardCI as l, NO_DEFAULT_LIMIT as m, useKeyBindings as n, ConfirmButton as o, getProgramConfig as p, KeyboardHintsProvider as r, PromptLabel as s, PickerMenu as t, runWizard as u, SELF_DRIVING_INTEGRATE_PATH_KEY as v, RUST_SDK_CRATE as w, GO_SDK_MODULE as x, AUTOMATABLE_VARIANTS as y };
5908
6062
 
5909
6063
  //# sourceMappingURL=bin.js.map