@posthog/wizard 2.59.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 (107) hide show
  1. package/README.md +33 -6
  2. package/dist/{add-mcp-server-to-clients-OsyI-Bkn.js → add-mcp-server-to-clients-KNIHRN_m.js} +190 -60
  3. package/dist/add-mcp-server-to-clients-KNIHRN_m.js.map +1 -0
  4. package/dist/{agent-interface-DB8B9fET.js → agent-interface-Bm4ZNGGV.js} +7 -7
  5. package/dist/{agent-interface-DB8B9fET.js.map → agent-interface-Bm4ZNGGV.js.map} +1 -1
  6. package/dist/{agent-prompt-loader-tcJQ-23m.js → agent-prompt-loader-CsHNQXsY.js} +38 -11
  7. package/dist/agent-prompt-loader-CsHNQXsY.js.map +1 -0
  8. package/dist/{agent-runner-2CRvJ1nS.js → agent-runner-DLAvB67P.js} +119 -19
  9. package/dist/agent-runner-DLAvB67P.js.map +1 -0
  10. package/dist/{analytics-D8LSdEq7.js → analytics-CkIyBHfd.js} +2 -2
  11. package/dist/{analytics-D8LSdEq7.js.map → analytics-CkIyBHfd.js.map} +1 -1
  12. package/dist/analytics-kxgLEv7U.js +2 -0
  13. package/dist/{api-XEopttY8.js → api-XHQTOfIi.js} +3 -3
  14. package/dist/{api-XEopttY8.js.map → api-XHQTOfIi.js.map} +1 -1
  15. package/dist/bin.js +322 -142
  16. package/dist/bin.js.map +1 -1
  17. package/dist/{bounded-fs-xzOAybCw.js → bounded-fs-B-qlKB0h.js} +3 -3
  18. package/dist/{bounded-fs-xzOAybCw.js.map → bounded-fs-B-qlKB0h.js.map} +1 -1
  19. package/dist/{ci-install-hArQ6Imb.js → ci-install-C9BAoO2n.js} +4 -4
  20. package/dist/{ci-install-hArQ6Imb.js.map → ci-install-C9BAoO2n.js.map} +1 -1
  21. package/dist/{debug-CxHQG5NZ.js → debug-8-IIHr5f.js} +6 -3
  22. package/dist/debug-8-IIHr5f.js.map +1 -0
  23. package/dist/{debug-BC-RNoVX.js → debug-npp6DzBK.js} +1 -1
  24. package/dist/{defaults-BWLIffh5.js → defaults-mrAre0xM.js} +28 -2
  25. package/dist/defaults-mrAre0xM.js.map +1 -0
  26. package/dist/{environment-D6GAX3yG.js → environment-BffaZJRI.js} +4 -4
  27. package/dist/{environment-D6GAX3yG.js.map → environment-BffaZJRI.js.map} +1 -1
  28. package/dist/{file-watcher-Bw5wz1_R.js → file-watcher-BreVxzh0.js} +2 -2
  29. package/dist/{file-watcher-Bw5wz1_R.js.map → file-watcher-BreVxzh0.js.map} +1 -1
  30. package/dist/{headless-ui-BGLCn1Dw.js → headless-ui-Dy_l8hCT.js} +2 -2
  31. package/dist/{headless-ui-BGLCn1Dw.js.map → headless-ui-Dy_l8hCT.js.map} +1 -1
  32. package/dist/{interactive-DRxZ-vOw.js → interactive-C9vepji1.js} +2 -2
  33. package/dist/{interactive-DRxZ-vOw.js.map → interactive-C9vepji1.js.map} +1 -1
  34. package/dist/{mcp-CRYv_nRt.js → mcp-C5Zheplw.js} +2 -2
  35. package/dist/{mcp-CRYv_nRt.js.map → mcp-C5Zheplw.js.map} +1 -1
  36. package/dist/{mcp-prompt-streaming-CO28aq_J.js → mcp-prompt-streaming-DEerU5M3.js} +3 -3
  37. package/dist/{mcp-prompt-streaming-CO28aq_J.js.map → mcp-prompt-streaming-DEerU5M3.js.map} +1 -1
  38. package/dist/{non-interactive-xhKZWWgs.js → non-interactive-C9vQaJF2.js} +2 -2
  39. package/dist/{non-interactive-xhKZWWgs.js.map → non-interactive-C9vQaJF2.js.map} +1 -1
  40. package/dist/{orchestrator-tools-PXqRljoB.js → orchestrator-tools-Bw97ahf6.js} +3 -3
  41. package/dist/{orchestrator-tools-PXqRljoB.js.map → orchestrator-tools-Bw97ahf6.js.map} +1 -1
  42. package/dist/{package-manager-C1HqgtVK.js → package-manager-Cm5vbUDR.js} +2 -2
  43. package/dist/{package-manager-C1HqgtVK.js.map → package-manager-Cm5vbUDR.js.map} +1 -1
  44. package/dist/{pi-DNkD3usA.js → pi-BocAqBQZ.js} +11 -11
  45. package/dist/{pi-DNkD3usA.js.map → pi-BocAqBQZ.js.map} +1 -1
  46. package/dist/{playground-BChSh0sx.js → playground-DmGHAiON.js} +25 -788
  47. package/dist/playground-DmGHAiON.js.map +1 -0
  48. package/dist/{posthog-B6grWFSr.js → posthog-BitL59Wc.js} +2 -2
  49. package/dist/{posthog-B6grWFSr.js.map → posthog-BitL59Wc.js.map} +1 -1
  50. package/dist/{posthog-integration-D-4mdP4b.js → posthog-integration-DbZ2oza2.js} +110 -33
  51. package/dist/posthog-integration-DbZ2oza2.js.map +1 -0
  52. package/dist/{provisioning-C1bETgMf.js → provisioning-CzRepu4H.js} +3 -3
  53. package/dist/{provisioning-C1bETgMf.js.map → provisioning-CzRepu4H.js.map} +1 -1
  54. package/dist/{queue-tools-D2FgGSUY.js → queue-tools-OynU-m2F.js} +37 -7
  55. package/dist/queue-tools-OynU-m2F.js.map +1 -0
  56. package/dist/{registry-CkGCJVqq.js → registry-eXzDlt51.js} +5 -5
  57. package/dist/{registry-CkGCJVqq.js.map → registry-eXzDlt51.js.map} +1 -1
  58. package/dist/{security-Cb5pp8kB.js → security-DqByFEpl.js} +5 -5
  59. package/dist/{security-Cb5pp8kB.js.map → security-DqByFEpl.js.map} +1 -1
  60. package/dist/{setup-utils-CcYGniRV.js → setup-utils-C2rGcUFv.js} +30 -13
  61. package/dist/setup-utils-C2rGcUFv.js.map +1 -0
  62. package/dist/{start-tui-BqyCxVie.js → start-tui-BYTQwgvK.js} +137 -42
  63. package/dist/start-tui-BYTQwgvK.js.map +1 -0
  64. package/dist/{steps-C0bR2Rxg.js → steps-CMIyEySI.js} +7 -7
  65. package/dist/{steps-C0bR2Rxg.js.map → steps-CMIyEySI.js.map} +1 -1
  66. package/dist/{store-suEDuCAn.js → store-pBYMaEuj.js} +25 -5
  67. package/dist/store-pBYMaEuj.js.map +1 -0
  68. package/dist/{subagent-Dd4Hu0C5.js → subagent-ZDD6XCmm.js} +2 -2
  69. package/dist/{subagent-Dd4Hu0C5.js.map → subagent-ZDD6XCmm.js.map} +1 -1
  70. package/dist/{task-CpU_54NS.js → task-1KHCdViK.js} +36 -23
  71. package/dist/task-1KHCdViK.js.map +1 -0
  72. package/dist/{task-stream-C_xfqch0.js → task-stream-DAU6Z6u6.js} +5 -5
  73. package/dist/{task-stream-C_xfqch0.js.map → task-stream-DAU6Z6u6.js.map} +1 -1
  74. package/dist/{tasks-Bfw6gqOO.js → tasks-BBo0_AZG.js} +2 -2
  75. package/dist/{tasks-Bfw6gqOO.js.map → tasks-BBo0_AZG.js.map} +1 -1
  76. package/dist/{telemetry-B62x5mwZ.js → telemetry-DSzPCPtQ.js} +3 -3
  77. package/dist/{telemetry-B62x5mwZ.js.map → telemetry-DSzPCPtQ.js.map} +1 -1
  78. package/dist/{terminal-CQjtfQzy.js → terminal-DW8x0UNT.js} +1031 -72
  79. package/dist/terminal-DW8x0UNT.js.map +1 -0
  80. package/dist/{tools-CzKtKaI1.js → tools-DCH7Ji4V.js} +6 -6
  81. package/dist/{tools-CzKtKaI1.js.map → tools-DCH7Ji4V.js.map} +1 -1
  82. package/dist/{wizard-abort-DPCzBsvy.js → wizard-abort-B_Sa9K3g.js} +1 -1
  83. package/dist/{wizard-abort-I6tR10Mx.js → wizard-abort-Cz916Qq5.js} +4 -4
  84. package/dist/{wizard-abort-I6tR10Mx.js.map → wizard-abort-Cz916Qq5.js.map} +1 -1
  85. package/dist/{wizard-session-CwgR5y3-.js → wizard-session-BUhlJ1b8.js} +2 -1
  86. package/dist/wizard-session-BUhlJ1b8.js.map +1 -0
  87. package/dist/{wizard-session-DsK16SGs.js → wizard-session-DZUDtS5d.js} +1 -1
  88. package/dist/yara-hooks-BVEBOdMu.js +2 -0
  89. package/dist/{yara-hooks-CpvNIFdW.js → yara-hooks-DgScuNrT.js} +4 -4
  90. package/dist/{yara-hooks-CpvNIFdW.js.map → yara-hooks-DgScuNrT.js.map} +1 -1
  91. package/package.json +2 -1
  92. package/dist/add-mcp-server-to-clients-OsyI-Bkn.js.map +0 -1
  93. package/dist/agent-prompt-loader-tcJQ-23m.js.map +0 -1
  94. package/dist/agent-runner-2CRvJ1nS.js.map +0 -1
  95. package/dist/analytics-fOKMWN6E.js +0 -2
  96. package/dist/debug-CxHQG5NZ.js.map +0 -1
  97. package/dist/defaults-BWLIffh5.js.map +0 -1
  98. package/dist/playground-BChSh0sx.js.map +0 -1
  99. package/dist/posthog-integration-D-4mdP4b.js.map +0 -1
  100. package/dist/queue-tools-D2FgGSUY.js.map +0 -1
  101. package/dist/setup-utils-CcYGniRV.js.map +0 -1
  102. package/dist/start-tui-BqyCxVie.js.map +0 -1
  103. package/dist/store-suEDuCAn.js.map +0 -1
  104. package/dist/task-CpU_54NS.js.map +0 -1
  105. package/dist/terminal-CQjtfQzy.js.map +0 -1
  106. package/dist/wizard-session-CwgR5y3-.js.map +0 -1
  107. package/dist/yara-hooks-lnituYce.js +0 -2
package/dist/bin.js CHANGED
@@ -1,23 +1,24 @@
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-CxHQG5NZ.js";
3
- import { n as analytics } from "./analytics-D8LSdEq7.js";
4
- import { r as setEntryCommand } from "./telemetry-B62x5mwZ.js";
5
- import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-xzOAybCw.js";
6
- import { n as isUsingTypeScript } from "./setup-utils-CcYGniRV.js";
7
- import { o as handleApiError } from "./api-XEopttY8.js";
8
- import "./wizard-session-CwgR5y3-.js";
9
- import { r as runCleanups } from "./wizard-abort-I6tR10Mx.js";
10
- import { n as isNonInteractiveEnvironment } from "./environment-D6GAX3yG.js";
11
- import { n as runAgent, o as authenticate } from "./agent-runner-2CRvJ1nS.js";
12
- import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-DB8B9fET.js";
13
- import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-CpvNIFdW.js";
14
- import { i as SPINNER_MESSAGE } from "./registry-CkGCJVqq.js";
15
- 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-D-4mdP4b.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";
16
17
  import { n as readApiKeyFromEnv } from "./env-api-key-cjD4yfGT.js";
17
18
  import { satisfies } from "semver";
18
19
  import yargs from "yargs";
19
20
  import { hideBin } from "yargs/helpers";
20
- import fs, { existsSync, readFileSync, readdirSync, statSync } from "fs";
21
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
21
22
  import path, { join, relative } from "path";
22
23
  import * as os from "node:os";
23
24
  import * as path$1 from "node:path";
@@ -27,9 +28,10 @@ import * as fs$1 from "node:fs";
27
28
  import axios from "axios";
28
29
  import { z } from "zod";
29
30
  import { Box, Text, render, useInput, useStdout } from "ink";
30
- import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
31
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
31
32
  import { jsx, jsxs } from "react/jsx-runtime";
32
33
  import { access, rm } from "node:fs/promises";
34
+ import Fuse from "fuse.js";
33
35
  import * as readline from "node:readline/promises";
34
36
  //#region src/commands/command.ts
35
37
  /** Extract the bare command word(s) from a yargs name spec, dropping positionals and aliases' arg syntax. */
@@ -200,7 +202,7 @@ function runProvision(argv) {
200
202
  }
201
203
  async function provision({ email, region, name, baseUrl, jsonMode }) {
202
204
  try {
203
- const { provisionNewAccount } = await import("./provisioning-C1bETgMf.js").then((n) => n.r);
205
+ const { provisionNewAccount } = await import("./provisioning-CzRepu4H.js").then((n) => n.r);
204
206
  if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
205
207
  emitResult(await provisionNewAccount(email, name, region, { baseUrl }), jsonMode);
206
208
  process.exit(0);
@@ -267,22 +269,22 @@ const basicIntegrationCommand = {
267
269
  setEntryCommand("integrate");
268
270
  (async () => {
269
271
  if (isHeadless(argv)) {
270
- const { runHeadlessInstall } = await import("./ci-install-hArQ6Imb.js");
272
+ const { runHeadlessInstall } = await import("./ci-install-C9BAoO2n.js");
271
273
  return runHeadlessInstall(argv);
272
274
  }
273
275
  if (argv.ci) {
274
- const { runCIInstall } = await import("./ci-install-hArQ6Imb.js");
276
+ const { runCIInstall } = await import("./ci-install-C9BAoO2n.js");
275
277
  return runCIInstall(argv);
276
278
  }
277
279
  if (isNonInteractiveEnvironment()) {
278
- const { failNonInteractive } = await import("./non-interactive-xhKZWWgs.js");
280
+ const { failNonInteractive } = await import("./non-interactive-C9vQaJF2.js");
279
281
  return failNonInteractive();
280
282
  }
281
283
  if (argv.playground) {
282
- const { runPlayground } = await import("./playground-BChSh0sx.js");
284
+ const { runPlayground } = await import("./playground-DmGHAiON.js");
283
285
  return runPlayground();
284
286
  }
285
- const { runInteractive } = await import("./interactive-DRxZ-vOw.js");
287
+ const { runInteractive } = await import("./interactive-C9vepji1.js");
286
288
  runInteractive(argv);
287
289
  })();
288
290
  }
@@ -665,10 +667,18 @@ const AUDIT_ABORT_CASES = [{
665
667
  //#endregion
666
668
  //#region src/lib/programs/audit/seed.ts
667
669
  /**
668
- * The 10 data-integrity checks the audit runs, plus one workflow row for the
669
- * notebook upload at the end (so the skill's `audit_resolve_checks` call for
670
- * `upload-notebook` succeeds — the skill writes the report to a PostHog
671
- * notebook as its final step).
670
+ * The 10 source-tree checks the audit runs, plus one row for the PostHog-side
671
+ * sweep and two workflow rows at the end (so the skill's
672
+ * `audit_resolve_checks` calls for `write-report` and `upload-notebook`
673
+ * succeed — the skill writes the report to disk, then mirrors it into a
674
+ * PostHog notebook as its final step).
675
+ *
676
+ * `posthog-side-findings` is a sweep row, not a rule: the skill reads what
677
+ * PostHog itself already computed for this project (error-tracking
678
+ * recommendations, health issues) and appends one row per open finding via
679
+ * `audit_add_checks`. That's why the seed can't enumerate them — the list is
680
+ * whatever the project's data says today, and it grows as PostHog ships new
681
+ * checks, with no wizard release needed.
672
682
  */
673
683
  const AUDIT_SEED_CHECKS = [
674
684
  {
@@ -731,6 +741,12 @@ const AUDIT_SEED_CHECKS = [
731
741
  label: "Key activation events captured",
732
742
  status: "pending"
733
743
  },
744
+ {
745
+ id: "live-data-findings",
746
+ area: "Live Data",
747
+ label: "Open findings in PostHog",
748
+ status: "pending"
749
+ },
734
750
  {
735
751
  id: "write-report",
736
752
  area: "Write report",
@@ -752,9 +768,7 @@ const AUDIT_SEED_CHECKS = [
752
768
  */
753
769
  function seedAuditLedger(installDir, checks = AUDIT_SEED_CHECKS) {
754
770
  const target = path.join(installDir, AUDIT_CHECKS_FILE);
755
- const tmp = `${target}.tmp`;
756
- fs.writeFileSync(tmp, JSON.stringify(checks, null, 2), "utf8");
757
- fs.renameSync(tmp, target);
771
+ writeJsonAtomic(target, checks);
758
772
  logToFile(`seedAuditLedger: wrote ${checks.length} entries to ${target}`);
759
773
  }
760
774
  //#endregion
@@ -3058,7 +3072,7 @@ function buildSelfDrivingPrompt(ctx, detectedSources = []) {
3058
3072
  const inboxUrl = `${projectBase}/inbox`;
3059
3073
  const optIn = (value) => value === true ? "ON" : value === false ? "OFF" : "unknown";
3060
3074
  const optIns = ctx.teamProductOptIns;
3061
- return `You are setting up PostHog Self-driving for this project: you will enable the right signal sources, make sure GitHub is connected, tune the scout troop, design custom scouts for what this product uniquely needs, and hand the user a configured inbox.
3075
+ return `You are setting up PostHog Self-driving for this project: you will enable the right signal sources, make sure GitHub is connected, tune the scout troop, design custom scouts for what this product uniquely needs, put Replay Vision scanners on its key flows, and hand the user a configured inbox.
3062
3076
 
3063
3077
  Project URLs:
3064
3078
  - Integrations settings: ${integrationsSettingsUrl}
@@ -3093,6 +3107,7 @@ tasks, in this order:
3093
3107
  5. Offer issue-tracker integrations
3094
3108
  6. Configure the scout troop
3095
3109
  6b. Design custom scouts
3110
+ 6c. Set up Replay Vision scanners
3096
3111
  7. Write report and hand off
3097
3112
  Drive the list with TaskUpdate — mark a task in_progress when you start
3098
3113
  it and completed when done. If a step turns out to be a no-op (e.g.
@@ -3183,6 +3198,16 @@ STEP 6 — Configure the scout troop. (skill: "Scouts")
3183
3198
  whole troop, including step 6b's custom scouts, stays at or under
3184
3199
  ten enabled scouts.
3185
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
+
3186
3211
  STEP 6b — Design custom scouts for this product. (skill: "Custom scouts")
3187
3212
  You are the only actor that has read this repo — turn that into
3188
3213
  coverage per the skill: a real gap analysis of the project's
@@ -3201,11 +3226,29 @@ STEP 6b — Design custom scouts for this product. (skill: "Custom scouts")
3201
3226
  no gap at all) is a valid outcome, not an abort. Mark the task
3202
3227
  completed either way.
3203
3228
 
3229
+ STEP 6c — Set up Replay Vision scanners. (skill: "Replay Vision scanners")
3230
+ Create the scanner skeletons the skill defines, filling the per-product
3231
+ blanks it leaves you from this repo's code. Scanners need Session Replay
3232
+ on (STEP 3b). Every failure here — no recordings yet, a backend-only
3233
+ project, the scanner API missing — is a follow-up, never an abort; mark
3234
+ the task completed regardless.
3235
+
3204
3236
  STEP 7 — Write the report and hand off. (skill: "Report")
3205
3237
  Write the report per the skill, including follow-ups for anything
3206
3238
  deferred. Tell the user findings will start appearing in their inbox
3207
3239
  at ${inboxUrl} within about 30 minutes.`;
3208
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.";
3209
3252
  //#endregion
3210
3253
  //#region src/lib/programs/self-driving/content/tips.ts
3211
3254
  const SELF_DRIVING_TIPS = [
@@ -3219,6 +3262,11 @@ const SELF_DRIVING_TIPS = [
3219
3262
  title: "Scout",
3220
3263
  description: "An agent on a schedule, watching your sources for anomalies and patterns. Your project gets up to 100 scout runs a day by default."
3221
3264
  },
3265
+ {
3266
+ id: "scanner",
3267
+ title: "Scanner",
3268
+ description: "Where a scout reads your data, a scanner watches session recordings — an LLM spotting what broke on screen. It spends Replay Vision quota, so it stays scoped to your key flows."
3269
+ },
3222
3270
  {
3223
3271
  id: "signal-report",
3224
3272
  title: "Signal → report",
@@ -3232,7 +3280,7 @@ const SELF_DRIVING_TIPS = [
3232
3280
  {
3233
3281
  id: "pricing",
3234
3282
  title: "What it costs",
3235
- description: "Watching is free. You pay a flat $15 only when a report ships a PR."
3283
+ description: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`
3236
3284
  },
3237
3285
  {
3238
3286
  id: "work-anywhere",
@@ -3471,8 +3519,12 @@ const getContentBlocks = (store) => {
3471
3519
  pause: 6e3
3472
3520
  },
3473
3521
  {
3474
- content: "That PR is the only thing you pay for: a flat $15 per report that ships a PR. Watching is free.",
3475
- pause: 6e3
3522
+ content: PRICING_LONG,
3523
+ pause: 5e3
3524
+ },
3525
+ {
3526
+ content: NO_DEFAULT_LIMIT,
3527
+ pause: 5e3
3476
3528
  },
3477
3529
  CLEAR,
3478
3530
  {
@@ -3607,11 +3659,12 @@ const buildRun = (session) => Promise.resolve({
3607
3659
  items: [
3608
3660
  "Investigate reports with the agent",
3609
3661
  "Tag teammates to loop them in",
3610
- "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",
3611
3664
  "Or work from Slack (tag @PostHog) and MCP"
3612
3665
  ]
3613
3666
  },
3614
- 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}`,
3615
3668
  reportFile: REPORT_FILE
3616
3669
  };
3617
3670
  }
@@ -3903,6 +3956,17 @@ function getProgramConfig(id) {
3903
3956
  return PROGRAM_REGISTRY.find((c) => c.id === id);
3904
3957
  }
3905
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
3906
3970
  //#region src/commands/mcp/add.ts
3907
3971
  const mcpAddCommand = {
3908
3972
  name: "add",
@@ -3932,8 +3996,8 @@ function runMcpAdd(argv) {
3932
3996
  const debug = argv.debug;
3933
3997
  const localMcp = argv.local;
3934
3998
  try {
3935
- const { startTUI } = await import("./start-tui-BqyCxVie.js");
3936
- 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");
3937
4001
  const tui = startTUI(VERSION, Program.McpAdd);
3938
4002
  tui.store.session = buildSession({
3939
4003
  debug,
@@ -3945,7 +4009,7 @@ function runMcpAdd(argv) {
3945
4009
  } catch (error) {
3946
4010
  if (!isTUIUnavailable(error)) throw error;
3947
4011
  setUI(new LoggingUI());
3948
- const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-OsyI-Bkn.js").then((n) => n.r);
4012
+ const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-KNIHRN_m.js").then((n) => n.r);
3949
4013
  await addMCPServerToClientsStep({
3950
4014
  local: localMcp,
3951
4015
  features,
@@ -3954,15 +4018,6 @@ function runMcpAdd(argv) {
3954
4018
  }
3955
4019
  })();
3956
4020
  }
3957
- /**
3958
- * Ink throws "Raw mode is not supported" when stdin has no TTY (piped input,
3959
- * CI, some IDE terminals). That is the only TUI failure we degrade to
3960
- * LoggingUI for — any other error from the TUI path is a real bug and must
3961
- * surface rather than be silently swallowed.
3962
- */
3963
- function isTUIUnavailable(error) {
3964
- return error instanceof Error && /raw mode is not supported/i.test(error.message);
3965
- }
3966
4021
  function parseFeatures(raw) {
3967
4022
  if (typeof raw !== "string") return void 0;
3968
4023
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -3984,17 +4039,18 @@ function runMcpRemove(argv) {
3984
4039
  const debug = argv.debug;
3985
4040
  const localMcp = argv.local;
3986
4041
  try {
3987
- const { startTUI } = await import("./start-tui-BqyCxVie.js");
3988
- 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");
3989
4044
  const tui = startTUI(VERSION, Program.McpRemove);
3990
4045
  tui.store.session = buildSession({
3991
4046
  debug,
3992
4047
  localMcp,
3993
4048
  baseUrl: argv.baseUrl
3994
4049
  });
3995
- } catch {
4050
+ } catch (error) {
4051
+ if (!isTUIUnavailable(error)) throw error;
3996
4052
  setUI(new LoggingUI());
3997
- const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-OsyI-Bkn.js").then((n) => n.r);
4053
+ const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-KNIHRN_m.js").then((n) => n.r);
3998
4054
  await removeMCPServerFromClientsStep({ local: localMcp });
3999
4055
  }
4000
4056
  })();
@@ -4016,8 +4072,8 @@ function runMcpTutorial(argv) {
4016
4072
  const debug = argv.debug;
4017
4073
  const localMcp = argv.local;
4018
4074
  try {
4019
- const { startTUI } = await import("./start-tui-BqyCxVie.js");
4020
- 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");
4021
4077
  const tui = startTUI(VERSION, Program.McpTutorial);
4022
4078
  tui.store.session = buildSession({
4023
4079
  debug,
@@ -4098,10 +4154,10 @@ function runWizard(config, options) {
4098
4154
  (async () => {
4099
4155
  try {
4100
4156
  const installDir = options.installDir || process.cwd();
4101
- const { startTUI } = await import("./start-tui-BqyCxVie.js");
4102
- const { buildSession, RunPhase } = await import("./wizard-session-DsK16SGs.js");
4103
- const { TaskStreamPush } = await import("./task-stream-C_xfqch0.js");
4104
- const { PostHogDestination } = await import("./posthog-B6grWFSr.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");
4105
4161
  tui = startTUI(WIZARD_VERSION, config.id);
4106
4162
  const activeTui = tui;
4107
4163
  const session = buildSession({
@@ -4167,7 +4223,7 @@ function runWizard(config, options) {
4167
4223
  if (shown(step)) await advanceStep(step, activeTui.store, config);
4168
4224
  }
4169
4225
  else if (skipAgent) {
4170
- const { getOrAskForProjectData } = await import("./setup-utils-CcYGniRV.js").then((n) => n.r);
4226
+ const { getOrAskForProjectData } = await import("./setup-utils-C2rGcUFv.js").then((n) => n.r);
4171
4227
  const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
4172
4228
  signup: session.signup,
4173
4229
  ci: session.ci,
@@ -4268,11 +4324,11 @@ function runNonInteractive(config, options, mode) {
4268
4324
  analytics.setTag("build", mode);
4269
4325
  (async () => {
4270
4326
  const path = await import("path");
4271
- const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-DsK16SGs.js");
4272
- const { readEnvironment } = await import("./environment-D6GAX3yG.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);
4273
4329
  const { readApiKeyFromEnv } = await import("./env-api-key-cjD4yfGT.js").then((n) => n.t);
4274
- const { configureLogFileFromEnvironment, logToFile } = await import("./debug-BC-RNoVX.js");
4275
- const { wizardAbort, WizardError } = await import("./wizard-abort-DPCzBsvy.js");
4330
+ const { configureLogFileFromEnvironment, logToFile } = await import("./debug-npp6DzBK.js");
4331
+ const { wizardAbort, WizardError } = await import("./wizard-abort-B_Sa9K3g.js");
4276
4332
  configureLogFileFromEnvironment();
4277
4333
  const env = readEnvironment();
4278
4334
  const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
@@ -4305,9 +4361,9 @@ function runNonInteractive(config, options, mode) {
4305
4361
  let store = null;
4306
4362
  let taskStream = null;
4307
4363
  if (mode === "headless") {
4308
- const { WizardStore } = await import("./store-suEDuCAn.js").then((n) => n.n);
4309
- const { HeadlessUI } = await import("./headless-ui-BGLCn1Dw.js");
4310
- const { TaskStreamPush, PostHogDestination } = await import("./task-stream-C_xfqch0.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");
4311
4367
  store = new WizardStore(config.id);
4312
4368
  store.session = session;
4313
4369
  setUI(new HeadlessUI(store));
@@ -4363,7 +4419,7 @@ function runNonInteractive(config, options, mode) {
4363
4419
  });
4364
4420
  }
4365
4421
  }
4366
- const { runAgent } = await import("./agent-runner-2CRvJ1nS.js").then((n) => n.t);
4422
+ const { runAgent } = await import("./agent-runner-DLAvB67P.js").then((n) => n.t);
4367
4423
  await runAgent(config, session);
4368
4424
  await settleStream(RunPhase.Completed);
4369
4425
  } catch (error) {
@@ -4641,6 +4697,81 @@ function pickerChildrenToShow(children) {
4641
4697
  return defaults.length > 0 ? [...defaults] : [...children];
4642
4698
  }
4643
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
4644
4775
  //#region src/ui/tui/primitives/PromptLabel.tsx
4645
4776
  /**
4646
4777
  * PromptLabel — Compact inline label for input prompts.
@@ -4722,6 +4853,8 @@ const DEFAULT_PRIORITY = {
4722
4853
  ["leftArrow"]: 1,
4723
4854
  ["rightArrow"]: 1,
4724
4855
  ["space"]: 10,
4856
+ ["printable"]: 12,
4857
+ ["backspace"]: 13,
4725
4858
  ["escape"]: 20,
4726
4859
  ["return"]: 21
4727
4860
  };
@@ -4739,6 +4872,8 @@ function matchesKey(m, input, key) {
4739
4872
  case "return": return !!key.return;
4740
4873
  case "escape": return !!key.escape;
4741
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");
4742
4877
  default: return input === m;
4743
4878
  }
4744
4879
  }
@@ -4931,29 +5066,70 @@ function usePickerViewport(count, rowCost, chromeBelow, enabled, focused) {
4931
5066
  pageStep: (f, dir) => (Math.floor(f / perPage) + dir + pageCount) % pageCount * perPage
4932
5067
  };
4933
5068
  }
4934
- const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
4935
- if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
4936
- 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,
4937
5078
  options,
4938
- centered,
4939
- columns,
4940
- optionMarginBottom,
4941
- onSelect
4942
- });
4943
- return /* @__PURE__ */ jsx(SinglePickerMenu, {
5079
+ filter
5080
+ ]);
5081
+ const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
5082
+ const shared = {
4944
5083
  message,
4945
- options,
5084
+ options: visible,
4946
5085
  centered,
4947
- columns,
4948
5086
  optionMarginBottom,
4949
- onSelect
4950
- });
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);
4951
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
+ }) });
4952
5128
  /** Custom single-select with triangle indicator and accent highlight. */
4953
- 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 }) => {
4954
5130
  const [focused, setFocused] = useState(() => firstEnabled(options));
4955
5131
  const rows = Math.ceil(options.length / columns);
4956
- 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);
4957
5133
  useEffect(() => {
4958
5134
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
4959
5135
  }, [options, focused]);
@@ -4963,22 +5139,10 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
4963
5139
  label: "↑↓",
4964
5140
  action: "navigate",
4965
5141
  handler: (_input, key) => {
4966
- const dir = key.upArrow ? -1 : 1;
4967
- if (columns === 1) {
4968
- const { start, end } = viewport;
4969
- const span = end - start;
4970
- let r = focused;
4971
- for (let i = 0; i < span; i++) {
4972
- r = start + (r - start + dir + span) % span;
4973
- if (!options[r]?.disabled) break;
4974
- }
4975
- setFocused(r);
4976
- return;
4977
- }
4978
- setFocused(stepEnabled(options, rows, focused, dir));
5142
+ setFocused(stepEnabled(options, rows, focused, key.upArrow ? -1 : 1));
4979
5143
  }
4980
5144
  },
4981
- ...viewport.needsScroll ? [{
5145
+ ...viewport.needsScroll && filter === null ? [{
4982
5146
  match: ["n", "p"],
4983
5147
  label: "n/p",
4984
5148
  action: "page",
@@ -5017,6 +5181,7 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5017
5181
  setFocused(next);
5018
5182
  }
5019
5183
  });
5184
+ if (filter !== null) bindings.push(...filterBindings(filter, setFilter));
5020
5185
  useKeyBindings("single-picker", bindings);
5021
5186
  const columnArrays = [];
5022
5187
  for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
@@ -5050,27 +5215,38 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5050
5215
  return /* @__PURE__ */ jsxs(Box, {
5051
5216
  flexDirection: "column",
5052
5217
  alignItems: align,
5053
- children: [/* @__PURE__ */ jsx(PromptLabel, { message }), viewport.needsScroll ? /* @__PURE__ */ jsxs(Box, {
5054
- flexDirection: "column",
5055
- children: [
5056
- /* @__PURE__ */ jsx(Text, {
5057
- dimColor: true,
5058
- children: viewport.hiddenAbove > 0 ? `↑ ${viewport.hiddenAbove} more [P] for previous page` : " "
5059
- }),
5060
- options.slice(viewport.start, viewport.end).map((opt, relIdx) => renderOption(opt, viewport.start + relIdx)),
5061
- /* @__PURE__ */ jsx(Text, {
5062
- dimColor: true,
5063
- children: viewport.hiddenBelow > 0 ? `↓ ${viewport.hiddenBelow} more [N] for next page` : " "
5064
- })
5065
- ]
5066
- }) : /* @__PURE__ */ jsx(Box, {
5067
- flexDirection: "row",
5068
- gap: 4,
5069
- 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, {
5070
5229
  flexDirection: "column",
5071
- children: colOpts.map((opt, rowIdx) => renderOption(opt, colIdx * rows + rowIdx))
5072
- }, colIdx))
5073
- })]
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
+ ]
5074
5250
  });
5075
5251
  };
5076
5252
  /**
@@ -5085,18 +5261,17 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
5085
5261
  * - moving onto the Confirm button and pressing enter submits the
5086
5262
  * current selection.
5087
5263
  */
5088
- 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 }) => {
5089
5265
  const [focused, setFocused] = useState(() => firstEnabled(options));
5090
5266
  const [onButton, setOnButton] = useState(false);
5091
- const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
5092
5267
  const rows = Math.ceil(options.length / columns);
5093
5268
  const rowCost = options.reduce((max, opt) => Math.max(max, 1 + optionMarginBottom + (opt.description ? wordWrap(opt.description, DESCRIPTION_WIDTH).length : 0)), 1);
5094
- 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);
5095
5270
  useEffect(() => {
5096
5271
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
5097
5272
  }, [options, focused]);
5098
5273
  const confirm = () => {
5099
- 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));
5100
5275
  };
5101
5276
  const bindings = [
5102
5277
  {
@@ -5110,13 +5285,6 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5110
5285
  setFocused(dir === -1 ? lastEnabled(options, viewport.start, viewport.end) : firstEnabled(options, viewport.start, viewport.end));
5111
5286
  return;
5112
5287
  }
5113
- if (columns === 1) {
5114
- let r = focused + dir;
5115
- while (r >= viewport.start && r < viewport.end && options[r]?.disabled) r += dir;
5116
- if (r >= viewport.start && r < viewport.end) setFocused(r);
5117
- else setOnButton(true);
5118
- return;
5119
- }
5120
5288
  const col = Math.floor(focused / rows);
5121
5289
  const row = focused % rows;
5122
5290
  const colLen = Math.min(rows, options.length - col * rows);
@@ -5126,7 +5294,7 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5126
5294
  else setOnButton(true);
5127
5295
  }
5128
5296
  },
5129
- ...viewport.needsScroll ? [{
5297
+ ...viewport.needsScroll && filter === null ? [{
5130
5298
  match: ["n", "p"],
5131
5299
  label: "n/p",
5132
5300
  action: "page",
@@ -5137,7 +5305,7 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5137
5305
  }
5138
5306
  }] : [],
5139
5307
  {
5140
- match: ["space", "return"],
5308
+ match: filter !== null ? "return" : ["space", "return"],
5141
5309
  label: "enter",
5142
5310
  action: "select",
5143
5311
  handler: () => {
@@ -5145,16 +5313,18 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5145
5313
  confirm();
5146
5314
  return;
5147
5315
  }
5148
- if (options[focused]?.disabled) return;
5316
+ const option = options[focused];
5317
+ if (!option || option.disabled) return;
5318
+ const label = option.label;
5149
5319
  setSelected((prev) => {
5150
5320
  const next = new Set(prev);
5151
- if (next.has(focused)) {
5152
- next.delete(focused);
5321
+ if (next.has(label)) {
5322
+ next.delete(label);
5153
5323
  return next;
5154
5324
  }
5155
- if (options[focused]?.exclusive) return new Set([focused]);
5156
- for (const i of next) if (options[i]?.exclusive) next.delete(i);
5157
- 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);
5158
5328
  return next;
5159
5329
  });
5160
5330
  }
@@ -5181,12 +5351,13 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5181
5351
  setFocused(next);
5182
5352
  }
5183
5353
  });
5354
+ if (filter !== null) bindings.push(...filterBindings(filter, setFilter));
5184
5355
  useKeyBindings("multi-picker", bindings);
5185
5356
  const columnArrays = [];
5186
5357
  for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
5187
5358
  const renderOption = (opt, flatIdx) => {
5188
5359
  const isFocused = !onButton && flatIdx === focused;
5189
- const isSelected = selected.has(flatIdx);
5360
+ const isSelected = selected.has(opt.label);
5190
5361
  const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
5191
5362
  const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
5192
5363
  return /* @__PURE__ */ jsxs(Box, {
@@ -5227,6 +5398,15 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5227
5398
  alignItems: centered ? "center" : void 0,
5228
5399
  children: [
5229
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
+ }),
5230
5410
  viewport.needsScroll ? /* @__PURE__ */ jsxs(Box, {
5231
5411
  flexDirection: "column",
5232
5412
  marginLeft: centered ? 0 : 2,
@@ -5234,12 +5414,12 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
5234
5414
  children: [
5235
5415
  /* @__PURE__ */ jsx(Text, {
5236
5416
  dimColor: true,
5237
- 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" : ""}` : " "
5238
5418
  }),
5239
5419
  options.slice(viewport.start, viewport.end).map((opt, relIdx) => renderOption(opt, viewport.start + relIdx)),
5240
5420
  /* @__PURE__ */ jsx(Text, {
5241
5421
  dimColor: true,
5242
- 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" : ""}` : " "
5243
5423
  })
5244
5424
  ]
5245
5425
  }) : /* @__PURE__ */ jsx(Box, {
@@ -5445,7 +5625,7 @@ async function runDoctorCI(options) {
5445
5625
  getUI().intro("Welcome to the PostHog setup wizard");
5446
5626
  getUI().log.info("Running posthog-doctor in CI mode");
5447
5627
  try {
5448
- const { getOrAskForProjectData } = await import("./setup-utils-CcYGniRV.js").then((n) => n.r);
5628
+ const { getOrAskForProjectData } = await import("./setup-utils-C2rGcUFv.js").then((n) => n.r);
5449
5629
  const { host, accessToken, projectId } = await getOrAskForProjectData({
5450
5630
  signup: false,
5451
5631
  ci: true,
@@ -5463,7 +5643,7 @@ async function runDoctorCI(options) {
5463
5643
  for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
5464
5644
  process.exit(1);
5465
5645
  } catch (error) {
5466
- const { ApiError } = await import("./api-XEopttY8.js").then((n) => n.n);
5646
+ const { ApiError } = await import("./api-XHQTOfIi.js").then((n) => n.n);
5467
5647
  const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
5468
5648
  getUI().log.error(`Doctor failed: ${message}`);
5469
5649
  process.exit(1);
@@ -5545,8 +5725,8 @@ function runSlackConnect(argv) {
5545
5725
  (async () => {
5546
5726
  const debug = argv.debug;
5547
5727
  try {
5548
- const { startTUI } = await import("./start-tui-BqyCxVie.js");
5549
- 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");
5550
5730
  const tui = startTUI(VERSION, Program.SlackConnect);
5551
5731
  tui.store.session = buildSession({
5552
5732
  debug,
@@ -5878,6 +6058,6 @@ function resolveInstallDir() {
5878
6058
  }
5879
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();
5880
6060
  //#endregion
5881
- 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 };
5882
6062
 
5883
6063
  //# sourceMappingURL=bin.js.map