@posthog/wizard 2.25.0 → 2.26.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 (60) hide show
  1. package/dist/{add-mcp-server-to-clients-t0xe8gn1.js → add-mcp-server-to-clients-C58l_KpV.js} +4 -4
  2. package/dist/{add-mcp-server-to-clients-t0xe8gn1.js.map → add-mcp-server-to-clients-C58l_KpV.js.map} +1 -1
  3. package/dist/{agent-interface-BsuUUPle.js → agent-interface-Dq_4h2eN.js} +39 -12
  4. package/dist/agent-interface-Dq_4h2eN.js.map +1 -0
  5. package/dist/{agent-runner-L_-kJ3y3.js → agent-runner-BNGW3osc.js} +176 -167
  6. package/dist/agent-runner-BNGW3osc.js.map +1 -0
  7. package/dist/{analytics-CDOujOSQ.js → analytics-BX3LKPch.js} +2 -2
  8. package/dist/{analytics-CDOujOSQ.js.map → analytics-BX3LKPch.js.map} +1 -1
  9. package/dist/{api-DNS-L-1U.js → api-DCHci5SD.js} +9 -5
  10. package/dist/api-DCHci5SD.js.map +1 -0
  11. package/dist/bin.js +583 -119
  12. package/dist/bin.js.map +1 -1
  13. package/dist/{ci-install-_9A7tL36.js → ci-install-CHIbwXio.js} +5 -5
  14. package/dist/{ci-install-_9A7tL36.js.map → ci-install-CHIbwXio.js.map} +1 -1
  15. package/dist/{debug-BwC7UkGH.js → debug-BizeRFR0.js} +3 -2
  16. package/dist/{debug-BwC7UkGH.js.map → debug-BizeRFR0.js.map} +1 -1
  17. package/dist/{debug-CZQcMAJT.js → debug-fg4BAKKA.js} +1 -1
  18. package/dist/{environment-DQPoj9sU.js → environment-DS5Pq9Wm.js} +3 -3
  19. package/dist/{environment-DQPoj9sU.js.map → environment-DS5Pq9Wm.js.map} +1 -1
  20. package/dist/{interactive-DT5dLd7N.js → interactive-DE3WDjk7.js} +3 -3
  21. package/dist/{interactive-DT5dLd7N.js.map → interactive-DE3WDjk7.js.map} +1 -1
  22. package/dist/{mcp-prompt-streaming-CBMr458Q.js → mcp-prompt-streaming-zsYd1zJx.js} +4 -4
  23. package/dist/{mcp-prompt-streaming-CBMr458Q.js.map → mcp-prompt-streaming-zsYd1zJx.js.map} +1 -1
  24. package/dist/{non-interactive-csP4yGdA.js → non-interactive-DNah9u3t.js} +2 -2
  25. package/dist/{non-interactive-csP4yGdA.js.map → non-interactive-DNah9u3t.js.map} +1 -1
  26. package/dist/{package-manager-CB4c2euf.js → package-manager-Dma9-zGs.js} +2 -2
  27. package/dist/{package-manager-CB4c2euf.js.map → package-manager-Dma9-zGs.js.map} +1 -1
  28. package/dist/{playground-C-lpKoKC.js → playground-Cwe0Q9HW.js} +145 -48
  29. package/dist/playground-Cwe0Q9HW.js.map +1 -0
  30. package/dist/{posthog-integration-BL8-vC0V.js → posthog-integration-CAYZdk0r.js} +11 -11
  31. package/dist/{posthog-integration-BL8-vC0V.js.map → posthog-integration-CAYZdk0r.js.map} +1 -1
  32. package/dist/{provisioning-DLOiFSM9.js → provisioning-BmL4ro-o.js} +10 -6
  33. package/dist/{provisioning-DLOiFSM9.js.map → provisioning-BmL4ro-o.js.map} +1 -1
  34. package/dist/{registry-BbRzCV5l.js → registry-C3wcDM3X.js} +4 -4
  35. package/dist/{registry-BbRzCV5l.js.map → registry-C3wcDM3X.js.map} +1 -1
  36. package/dist/{setup-utils-D87CyNkw.js → setup-utils-CNWIMZ-d.js} +71 -16
  37. package/dist/setup-utils-CNWIMZ-d.js.map +1 -0
  38. package/dist/{start-tui-DnAG57vY.js → start-tui-CS802Ww9.js} +311 -54
  39. package/dist/start-tui-CS802Ww9.js.map +1 -0
  40. package/dist/{steps-JaxH6u0f.js → steps-BX44xr30.js} +6 -6
  41. package/dist/{steps-JaxH6u0f.js.map → steps-BX44xr30.js.map} +1 -1
  42. package/dist/{telemetry-DL28cCwY.js → telemetry-BH-MgWPT.js} +3 -3
  43. package/dist/{telemetry-DL28cCwY.js.map → telemetry-BH-MgWPT.js.map} +1 -1
  44. package/dist/{AiOptInRequiredScreen-C-D9tN6r.js → terminal-BSiupnOQ.js} +1047 -85
  45. package/dist/terminal-BSiupnOQ.js.map +1 -0
  46. package/dist/{urls-vkJ5c0ix.js → urls-BuEABcmF.js} +2 -2
  47. package/dist/{urls-vkJ5c0ix.js.map → urls-BuEABcmF.js.map} +1 -1
  48. package/dist/{wizard-abort-BRXKRL4F.js → wizard-abort-CR3w2Efg.js} +1 -1
  49. package/dist/{wizard-abort-CLGgMAEe.js → wizard-abort-Dl2MJOP9.js} +3 -3
  50. package/dist/{wizard-abort-CLGgMAEe.js.map → wizard-abort-Dl2MJOP9.js.map} +1 -1
  51. package/dist/wizard-session-G3VWD6hv.js.map +1 -1
  52. package/dist/wizard-ui-WZ48rUgr.js.map +1 -1
  53. package/package.json +1 -1
  54. package/dist/AiOptInRequiredScreen-C-D9tN6r.js.map +0 -1
  55. package/dist/agent-interface-BsuUUPle.js.map +0 -1
  56. package/dist/agent-runner-L_-kJ3y3.js.map +0 -1
  57. package/dist/api-DNS-L-1U.js.map +0 -1
  58. package/dist/playground-C-lpKoKC.js.map +0 -1
  59. package/dist/setup-utils-D87CyNkw.js.map +0 -1
  60. package/dist/start-tui-DnAG57vY.js.map +0 -1
package/dist/bin.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { $ as getSkillsBaseUrl, P as POSTHOG_DOCS_URL, X as WIZARD_USER_AGENT, _ as SIGNUP_WIZARD_READINESS_CONFIG, a as getLogFilePath, et as VERSION, h as LoggingUI, m as setUI, p as getUI, r as debug, s as logToFile, v as evaluateWizardReadiness, y as getBlockingServiceKeys } from "./debug-BwC7UkGH.js";
3
- import { t as analytics } from "./analytics-CDOujOSQ.js";
4
- import { r as setEntryCommand } from "./telemetry-DL28cCwY.js";
5
- import { n as isUsingTypeScript } from "./setup-utils-D87CyNkw.js";
6
- import { a as getUiHostFromHost, n as getCloudUrlFromRegion } from "./urls-vkJ5c0ix.js";
7
- import { o as handleApiError } from "./api-DNS-L-1U.js";
2
+ import { $ as getSkillsBaseUrl, P as POSTHOG_DOCS_URL, X as WIZARD_USER_AGENT, _ as SIGNUP_WIZARD_READINESS_CONFIG, a as getLogFilePath, et as VERSION, h as LoggingUI, m as setUI, p as getUI, r as debug, s as logToFile, v as evaluateWizardReadiness, y as getBlockingServiceKeys } from "./debug-BizeRFR0.js";
3
+ import { t as analytics } from "./analytics-BX3LKPch.js";
4
+ import { r as setEntryCommand } from "./telemetry-BH-MgWPT.js";
5
+ import { n as isUsingTypeScript } from "./setup-utils-CNWIMZ-d.js";
6
+ import { a as getUiHostFromHost, n as getCloudUrlFromRegion } from "./urls-BuEABcmF.js";
7
+ import { o as handleApiError } from "./api-DCHci5SD.js";
8
8
  import "./wizard-session-G3VWD6hv.js";
9
- import { r as runCleanups } from "./wizard-abort-CLGgMAEe.js";
10
- import { n as isNonInteractiveEnvironment } from "./environment-DQPoj9sU.js";
11
- import { S as AUDIT_REPORT_FILE, b as AUDIT_CHECKS_FILE, c as recoverOrphanedSettingsBackups, h as fetchSkillMenu, p as WIZARD_TOOL_NAMES, u as AgentSignals, x as AUDIT_CHECKS_KEY } from "./agent-interface-BsuUUPle.js";
12
- import { i as SPINNER_MESSAGE } from "./registry-BbRzCV5l.js";
13
- import { a as PRODUCT_SUITE_BLOCK, f as Colors, i as LINE_CHART_BLOCK, l as isClearBlock, m as HEALTH_CHECK_STEP, n as posthogIntegrationConfig, o as StatusPeekTrigger, p as Icons, r as FUNNEL_BLOCK } from "./posthog-integration-BL8-vC0V.js";
9
+ import { r as runCleanups } from "./wizard-abort-Dl2MJOP9.js";
10
+ import { n as isNonInteractiveEnvironment } from "./environment-DS5Pq9Wm.js";
11
+ import { S as AUDIT_REPORT_FILE, b as AUDIT_CHECKS_FILE, c as recoverOrphanedSettingsBackups, h as fetchSkillMenu, p as WIZARD_TOOL_NAMES, u as AgentSignals, x as AUDIT_CHECKS_KEY } from "./agent-interface-Dq_4h2eN.js";
12
+ import { i as SPINNER_MESSAGE } from "./registry-C3wcDM3X.js";
13
+ import { a as PRODUCT_SUITE_BLOCK, f as Colors, i as LINE_CHART_BLOCK, l as isClearBlock, m as HEALTH_CHECK_STEP, n as posthogIntegrationConfig, o as StatusPeekTrigger, p as Icons, r as FUNNEL_BLOCK } from "./posthog-integration-CAYZdk0r.js";
14
14
  import { t as IGNORED_DIRS } from "./file-utils-VAXoyXVA.js";
15
15
  import { n as readApiKeyFromEnv } from "./env-api-key-MlzJYAvt.js";
16
16
  import { satisfies } from "semver";
@@ -27,6 +27,7 @@ import { z } from "zod";
27
27
  import { Box, Text, render, useInput } from "ink";
28
28
  import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
29
29
  import { jsx, jsxs } from "react/jsx-runtime";
30
+ import { access, rm } from "node:fs/promises";
30
31
  import * as readline from "node:readline/promises";
31
32
  //#region src/commands/command.ts
32
33
  /** Extract the bare command word(s) from a yargs name spec, dropping positionals and aliases' arg syntax. */
@@ -189,7 +190,7 @@ function runProvision(argv) {
189
190
  }
190
191
  async function provision({ email, region, name, jsonMode }) {
191
192
  try {
192
- const { provisionNewAccount } = await import("./provisioning-DLOiFSM9.js").then((n) => n.n);
193
+ const { provisionNewAccount } = await import("./provisioning-BmL4ro-o.js").then((n) => n.n);
193
194
  if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
194
195
  emitResult(await provisionNewAccount(email, name, region), jsonMode);
195
196
  process.exit(0);
@@ -254,18 +255,18 @@ const basicIntegrationCommand = {
254
255
  setEntryCommand("integrate");
255
256
  (async () => {
256
257
  if (argv.ci) {
257
- const { runCIInstall } = await import("./ci-install-_9A7tL36.js");
258
+ const { runCIInstall } = await import("./ci-install-CHIbwXio.js");
258
259
  return runCIInstall(argv);
259
260
  }
260
261
  if (isNonInteractiveEnvironment()) {
261
- const { failNonInteractive } = await import("./non-interactive-csP4yGdA.js");
262
+ const { failNonInteractive } = await import("./non-interactive-DNah9u3t.js");
262
263
  return failNonInteractive();
263
264
  }
264
265
  if (argv.playground) {
265
- const { runPlayground } = await import("./playground-C-lpKoKC.js");
266
+ const { runPlayground } = await import("./playground-Cwe0Q9HW.js");
266
267
  return runPlayground();
267
268
  }
268
- const { runInteractive } = await import("./interactive-DT5dLd7N.js");
269
+ const { runInteractive } = await import("./interactive-DE3WDjk7.js");
269
270
  runInteractive(argv);
270
271
  })();
271
272
  }
@@ -846,7 +847,7 @@ const EVENTS_AUDIT_SEED_CHECKS = [
846
847
  //#endregion
847
848
  //#region src/lib/programs/events-audit/index.ts
848
849
  const SETUP_REPORT_FILE = "posthog-events-audit-report.md";
849
- const DOCS_URL$2 = "https://posthog.com/docs/product-analytics/best-practices";
850
+ const DOCS_URL$3 = "https://posthog.com/docs/product-analytics/best-practices";
850
851
  const eventsAuditConfig = {
851
852
  command: "events-audit",
852
853
  description: "Audit PostHog event tracking in this project",
@@ -868,7 +869,7 @@ const eventsAuditConfig = {
868
869
  successMessage: "Events audit complete! You can view the report at ./posthog-events-audit-report.md",
869
870
  estimatedDurationMinutes: 5,
870
871
  reportFile: SETUP_REPORT_FILE,
871
- docsUrl: DOCS_URL$2,
872
+ docsUrl: DOCS_URL$3,
872
873
  errorMessage: "Events audit failed",
873
874
  additionalFeatureQueue: session.additionalFeatureQueue,
874
875
  customPrompt: (ctx) => `Audit PostHog event capture in this project. Do not modify any project files — produce a read-only report only.
@@ -886,7 +887,7 @@ Project context:
886
887
  message: "Your events audit was successful",
887
888
  reportFile: SETUP_REPORT_FILE,
888
889
  changes: [],
889
- docsUrl: DOCS_URL$2,
890
+ docsUrl: DOCS_URL$3,
890
891
  continueUrl: sess.signup && cloudUrl ? `${cloudUrl}/products?source=wizard` : void 0,
891
892
  dashboardUrl: sess.dashboardUrl ?? (cloudUrl ? `${cloudUrl}/dashboard` : void 0),
892
893
  notebookUrl: sess.notebookUrl ?? void 0
@@ -2415,15 +2416,15 @@ const getContentBlocks = (store) => pace([
2415
2416
  ]);
2416
2417
  //#endregion
2417
2418
  //#region src/lib/programs/error-tracking-upload-source-maps/index.ts
2418
- const REPORT_FILE = "posthog-source-maps-report.md";
2419
- const DOCS_URL = "https://posthog.com/docs/error-tracking/upload-source-maps";
2419
+ const REPORT_FILE$1 = "posthog-source-maps-report.md";
2420
+ const DOCS_URL$1 = "https://posthog.com/docs/error-tracking/upload-source-maps";
2420
2421
  const errorTrackingUploadSourceMapsConfig = {
2421
2422
  command: "upload-source-maps",
2422
2423
  description: "Upload source maps to PostHog Error Tracking",
2423
2424
  id: "error-tracking-upload-source-maps",
2424
2425
  requiresAi: true,
2425
2426
  steps: ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM,
2426
- reportFile: REPORT_FILE,
2427
+ reportFile: REPORT_FILE$1,
2427
2428
  getContentBlocks,
2428
2429
  requires: ["posthog-integration"],
2429
2430
  run: (session) => {
@@ -2433,8 +2434,8 @@ const errorTrackingUploadSourceMapsConfig = {
2433
2434
  return Promise.resolve({
2434
2435
  integrationLabel: "error-tracking-upload-source-maps",
2435
2436
  successMessage: "Source maps wired up!",
2436
- reportFile: REPORT_FILE,
2437
- docsUrl: DOCS_URL,
2437
+ reportFile: REPORT_FILE$1,
2438
+ docsUrl: DOCS_URL$1,
2438
2439
  spinnerMessage: "Wiring up source maps...",
2439
2440
  estimatedDurationMinutes: 3,
2440
2441
  abortCases: SOURCE_MAPS_ABORT_CASES,
@@ -2460,14 +2461,363 @@ const errorTrackingUploadSourceMapsConfig = {
2460
2461
  return {
2461
2462
  kind: "success",
2462
2463
  message: "Source maps wired up!",
2463
- reportFile: REPORT_FILE,
2464
- docsUrl: DOCS_URL
2464
+ reportFile: REPORT_FILE$1,
2465
+ docsUrl: DOCS_URL$1
2465
2466
  };
2466
2467
  }
2467
2468
  });
2468
2469
  }
2469
2470
  };
2470
2471
  //#endregion
2472
+ //#region src/lib/programs/self-driving/detect.ts
2473
+ /**
2474
+ * Self-driving prerequisite detection + abort vocabulary.
2475
+ *
2476
+ * The only thing worth verifying before auth is local and cheap: that
2477
+ * `session.installDir` is a real, readable directory. We deliberately do
2478
+ * NOT require the base posthog-integration report to be present — it is a
2479
+ * report many users never commit, and `requires: ['posthog-integration']`
2480
+ * is metadata, not a hard runtime gate. Real readiness (integration state
2481
+ * + beta access) is established by the agent's STEP 1 Signals API probe at
2482
+ * the start of the run. The beta gates (the `product-autonomy` access flag
2483
+ * and `signals-scout` enrollment — PostHog-side flag names, unchanged by
2484
+ * the wizard-side "self-driving" rename) are PostHog-internal flags with no
2485
+ * customer-facing read API, which is why that probe lives in the run and
2486
+ * emits a structured `[ABORT]` when the product is not available.
2487
+ */
2488
+ /**
2489
+ * `[ABORT] <reason>` cases the self-driving skill can emit. The
2490
+ * reason strings are part of the skill contract — the context-mill
2491
+ * `self-driving-setup` skill emits these exact strings.
2492
+ */
2493
+ const SELF_DRIVING_ABORT_CASES = [
2494
+ {
2495
+ match: /^self-driving is not available for this project$/i,
2496
+ message: "PostHog Self-driving is not available for this project",
2497
+ body: "Self-driving is in beta and is enabled per team by PostHog. This project does not appear to have access yet. Reach out to your PostHog contact (or wizard@posthog.com) to join the beta, then run the wizard again."
2498
+ },
2499
+ {
2500
+ match: /^github connection declined$/i,
2501
+ message: "GitHub connection required",
2502
+ body: "Self-driving needs GitHub access to research issues in your code and open fixes, so setup cannot finish without it. Nothing was left half-configured. When you are ready to install the PostHog GitHub App, run the wizard again."
2503
+ },
2504
+ {
2505
+ match: /^requires-interactive-mode$/i,
2506
+ message: "Interactive terminal required",
2507
+ body: "Self-driving setup asks questions along the way (GitHub and issue trackers), so it needs an interactive terminal. Run the wizard outside CI / non-interactive mode."
2508
+ },
2509
+ {
2510
+ match: /^requirements-incomplete$/i,
2511
+ message: "Setup needs your input",
2512
+ body: "The wizard could not collect the answers this setup needs (the environment was non-interactive, or the question budget ran out). Nothing was left half-configured. Run the wizard again in an interactive terminal."
2513
+ }
2514
+ ];
2515
+ /**
2516
+ * Verify `session.installDir` is a readable directory. Writes a
2517
+ * `SelfDrivingDetectError` to frameworkContext on failure — the intro
2518
+ * screen renders it and blocks.
2519
+ */
2520
+ function detectSelfDrivingPrerequisites(session, setFrameworkContext) {
2521
+ const fail = (error) => setFrameworkContext("detectError", error);
2522
+ const installDir = session.installDir;
2523
+ if (!existsSync(installDir)) {
2524
+ fail({
2525
+ kind: "bad-directory",
2526
+ path: installDir,
2527
+ reason: "missing"
2528
+ });
2529
+ return;
2530
+ }
2531
+ try {
2532
+ if (!statSync(installDir).isDirectory()) {
2533
+ fail({
2534
+ kind: "bad-directory",
2535
+ path: installDir,
2536
+ reason: "not-dir"
2537
+ });
2538
+ return;
2539
+ }
2540
+ } catch {
2541
+ fail({
2542
+ kind: "bad-directory",
2543
+ path: installDir,
2544
+ reason: "unreadable"
2545
+ });
2546
+ return;
2547
+ }
2548
+ }
2549
+ //#endregion
2550
+ //#region src/lib/programs/self-driving/steps.ts
2551
+ const SELF_DRIVING_PROGRAM = [
2552
+ {
2553
+ id: "detect",
2554
+ label: "Detecting prerequisites",
2555
+ onReady: (ctx) => detectSelfDrivingPrerequisites(ctx.session, ctx.setFrameworkContext)
2556
+ },
2557
+ {
2558
+ id: "intro",
2559
+ label: "Welcome",
2560
+ screenId: "self-driving-intro",
2561
+ gate: (session) => session.setupConfirmed
2562
+ },
2563
+ HEALTH_CHECK_STEP,
2564
+ {
2565
+ id: "auth",
2566
+ label: "Authentication",
2567
+ screenId: "auth",
2568
+ isComplete: (session) => session.credentials !== null
2569
+ },
2570
+ {
2571
+ id: "run",
2572
+ label: "Self-driving",
2573
+ screenId: "run",
2574
+ isComplete: (session) => session.runPhase === "completed" || session.runPhase === "error"
2575
+ },
2576
+ {
2577
+ id: "outro",
2578
+ label: "Done",
2579
+ screenId: "outro",
2580
+ isComplete: (session) => session.outroDismissed
2581
+ }
2582
+ ];
2583
+ //#endregion
2584
+ //#region src/lib/programs/self-driving/prompt.ts
2585
+ /**
2586
+ * Build the self-driving run prompt. The installed
2587
+ * `self-driving-setup` skill is the source of truth for the HOW of
2588
+ * every step (which MCP tools to call, which sources/scouts apply, how
2589
+ * to verify); this prompt carries the order, the wizard-specific
2590
+ * mechanics (wizard_ask, abort signals), and the project URLs.
2591
+ */
2592
+ function buildSelfDrivingPrompt(ctx) {
2593
+ const uiHost = getUiHostFromHost(ctx.host).replace(/\/$/, "");
2594
+ const projectBase = `${uiHost}/project/${ctx.projectId}`;
2595
+ const integrationsSettingsUrl = `${projectBase}/settings/environment-integrations`;
2596
+ const orgAiSettingsUrl = `${uiHost}/settings/organization#organization-ai-consent`;
2597
+ const newWarehouseSourceUrl = `${projectBase}/pipeline/new/source`;
2598
+ const inboxUrl = `${projectBase}/inbox`;
2599
+ const optIn = (value) => value === true ? "ON" : value === false ? "OFF" : "unknown";
2600
+ const optIns = ctx.teamProductOptIns;
2601
+ 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 fleet, design custom scouts for what this product uniquely needs, and hand the user a configured inbox.
2602
+
2603
+ Project URLs:
2604
+ - Integrations settings (GitHub App install): ${integrationsSettingsUrl}
2605
+ - Organization AI settings: ${orgAiSettingsUrl}
2606
+ - New data warehouse source (Linear / Zendesk / GitHub issues / pganalyze): ${newWarehouseSourceUrl}
2607
+ - Self-driving inbox: ${inboxUrl}
2608
+
2609
+ Project state read at auth time (PostHog project settings — authoritative
2610
+ for whether a product is enabled, regardless of what this repo
2611
+ instruments; products are often instrumented from other repos or the
2612
+ snippet, so repo evidence may rule a product IN but never OUT):
2613
+ - Session replay recording: ${optIn(optIns?.sessionReplay)}
2614
+ - Exception autocapture (error tracking): ${optIn(optIns?.exceptionAutocapture)}
2615
+ - Surveys: ${optIn(optIns?.surveys)}
2616
+
2617
+ The installed skill is the source of truth for the HOW of every step:
2618
+ which MCP tools to call, which sources and scouts apply to this product,
2619
+ and how to verify each change. The STEPS below give the order and the
2620
+ wizard-specific mechanics — read the matching skill reference before
2621
+ doing the work, and do not invent steps the skill doesn't describe.
2622
+
2623
+ Before doing any work, create your FULL task list in a single TaskCreate
2624
+ call so the user can follow your progress in the TUI. Use exactly these
2625
+ tasks, in this order:
2626
+ 1. Check Self-driving access
2627
+ 2. Read project and current Self-driving state
2628
+ 3. Connect GitHub (required)
2629
+ 4. Enable signal sources
2630
+ 5. Offer issue-tracker integrations
2631
+ 6. Configure the scout fleet
2632
+ 7. Design custom scouts
2633
+ 8. Write report and hand off
2634
+ Drive the list with TaskUpdate — mark a task in_progress when you start
2635
+ it and completed when done. If a step turns out to be a no-op (e.g.
2636
+ GitHub is already connected), still mark its task completed.
2637
+
2638
+ Wizard mechanics:
2639
+ - Ask the user things ONLY with the wizard_ask MCP tool, and batch
2640
+ related questions (e.g. one multi-select for all issue trackers, not
2641
+ one ask per tool). The per-run ask budget is limited.
2642
+ - If wizard_ask is unavailable (CI / non-interactive), emit
2643
+ ${AgentSignals.ABORT} requires-interactive-mode and halt.
2644
+ - When a step requires the user to do something in the browser, give
2645
+ them the exact URL inside the wizard_ask prompt text — do not try to
2646
+ open a browser yourself.
2647
+ - Emit ${AgentSignals.STATUS} lines as you complete each step so the
2648
+ user sees progress.
2649
+
2650
+ Follow these steps IN ORDER. Do not skip or reorder.
2651
+
2652
+ STEP 1 — Check Self-driving access. (skill: "Check access")
2653
+ Probe the Signals API as the skill describes. If the API is not
2654
+ available for this project (permission or not-found errors), emit
2655
+ ${AgentSignals.ABORT} self-driving is not available for this project
2656
+ and halt.
2657
+
2658
+ STEP 2 — Read project and current Signals state. (skill: "Read context")
2659
+ If ./posthog-setup-report.md exists, read it as a strong hint for what
2660
+ THIS repo instruments — but it is often absent (users frequently don't
2661
+ commit it), so do NOT depend on it. Combine whatever you find with the
2662
+ project-state block above and the skill's server-side usage probes —
2663
+ repo evidence rules products in, never out. Do a light scan ONLY for
2664
+ what neither covers. List the currently enabled signal sources so every
2665
+ later write is idempotent.
2666
+
2667
+ STEP 3 — Connect GitHub. REQUIRED. (skill: "Connect GitHub")
2668
+ Signals cannot research or fix issues without code access. Check for
2669
+ an existing GitHub integration first; if absent, send the user to
2670
+ ${integrationsSettingsUrl} via wizard_ask and verify the connection
2671
+ after they confirm. If the user cannot connect now, emit
2672
+ ${AgentSignals.ABORT} github connection declined
2673
+ and halt — never finish setup without GitHub.
2674
+
2675
+ STEP 4 — Enable signal sources. (skill: "Enable sources")
2676
+ Enable the sources that match what this product actually uses, per
2677
+ the skill. Never enable a source for a tool the user hasn't
2678
+ confirmed they use.
2679
+
2680
+ STEP 5 — Offer issue-tracker integrations. (skill: "Connected tools")
2681
+ One batched multi-select wizard_ask for the external tools the skill
2682
+ lists. The run auto-connects the ones it can (GitHub Issues, and
2683
+ Linear via a one-click OAuth link), verifying each with a single
2684
+ silent check — never nudge. It arms the rest as dormant responders to
2685
+ finish later: for tools it can't auto-connect (Zendesk, pganalyze) it
2686
+ never sends the user to paste credentials and never re-prompts. Enable
2687
+ a source only for a tool the user picked.
2688
+
2689
+ STEP 6 — Configure the scout fleet. (skill: "Scouts")
2690
+ Materialize the fleet and disable the scouts whose product surface
2691
+ this project lacks, per the skill.
2692
+
2693
+ STEP 7 — Design custom scouts for this product. (skill: "Custom scouts")
2694
+ You are the only actor that has read this repo — turn that into
2695
+ coverage per the skill: a real gap analysis of the project's
2696
+ watchable surfaces against what the canonical fleet already covers,
2697
+ then custom scouts for the uncovered ones. Keep scout bodies
2698
+ high-level: describe the behavior and signal conditions to watch,
2699
+ referencing repo evidence by file/function name — never paste raw
2700
+ source, secrets, env values, or customer data into a scout body.
2701
+ Never edit canonical scout bodies. Propose all candidates in ONE
2702
+ batched wizard_ask
2703
+ before creating anything; the user declining everything (or finding
2704
+ no gap at all) is a valid outcome, not an abort. Mark the task
2705
+ completed either way.
2706
+
2707
+ STEP 8 — Write the report and hand off. (skill: "Report")
2708
+ Write the report per the skill, including follow-ups for anything
2709
+ deferred. Tell the user findings will start appearing in their inbox
2710
+ at ${inboxUrl} within about 30 minutes.`;
2711
+ }
2712
+ //#endregion
2713
+ //#region src/lib/programs/self-driving/content/tips.ts
2714
+ const SELF_DRIVING_TIPS = [
2715
+ {
2716
+ id: "what-is-a-signal-source",
2717
+ title: "What's a signal source?",
2718
+ description: "A signal source is a PostHog product or connected tool — errors, session replays, support, GitHub or Linear issues — that feeds findings into your Self-driving inbox."
2719
+ },
2720
+ {
2721
+ id: "what-is-a-scout",
2722
+ title: "What's a scout?",
2723
+ description: "Scouts are scheduled checks that scan your data and flag issues — a spike in errors, a dropping funnel — straight to your inbox."
2724
+ },
2725
+ {
2726
+ id: "findings-in-inbox",
2727
+ title: "Findings land in your inbox",
2728
+ description: "Once setup finishes, PostHog starts scanning within ~30 minutes and surfaces what it finds in your Self-driving inbox — grouped, researched, and ready to act on."
2729
+ }
2730
+ ];
2731
+ const getTips = () => SELF_DRIVING_TIPS;
2732
+ //#endregion
2733
+ //#region src/lib/programs/self-driving/index.ts
2734
+ const SELF_DRIVING_SKILL_ID = "self-driving-setup";
2735
+ const REPORT_FILE = "posthog-self-driving-report.md";
2736
+ const DOCS_URL = "https://posthog.com/docs";
2737
+ const SUCCESS_MESSAGE = "Self-driving is on! PostHog will start scanning within ~30 minutes and surface findings in your inbox.";
2738
+ const WIZARD_MARKER = ".posthog-wizard";
2739
+ /**
2740
+ * Remove the installed setup skill. It is transient orchestration
2741
+ * knowledge (unlike integration skills such as the Next.js one, which
2742
+ * are worth keeping for the user's coding agents), so the program
2743
+ * cleans it up instead of showing the keep-skills prompt. Marker-
2744
+ * guarded: only directories the wizard installed are touched.
2745
+ */
2746
+ async function removeInstalledSkill(installDir) {
2747
+ const skillDir = join(installDir, ".claude", "skills", SELF_DRIVING_SKILL_ID);
2748
+ try {
2749
+ await access(join(skillDir, WIZARD_MARKER));
2750
+ } catch {
2751
+ return;
2752
+ }
2753
+ await rm(skillDir, {
2754
+ recursive: true,
2755
+ force: true
2756
+ }).catch(() => void 0);
2757
+ }
2758
+ const run = {
2759
+ skillId: SELF_DRIVING_SKILL_ID,
2760
+ integrationLabel: SELF_DRIVING_SKILL_ID,
2761
+ customPrompt: buildSelfDrivingPrompt,
2762
+ successMessage: SUCCESS_MESSAGE,
2763
+ reportFile: REPORT_FILE,
2764
+ docsUrl: DOCS_URL,
2765
+ spinnerMessage: "Setting up PostHog Self-driving...",
2766
+ estimatedDurationMinutes: 10,
2767
+ abortCases: SELF_DRIVING_ABORT_CASES,
2768
+ maxQuestions: 13,
2769
+ richLinks: true,
2770
+ askTimeoutMs: 1800 * 1e3,
2771
+ postRun: async (session) => {
2772
+ await removeInstalledSkill(session.installDir);
2773
+ },
2774
+ buildOutroData: (_session, credentials) => {
2775
+ return {
2776
+ kind: "success",
2777
+ message: "Self-driving is on. PostHog is scanning your project — first findings hit your inbox within ~30 minutes.",
2778
+ primaryLink: {
2779
+ label: "Your Self-driving inbox",
2780
+ url: `${getUiHostFromHost(credentials.host).replace(/\/$/, "")}/project/${credentials.projectId}/inbox`
2781
+ },
2782
+ nextSteps: {
2783
+ heading: "In your inbox you can:",
2784
+ items: [
2785
+ "Review the findings PostHog surfaces",
2786
+ "Triage what matters and dismiss the noise",
2787
+ "Kick off fixes and open issues"
2788
+ ]
2789
+ },
2790
+ reportFile: REPORT_FILE
2791
+ };
2792
+ }
2793
+ };
2794
+ const selfDrivingConfig = {
2795
+ ...createSkillProgram({
2796
+ skillId: SELF_DRIVING_SKILL_ID,
2797
+ command: "self-driving",
2798
+ id: "self-driving",
2799
+ description: "Set up PostHog Self-driving for this project",
2800
+ integrationLabel: SELF_DRIVING_SKILL_ID,
2801
+ successMessage: SUCCESS_MESSAGE,
2802
+ reportFile: REPORT_FILE,
2803
+ docsUrl: DOCS_URL,
2804
+ spinnerMessage: "Setting up PostHog Self-driving...",
2805
+ estimatedDurationMinutes: 10,
2806
+ requires: ["posthog-integration"],
2807
+ abortCases: SELF_DRIVING_ABORT_CASES
2808
+ }),
2809
+ steps: SELF_DRIVING_PROGRAM,
2810
+ run,
2811
+ getTips,
2812
+ getContentBlocks: (store) => {
2813
+ const blocks = getContentBlocks$2(store);
2814
+ return blocks.map((b, i) => i === blocks.length - 1 && typeof b === "object" ? {
2815
+ ...b,
2816
+ pause: 5e3
2817
+ } : b);
2818
+ }
2819
+ };
2820
+ //#endregion
2471
2821
  //#region src/lib/programs/mcp/index.ts
2472
2822
  const mcpAddConfig = {
2473
2823
  id: "mcp-add",
@@ -2589,6 +2939,7 @@ const PROGRAM_REGISTRY = [
2589
2939
  posthogDoctorConfig,
2590
2940
  webAnalyticsDoctorConfig,
2591
2941
  migrationConfig,
2942
+ selfDrivingConfig,
2592
2943
  agentSkillConfig,
2593
2944
  mcpAddConfig,
2594
2945
  mcpRemoveConfig,
@@ -2609,6 +2960,7 @@ const Program = {
2609
2960
  EventsAudit: eventsAuditConfig.id,
2610
2961
  PosthogDoctor: posthogDoctorConfig.id,
2611
2962
  WebAnalyticsDoctor: webAnalyticsDoctorConfig.id,
2963
+ SelfDriving: selfDrivingConfig.id,
2612
2964
  AgentSkill: agentSkillConfig.id,
2613
2965
  McpAdd: mcpAddConfig.id,
2614
2966
  McpRemove: mcpRemoveConfig.id,
@@ -2653,7 +3005,7 @@ function runMcpAdd(argv) {
2653
3005
  const debug = argv.debug;
2654
3006
  const localMcp = argv.local;
2655
3007
  try {
2656
- const { startTUI } = await import("./start-tui-DnAG57vY.js");
3008
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2657
3009
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2658
3010
  const tui = startTUI(VERSION, Program.McpAdd);
2659
3011
  tui.store.session = buildSession({
@@ -2665,7 +3017,7 @@ function runMcpAdd(argv) {
2665
3017
  } catch (error) {
2666
3018
  if (!isTUIUnavailable(error)) throw error;
2667
3019
  setUI(new LoggingUI());
2668
- const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-t0xe8gn1.js").then((n) => n.r);
3020
+ const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-C58l_KpV.js").then((n) => n.r);
2669
3021
  await addMCPServerToClientsStep({
2670
3022
  local: localMcp,
2671
3023
  features,
@@ -2704,7 +3056,7 @@ function runMcpRemove(argv) {
2704
3056
  const debug = argv.debug;
2705
3057
  const localMcp = argv.local;
2706
3058
  try {
2707
- const { startTUI } = await import("./start-tui-DnAG57vY.js");
3059
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2708
3060
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2709
3061
  const tui = startTUI(VERSION, Program.McpRemove);
2710
3062
  tui.store.session = buildSession({
@@ -2713,7 +3065,7 @@ function runMcpRemove(argv) {
2713
3065
  });
2714
3066
  } catch {
2715
3067
  setUI(new LoggingUI());
2716
- const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-t0xe8gn1.js").then((n) => n.r);
3068
+ const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-C58l_KpV.js").then((n) => n.r);
2717
3069
  await removeMCPServerFromClientsStep({ local: localMcp });
2718
3070
  }
2719
3071
  })();
@@ -2735,7 +3087,7 @@ function runMcpTutorial(argv) {
2735
3087
  const debug = argv.debug;
2736
3088
  const localMcp = argv.local;
2737
3089
  try {
2738
- const { startTUI } = await import("./start-tui-DnAG57vY.js");
3090
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2739
3091
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2740
3092
  const tui = startTUI(VERSION, Program.McpTutorial);
2741
3093
  tui.store.session = buildSession({
@@ -2790,7 +3142,7 @@ function runWizard(config, options) {
2790
3142
  (async () => {
2791
3143
  try {
2792
3144
  const installDir = options.installDir || process.cwd();
2793
- const { startTUI } = await import("./start-tui-DnAG57vY.js");
3145
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2794
3146
  const { buildSession, RunPhase } = await import("./wizard-session-wPJtNl4c.js");
2795
3147
  const { TaskStreamPush } = await import("./task-stream-BQNSp0qR.js");
2796
3148
  const { PostHogDestination } = await import("./posthog-Cr37rnla.js");
@@ -2846,7 +3198,7 @@ function runWizard(config, options) {
2846
3198
  await activeTui.store.getGate("health-check");
2847
3199
  const skipAgent = config.run == null;
2848
3200
  if (skipAgent) {
2849
- const { getOrAskForProjectData } = await import("./setup-utils-D87CyNkw.js").then((n) => n.r);
3201
+ const { getOrAskForProjectData } = await import("./setup-utils-CNWIMZ-d.js").then((n) => n.r);
2850
3202
  const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
2851
3203
  signup: session.signup,
2852
3204
  ci: session.ci,
@@ -2861,7 +3213,7 @@ function runWizard(config, options) {
2861
3213
  projectId
2862
3214
  });
2863
3215
  } else {
2864
- const { runAgent } = await import("./agent-runner-L_-kJ3y3.js");
3216
+ const { runAgent } = await import("./agent-runner-BNGW3osc.js");
2865
3217
  await runAgent(config, activeTui.store.session);
2866
3218
  }
2867
3219
  const isDone = () => skipAgent ? activeTui.store.session.outroDismissed : activeTui.store.session.skillsComplete;
@@ -2938,10 +3290,10 @@ function runWizardCI(config, options) {
2938
3290
  (async () => {
2939
3291
  const path = await import("path");
2940
3292
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2941
- const { readEnvironment } = await import("./environment-DQPoj9sU.js").then((n) => n.t);
3293
+ const { readEnvironment } = await import("./environment-DS5Pq9Wm.js").then((n) => n.t);
2942
3294
  const { readApiKeyFromEnv } = await import("./env-api-key-MlzJYAvt.js").then((n) => n.t);
2943
- const { configureLogFileFromEnvironment, logToFile } = await import("./debug-CZQcMAJT.js");
2944
- const { wizardAbort, WizardError } = await import("./wizard-abort-BRXKRL4F.js");
3295
+ const { configureLogFileFromEnvironment, logToFile } = await import("./debug-fg4BAKKA.js");
3296
+ const { wizardAbort, WizardError } = await import("./wizard-abort-CR3w2Efg.js");
2945
3297
  configureLogFileFromEnvironment();
2946
3298
  const env = readEnvironment();
2947
3299
  const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
@@ -2992,7 +3344,7 @@ function runWizardCI(config, options) {
2992
3344
  })
2993
3345
  });
2994
3346
  }
2995
- const { runAgent } = await import("./agent-runner-L_-kJ3y3.js");
3347
+ const { runAgent } = await import("./agent-runner-BNGW3osc.js");
2996
3348
  await runAgent(config, session);
2997
3349
  } catch (error) {
2998
3350
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -3210,6 +3562,34 @@ const PromptLabel = ({ message }) => {
3210
3562
  }) });
3211
3563
  };
3212
3564
  //#endregion
3565
+ //#region src/ui/tui/primitives/ConfirmButton.tsx
3566
+ /**
3567
+ * ConfirmButton — the confirm row used to submit a selection.
3568
+ *
3569
+ * Pure render. Multi-select menus (PickerMenu mode="multi", GroupedPickerMenu)
3570
+ * append this below their options as the final focusable row: the user toggles
3571
+ * options with enter, then arrows down onto this row and presses enter to
3572
+ * submit. This replaces the older "enter anywhere submits" pattern, which
3573
+ * confused people who expected enter to toggle the focused item.
3574
+ *
3575
+ * Renders flat, mirroring the option rows — a focus triangle and the label,
3576
+ * accent and bold when focused, dimmed otherwise — so it lines up under the
3577
+ * options instead of sitting in a separate boxed target.
3578
+ */
3579
+ const ConfirmButton = ({ label = "Confirm", focused, count }) => {
3580
+ const text = count && count > 0 ? `${label} (${count})` : label;
3581
+ return /* @__PURE__ */ jsxs(Text, {
3582
+ color: focused ? Colors.accent : void 0,
3583
+ bold: focused,
3584
+ dimColor: !focused,
3585
+ children: [
3586
+ focused ? Icons.triangleSmallRight : " ",
3587
+ " ",
3588
+ text
3589
+ ]
3590
+ });
3591
+ };
3592
+ //#endregion
3213
3593
  //#region src/ui/tui/hooks/keyboard-hints-utils.ts
3214
3594
  /** Default priorities by key type. */
3215
3595
  const DEFAULT_PRIORITY = {
@@ -3345,8 +3725,10 @@ function useKeyBindings(id, bindings) {
3345
3725
  //#region src/ui/tui/primitives/PickerMenu.tsx
3346
3726
  /**
3347
3727
  * PickerMenu — Single and multi select.
3348
- * Single mode: custom renderer with small triangle indicator.
3349
- * Multi mode: checkbox glyphs with space to toggle.
3728
+ * Single mode: custom renderer with small triangle indicator; enter selects.
3729
+ * Multi mode: checkbox glyphs toggled with enter, plus a focusable
3730
+ * Confirm button below the options. The cursor moves onto the button and
3731
+ * enter submits — see MultiPickerMenu for the rationale.
3350
3732
  *
3351
3733
  * Key bindings are declared via useKeyBindings, which auto-registers
3352
3734
  * hints in the KeyboardHintsBar.
@@ -3372,6 +3754,12 @@ function firstEnabled(options) {
3372
3754
  const idx = options.findIndex((o) => !o.disabled);
3373
3755
  return idx === -1 ? 0 : idx;
3374
3756
  }
3757
+ /** Index of the last enabled option, for wrapping from the button onto
3758
+ * the bottom of the grid. */
3759
+ function lastEnabled(options) {
3760
+ for (let i = options.length - 1; i >= 0; i--) if (!options[i]?.disabled) return i;
3761
+ return options.length - 1;
3762
+ }
3375
3763
  const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
3376
3764
  if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
3377
3765
  message,
@@ -3475,60 +3863,90 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
3475
3863
  })]
3476
3864
  });
3477
3865
  };
3478
- /** Custom multi-select with checkbox glyphs and accent highlight. */
3866
+ /**
3867
+ * Custom multi-select with checkbox glyphs and accent highlight.
3868
+ *
3869
+ * Interaction model (shared with GroupedPickerMenu):
3870
+ * - \u2191\u2193 move the cursor through the options AND onto the Confirm button,
3871
+ * which lives just past the last option.
3872
+ * - enter toggles the focused option (no more "space toggles but enter
3873
+ * advances" split that tripped people up). Space is kept as an
3874
+ * undocumented alias, but the hints bar advertises only enter.
3875
+ * - moving onto the Confirm button and pressing enter submits the
3876
+ * current selection.
3877
+ */
3479
3878
  const MultiPickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
3480
3879
  const [focused, setFocused] = useState(() => firstEnabled(options));
3880
+ const [onButton, setOnButton] = useState(false);
3481
3881
  const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
3482
3882
  const rows = Math.ceil(options.length / columns);
3483
3883
  useEffect(() => {
3484
3884
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
3485
3885
  }, [options, focused]);
3486
- const bindings = [
3487
- {
3488
- match: ["upArrow", "downArrow"],
3489
- label: "↑↓",
3490
- action: "navigate",
3491
- handler: (_input, key) => {
3492
- if (key.upArrow) setFocused(stepEnabled(options, rows, focused, -1));
3493
- if (key.downArrow) setFocused(stepEnabled(options, rows, focused, 1));
3886
+ const confirm = () => {
3887
+ onSelect([...selected].sort((a, b) => a - b).map((i) => options[i].value));
3888
+ };
3889
+ const bindings = [{
3890
+ match: ["upArrow", "downArrow"],
3891
+ label: "↑↓",
3892
+ action: "navigate",
3893
+ handler: (_input, key) => {
3894
+ if (key.upArrow) {
3895
+ if (onButton) {
3896
+ setOnButton(false);
3897
+ setFocused(lastEnabled(options));
3898
+ return;
3899
+ }
3900
+ const col = Math.floor(focused / rows);
3901
+ let r = focused % rows - 1;
3902
+ while (r >= 0 && options[col * rows + r]?.disabled) r--;
3903
+ if (r >= 0) setFocused(col * rows + r);
3904
+ else setOnButton(true);
3494
3905
  }
3495
- },
3496
- {
3497
- match: "space",
3498
- label: "space",
3499
- action: "toggle",
3500
- handler: () => {
3501
- if (options[focused]?.disabled) return;
3502
- setSelected((prev) => {
3503
- const next = new Set(prev);
3504
- if (next.has(focused)) {
3505
- next.delete(focused);
3506
- return next;
3507
- }
3508
- if (options[focused]?.exclusive) return new Set([focused]);
3509
- for (const i of next) if (options[i]?.exclusive) next.delete(i);
3510
- next.add(focused);
3511
- return next;
3512
- });
3906
+ if (key.downArrow) {
3907
+ if (onButton) {
3908
+ setOnButton(false);
3909
+ setFocused(firstEnabled(options));
3910
+ return;
3911
+ }
3912
+ const col = Math.floor(focused / rows);
3913
+ const row = focused % rows;
3914
+ const colLen = Math.min(rows, options.length - col * rows);
3915
+ let r = row + 1;
3916
+ while (r < colLen && options[col * rows + r]?.disabled) r++;
3917
+ if (r < colLen) setFocused(col * rows + r);
3918
+ else setOnButton(true);
3513
3919
  }
3514
- },
3515
- {
3516
- match: "return",
3517
- label: "enter",
3518
- action: "confirm",
3519
- handler: () => {
3520
- if (selected.size === 0) {
3521
- const hovered = options[focused];
3522
- if (hovered && !hovered.disabled) onSelect(hovered.value);
3523
- } else onSelect([...selected].sort().map((i) => options[i].value));
3920
+ }
3921
+ }, {
3922
+ match: ["space", "return"],
3923
+ label: "enter",
3924
+ action: "select",
3925
+ handler: () => {
3926
+ if (onButton) {
3927
+ confirm();
3928
+ return;
3524
3929
  }
3930
+ if (options[focused]?.disabled) return;
3931
+ setSelected((prev) => {
3932
+ const next = new Set(prev);
3933
+ if (next.has(focused)) {
3934
+ next.delete(focused);
3935
+ return next;
3936
+ }
3937
+ if (options[focused]?.exclusive) return new Set([focused]);
3938
+ for (const i of next) if (options[i]?.exclusive) next.delete(i);
3939
+ next.add(focused);
3940
+ return next;
3941
+ });
3525
3942
  }
3526
- ];
3943
+ }];
3527
3944
  if (columns > 1) bindings.splice(1, 0, {
3528
3945
  match: ["leftArrow", "rightArrow"],
3529
3946
  label: "←→",
3530
3947
  action: "navigate",
3531
3948
  handler: (_input, key) => {
3949
+ if (onButton) return;
3532
3950
  const col = Math.floor(focused / rows);
3533
3951
  const row = focused % rows;
3534
3952
  let next = focused;
@@ -3550,43 +3968,65 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
3550
3968
  return /* @__PURE__ */ jsxs(Box, {
3551
3969
  flexDirection: "column",
3552
3970
  alignItems: centered ? "center" : void 0,
3553
- children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsx(Box, {
3554
- flexDirection: "row",
3555
- gap: 4,
3556
- marginLeft: centered ? 0 : 2,
3557
- marginTop: 1,
3558
- children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
3559
- flexDirection: "column",
3560
- children: colOpts.map((opt, rowIdx) => {
3561
- const flatIdx = colIdx * rows + rowIdx;
3562
- const isFocused = flatIdx === focused;
3563
- const isSelected = selected.has(flatIdx);
3564
- const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
3565
- const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
3566
- return /* @__PURE__ */ jsxs(Box, {
3567
- gap: 1,
3568
- marginBottom: optionMarginBottom,
3569
- children: [
3570
- /* @__PURE__ */ jsx(Text, {
3571
- color: isSelected ? "white" : Colors.muted,
3572
- dimColor: !isFocused && !isSelected,
3573
- children: checkbox
3574
- }),
3575
- opt.icon && /* @__PURE__ */ jsx(Text, {
3576
- color: opt.icon.color,
3577
- children: opt.icon.glyph
3578
- }),
3579
- /* @__PURE__ */ jsx(Text, {
3580
- color: opt.disabled ? Colors.muted : isFocused ? Colors.accent : void 0,
3581
- bold: isFocused && !opt.disabled,
3582
- dimColor: !isFocused || opt.disabled,
3583
- children: label
3584
- })
3585
- ]
3586
- }, flatIdx);
3971
+ children: [
3972
+ /* @__PURE__ */ jsx(PromptLabel, { message }),
3973
+ /* @__PURE__ */ jsx(Box, {
3974
+ flexDirection: "row",
3975
+ gap: 4,
3976
+ marginLeft: centered ? 0 : 2,
3977
+ marginTop: 1,
3978
+ children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
3979
+ flexDirection: "column",
3980
+ children: colOpts.map((opt, rowIdx) => {
3981
+ const flatIdx = colIdx * rows + rowIdx;
3982
+ const isFocused = !onButton && flatIdx === focused;
3983
+ const isSelected = selected.has(flatIdx);
3984
+ const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
3985
+ const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
3986
+ return /* @__PURE__ */ jsxs(Box, {
3987
+ flexDirection: "column",
3988
+ marginBottom: optionMarginBottom,
3989
+ children: [/* @__PURE__ */ jsxs(Box, {
3990
+ gap: 1,
3991
+ children: [
3992
+ /* @__PURE__ */ jsx(Text, {
3993
+ color: isSelected ? "white" : Colors.muted,
3994
+ dimColor: !isFocused && !isSelected,
3995
+ children: checkbox
3996
+ }),
3997
+ opt.icon && /* @__PURE__ */ jsx(Text, {
3998
+ color: opt.icon.color,
3999
+ children: opt.icon.glyph
4000
+ }),
4001
+ /* @__PURE__ */ jsx(Text, {
4002
+ color: opt.disabled ? Colors.muted : isFocused ? Colors.accent : void 0,
4003
+ bold: isFocused && !opt.disabled,
4004
+ dimColor: !isFocused || opt.disabled,
4005
+ children: label
4006
+ })
4007
+ ]
4008
+ }), opt.description && /* @__PURE__ */ jsx(Box, {
4009
+ marginLeft: 4,
4010
+ width: 56,
4011
+ children: /* @__PURE__ */ jsx(Text, {
4012
+ dimColor: true,
4013
+ wrap: "wrap",
4014
+ children: opt.description
4015
+ })
4016
+ })]
4017
+ }, flatIdx);
4018
+ })
4019
+ }, colIdx))
4020
+ }),
4021
+ /* @__PURE__ */ jsx(Box, {
4022
+ marginTop: 1,
4023
+ marginLeft: centered ? 0 : 2,
4024
+ children: /* @__PURE__ */ jsx(ConfirmButton, {
4025
+ focused: onButton,
4026
+ count: selected.size
3587
4027
  })
3588
- }, colIdx))
3589
- })]
4028
+ })
4029
+ ]
3590
4030
  });
3591
4031
  };
3592
4032
  //#endregion
@@ -3771,7 +4211,7 @@ async function runDoctorCI(options) {
3771
4211
  getUI().intro("Welcome to the PostHog setup wizard");
3772
4212
  getUI().log.info("Running posthog-doctor in CI mode");
3773
4213
  try {
3774
- const { getOrAskForProjectData } = await import("./setup-utils-D87CyNkw.js").then((n) => n.r);
4214
+ const { getOrAskForProjectData } = await import("./setup-utils-CNWIMZ-d.js").then((n) => n.r);
3775
4215
  const { host, accessToken, projectId } = await getOrAskForProjectData({
3776
4216
  signup: false,
3777
4217
  ci: true,
@@ -3788,7 +4228,7 @@ async function runDoctorCI(options) {
3788
4228
  for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
3789
4229
  process.exit(1);
3790
4230
  } catch (error) {
3791
- const { ApiError } = await import("./api-DNS-L-1U.js").then((n) => n.n);
4231
+ const { ApiError } = await import("./api-DCHci5SD.js").then((n) => n.n);
3792
4232
  const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
3793
4233
  getUI().log.error(`Doctor failed: ${message}`);
3794
4234
  process.exit(1);
@@ -3836,6 +4276,30 @@ const migrateCommand = nativeCommandFactory(migrationConfig);
3836
4276
  */
3837
4277
  const revenueCommand = nativeCommandFactory(revenueAnalyticsConfig);
3838
4278
  //#endregion
4279
+ //#region src/commands/self-driving.ts
4280
+ const selfDrivingCommand = {
4281
+ name: "self-driving",
4282
+ description: selfDrivingConfig.description,
4283
+ options: {
4284
+ ...skillProgramOptions,
4285
+ ...selfDrivingConfig.cliOptions ?? {}
4286
+ },
4287
+ check: (argv) => {
4288
+ if (argv.signup) throw new Error("`self-driving` cannot run with --signup. It builds on an existing PostHog integration — run the base `wizard` to create your account and set up PostHog first, then run `wizard self-driving`.");
4289
+ if (argv.ci) throw new Error("`self-driving` cannot run in CI mode — it requires interactive steps (GitHub connect, issue-tracker selection, custom-scout approval).");
4290
+ return true;
4291
+ },
4292
+ handler: (argv) => {
4293
+ const extras = selfDrivingConfig.mapCliOptions?.(argv) ?? {};
4294
+ const options = {
4295
+ ...argv,
4296
+ ...extras
4297
+ };
4298
+ if (options.ci) runWizardCI(selfDrivingConfig, options);
4299
+ else runWizard(selfDrivingConfig, options);
4300
+ }
4301
+ };
4302
+ //#endregion
3839
4303
  //#region src/commands/slack.ts
3840
4304
  const slackCommand = {
3841
4305
  name: "slack",
@@ -3851,7 +4315,7 @@ function runSlackConnect(argv) {
3851
4315
  (async () => {
3852
4316
  const debug = argv.debug;
3853
4317
  try {
3854
- const { startTUI } = await import("./start-tui-DnAG57vY.js");
4318
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
3855
4319
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
3856
4320
  const tui = startTUI(VERSION, Program.SlackConnect);
3857
4321
  tui.store.session = buildSession({ debug });
@@ -4228,8 +4692,8 @@ function resolveInstallDir() {
4228
4692
  if (inline) return inline.slice(14);
4229
4693
  return process.env.POSTHOG_WIZARD_INSTALL_DIR ?? process.cwd();
4230
4694
  }
4231
- Wizard.use(basicIntegrationCommand).use(mcpCommand).use(cliCommand).use(auditCommand).use(doctorCommand).use(migrateCommand).use(revenueCommand).use(slackCommand).use(uploadSourcemapsCommand).use(skillCommand).init();
4695
+ Wizard.use(basicIntegrationCommand).use(mcpCommand).use(cliCommand).use(auditCommand).use(doctorCommand).use(migrateCommand).use(revenueCommand).use(selfDrivingCommand).use(slackCommand).use(uploadSourcemapsCommand).use(skillCommand).init();
4232
4696
  //#endregion
4233
- export { POSTHOG_SDKS$1 as _, PromptLabel as a, PROGRAM_REGISTRY as c, DISPLAY_NAME as d, SOURCE_MAPS_CONTEXT_KEYS as f, getContentBlocks$2 as g, fetchHealthIssues as h, useKeyboardHintsContext as i, Program as l, getKindMeta as m, useKeyBindings as n, runWizardCI as o, getContentBlocks$1 as p, KeyboardHintsProvider as r, runWizard as s, PickerMenu as t, getProgramConfig as u, STRIPE_SDKS as v };
4697
+ export { POSTHOG_SDKS$1 as _, ConfirmButton as a, runWizard as c, getProgramConfig as d, DISPLAY_NAME as f, getContentBlocks$2 as g, fetchHealthIssues as h, useKeyboardHintsContext as i, PROGRAM_REGISTRY as l, getKindMeta as m, useKeyBindings as n, PromptLabel as o, SOURCE_MAPS_CONTEXT_KEYS as p, KeyboardHintsProvider as r, runWizardCI as s, PickerMenu as t, Program as u, STRIPE_SDKS as v };
4234
4698
 
4235
4699
  //# sourceMappingURL=bin.js.map