@posthog/wizard 2.24.1 → 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 (69) hide show
  1. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js → add-mcp-server-to-clients-C58l_KpV.js} +4 -4
  2. package/dist/{add-mcp-server-to-clients-CfwEQT_z.js.map → add-mcp-server-to-clients-C58l_KpV.js.map} +1 -1
  3. package/dist/{agent-interface-D1vtN6Wn.js → agent-interface-Dq_4h2eN.js} +435 -45
  4. package/dist/agent-interface-Dq_4h2eN.js.map +1 -0
  5. package/dist/{agent-runner-CBbkS0Ro.js → agent-runner-BNGW3osc.js} +748 -132
  6. package/dist/agent-runner-BNGW3osc.js.map +1 -0
  7. package/dist/{analytics-CUr82BDl.js → analytics-BX3LKPch.js} +51 -17
  8. package/dist/analytics-BX3LKPch.js.map +1 -0
  9. package/dist/{api-CI3Z74NG.js → api-DCHci5SD.js} +9 -5
  10. package/dist/api-DCHci5SD.js.map +1 -0
  11. package/dist/bin.js +830 -120
  12. package/dist/bin.js.map +1 -1
  13. package/dist/{ci-install-D_kxNmbJ.js → ci-install-CHIbwXio.js} +5 -5
  14. package/dist/{ci-install-D_kxNmbJ.js.map → ci-install-CHIbwXio.js.map} +1 -1
  15. package/dist/{debug-DxA_f5QT.js → debug-BizeRFR0.js} +17 -8
  16. package/dist/debug-BizeRFR0.js.map +1 -0
  17. package/dist/{debug-zMvpNYb2.js → debug-fg4BAKKA.js} +1 -1
  18. package/dist/{environment-CyS37cmM.js → environment-DS5Pq9Wm.js} +3 -3
  19. package/dist/{environment-CyS37cmM.js.map → environment-DS5Pq9Wm.js.map} +1 -1
  20. package/dist/{interactive-CG6FFqSw.js → interactive-DE3WDjk7.js} +3 -3
  21. package/dist/{interactive-CG6FFqSw.js.map → interactive-DE3WDjk7.js.map} +1 -1
  22. package/dist/{mcp-prompt-streaming-DQz4FSb1.js → mcp-prompt-streaming-zsYd1zJx.js} +7 -26
  23. package/dist/mcp-prompt-streaming-zsYd1zJx.js.map +1 -0
  24. package/dist/{non-interactive-DWtHX3ZR.js → non-interactive-DNah9u3t.js} +2 -2
  25. package/dist/{non-interactive-DWtHX3ZR.js.map → non-interactive-DNah9u3t.js.map} +1 -1
  26. package/dist/{package-manager-BWUS4CP0.js → package-manager-Dma9-zGs.js} +2 -2
  27. package/dist/{package-manager-BWUS4CP0.js.map → package-manager-Dma9-zGs.js.map} +1 -1
  28. package/dist/{playground-D7AhMMF5.js → playground-Cwe0Q9HW.js} +146 -49
  29. package/dist/playground-Cwe0Q9HW.js.map +1 -0
  30. package/dist/{posthog-integration-DexZ2uHU.js → posthog-integration-CAYZdk0r.js} +11 -11
  31. package/dist/{posthog-integration-DexZ2uHU.js.map → posthog-integration-CAYZdk0r.js.map} +1 -1
  32. package/dist/{provisioning-9c-AQbsa.js → provisioning-BmL4ro-o.js} +10 -6
  33. package/dist/{provisioning-9c-AQbsa.js.map → provisioning-BmL4ro-o.js.map} +1 -1
  34. package/dist/{registry-CO7JVZyE.js → registry-C3wcDM3X.js} +4 -4
  35. package/dist/{registry-CO7JVZyE.js.map → registry-C3wcDM3X.js.map} +1 -1
  36. package/dist/{setup-utils-0U-_Md2G.js → setup-utils-CNWIMZ-d.js} +71 -16
  37. package/dist/setup-utils-CNWIMZ-d.js.map +1 -0
  38. package/dist/smoke-test.sh +36 -1
  39. package/dist/{start-tui-WNb3ET14.js → start-tui-CS802Ww9.js} +311 -54
  40. package/dist/start-tui-CS802Ww9.js.map +1 -0
  41. package/dist/{steps-BAUXDCC4.js → steps-BX44xr30.js} +6 -6
  42. package/dist/{steps-BAUXDCC4.js.map → steps-BX44xr30.js.map} +1 -1
  43. package/dist/{task-stream-CZawuzlz.js → task-stream-BQNSp0qR.js} +4 -3
  44. package/dist/task-stream-BQNSp0qR.js.map +1 -0
  45. package/dist/{telemetry-ycqCpNPr.js → telemetry-BH-MgWPT.js} +3 -3
  46. package/dist/{telemetry-ycqCpNPr.js.map → telemetry-BH-MgWPT.js.map} +1 -1
  47. package/dist/{AiOptInRequiredScreen-_33FOcVo.js → terminal-BSiupnOQ.js} +1058 -92
  48. package/dist/terminal-BSiupnOQ.js.map +1 -0
  49. package/dist/{urls-C8aJWvgh.js → urls-BuEABcmF.js} +2 -2
  50. package/dist/{urls-C8aJWvgh.js.map → urls-BuEABcmF.js.map} +1 -1
  51. package/dist/{wizard-abort-DWXyJdws.js → wizard-abort-CR3w2Efg.js} +1 -1
  52. package/dist/{wizard-abort-C6gRLxUE.js → wizard-abort-Dl2MJOP9.js} +3 -3
  53. package/dist/{wizard-abort-C6gRLxUE.js.map → wizard-abort-Dl2MJOP9.js.map} +1 -1
  54. package/dist/wizard-session-G3VWD6hv.js.map +1 -1
  55. package/dist/{wizard-ui-YdGFRyu_.js → wizard-ui-WZ48rUgr.js} +2 -1
  56. package/dist/wizard-ui-WZ48rUgr.js.map +1 -0
  57. package/package.json +1 -1
  58. package/dist/AiOptInRequiredScreen-_33FOcVo.js.map +0 -1
  59. package/dist/agent-interface-D1vtN6Wn.js.map +0 -1
  60. package/dist/agent-runner-CBbkS0Ro.js.map +0 -1
  61. package/dist/analytics-CUr82BDl.js.map +0 -1
  62. package/dist/api-CI3Z74NG.js.map +0 -1
  63. package/dist/debug-DxA_f5QT.js.map +0 -1
  64. package/dist/mcp-prompt-streaming-DQz4FSb1.js.map +0 -1
  65. package/dist/playground-D7AhMMF5.js.map +0 -1
  66. package/dist/setup-utils-0U-_Md2G.js.map +0 -1
  67. package/dist/start-tui-WNb3ET14.js.map +0 -1
  68. package/dist/task-stream-CZawuzlz.js.map +0 -1
  69. package/dist/wizard-ui-YdGFRyu_.js.map +0 -1
package/dist/bin.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { $ as VERSION, P as POSTHOG_DOCS_URL, Q as getSkillsBaseUrl, Y as WIZARD_USER_AGENT, _ as SIGNUP_WIZARD_READINESS_CONFIG, a as getLogFilePath, h as LoggingUI, m as setUI, p as getUI, s as logToFile, v as evaluateWizardReadiness, y as getBlockingServiceKeys } from "./debug-DxA_f5QT.js";
3
- import { t as analytics } from "./analytics-CUr82BDl.js";
4
- import { r as setEntryCommand } from "./telemetry-ycqCpNPr.js";
5
- import { n as isUsingTypeScript } from "./setup-utils-0U-_Md2G.js";
6
- import { a as getUiHostFromHost, n as getCloudUrlFromRegion } from "./urls-C8aJWvgh.js";
7
- import { o as handleApiError } from "./api-CI3Z74NG.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-C6gRLxUE.js";
10
- import { n as isNonInteractiveEnvironment } from "./environment-CyS37cmM.js";
11
- import { _ as AUDIT_CHECKS_KEY, f as WIZARD_TOOL_NAMES, g as AUDIT_CHECKS_FILE, l as AgentSignals, m as fetchSkillMenu, s as recoverOrphanedSettingsBackups, v as AUDIT_REPORT_FILE } from "./agent-interface-D1vtN6Wn.js";
12
- import { i as SPINNER_MESSAGE } from "./registry-CO7JVZyE.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-DexZ2uHU.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";
@@ -18,11 +18,17 @@ import yargs from "yargs";
18
18
  import { hideBin } from "yargs/helpers";
19
19
  import fs, { existsSync, readFileSync, readdirSync, statSync } from "fs";
20
20
  import path, { join, relative } from "path";
21
+ import * as os from "node:os";
22
+ import * as path$1 from "node:path";
23
+ import { spawnSync } from "node:child_process";
24
+ import * as fs$1 from "node:fs";
21
25
  import axios from "axios";
22
26
  import { z } from "zod";
23
27
  import { Box, Text, render, useInput } from "ink";
24
28
  import { createContext, createElement, useCallback, useContext, useEffect, useRef, useState } from "react";
25
29
  import { jsx, jsxs } from "react/jsx-runtime";
30
+ import { access, rm } from "node:fs/promises";
31
+ import * as readline from "node:readline/promises";
26
32
  //#region src/commands/command.ts
27
33
  /** Extract the bare command word(s) from a yargs name spec, dropping positionals and aliases' arg syntax. */
28
34
  function commandKeys(name) {
@@ -184,7 +190,7 @@ function runProvision(argv) {
184
190
  }
185
191
  async function provision({ email, region, name, jsonMode }) {
186
192
  try {
187
- const { provisionNewAccount } = await import("./provisioning-9c-AQbsa.js").then((n) => n.n);
193
+ const { provisionNewAccount } = await import("./provisioning-BmL4ro-o.js").then((n) => n.n);
188
194
  if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
189
195
  emitResult(await provisionNewAccount(email, name, region), jsonMode);
190
196
  process.exit(0);
@@ -249,18 +255,18 @@ const basicIntegrationCommand = {
249
255
  setEntryCommand("integrate");
250
256
  (async () => {
251
257
  if (argv.ci) {
252
- const { runCIInstall } = await import("./ci-install-D_kxNmbJ.js");
258
+ const { runCIInstall } = await import("./ci-install-CHIbwXio.js");
253
259
  return runCIInstall(argv);
254
260
  }
255
261
  if (isNonInteractiveEnvironment()) {
256
- const { failNonInteractive } = await import("./non-interactive-DWtHX3ZR.js");
262
+ const { failNonInteractive } = await import("./non-interactive-DNah9u3t.js");
257
263
  return failNonInteractive();
258
264
  }
259
265
  if (argv.playground) {
260
- const { runPlayground } = await import("./playground-D7AhMMF5.js");
266
+ const { runPlayground } = await import("./playground-Cwe0Q9HW.js");
261
267
  return runPlayground();
262
268
  }
263
- const { runInteractive } = await import("./interactive-CG6FFqSw.js");
269
+ const { runInteractive } = await import("./interactive-DE3WDjk7.js");
264
270
  runInteractive(argv);
265
271
  })();
266
272
  }
@@ -841,7 +847,7 @@ const EVENTS_AUDIT_SEED_CHECKS = [
841
847
  //#endregion
842
848
  //#region src/lib/programs/events-audit/index.ts
843
849
  const SETUP_REPORT_FILE = "posthog-events-audit-report.md";
844
- 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";
845
851
  const eventsAuditConfig = {
846
852
  command: "events-audit",
847
853
  description: "Audit PostHog event tracking in this project",
@@ -863,7 +869,7 @@ const eventsAuditConfig = {
863
869
  successMessage: "Events audit complete! You can view the report at ./posthog-events-audit-report.md",
864
870
  estimatedDurationMinutes: 5,
865
871
  reportFile: SETUP_REPORT_FILE,
866
- docsUrl: DOCS_URL$2,
872
+ docsUrl: DOCS_URL$3,
867
873
  errorMessage: "Events audit failed",
868
874
  additionalFeatureQueue: session.additionalFeatureQueue,
869
875
  customPrompt: (ctx) => `Audit PostHog event capture in this project. Do not modify any project files — produce a read-only report only.
@@ -881,7 +887,7 @@ Project context:
881
887
  message: "Your events audit was successful",
882
888
  reportFile: SETUP_REPORT_FILE,
883
889
  changes: [],
884
- docsUrl: DOCS_URL$2,
890
+ docsUrl: DOCS_URL$3,
885
891
  continueUrl: sess.signup && cloudUrl ? `${cloudUrl}/products?source=wizard` : void 0,
886
892
  dashboardUrl: sess.dashboardUrl ?? (cloudUrl ? `${cloudUrl}/dashboard` : void 0),
887
893
  notebookUrl: sess.notebookUrl ?? void 0
@@ -2410,15 +2416,15 @@ const getContentBlocks = (store) => pace([
2410
2416
  ]);
2411
2417
  //#endregion
2412
2418
  //#region src/lib/programs/error-tracking-upload-source-maps/index.ts
2413
- const REPORT_FILE = "posthog-source-maps-report.md";
2414
- 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";
2415
2421
  const errorTrackingUploadSourceMapsConfig = {
2416
2422
  command: "upload-source-maps",
2417
2423
  description: "Upload source maps to PostHog Error Tracking",
2418
2424
  id: "error-tracking-upload-source-maps",
2419
2425
  requiresAi: true,
2420
2426
  steps: ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM,
2421
- reportFile: REPORT_FILE,
2427
+ reportFile: REPORT_FILE$1,
2422
2428
  getContentBlocks,
2423
2429
  requires: ["posthog-integration"],
2424
2430
  run: (session) => {
@@ -2428,8 +2434,8 @@ const errorTrackingUploadSourceMapsConfig = {
2428
2434
  return Promise.resolve({
2429
2435
  integrationLabel: "error-tracking-upload-source-maps",
2430
2436
  successMessage: "Source maps wired up!",
2431
- reportFile: REPORT_FILE,
2432
- docsUrl: DOCS_URL,
2437
+ reportFile: REPORT_FILE$1,
2438
+ docsUrl: DOCS_URL$1,
2433
2439
  spinnerMessage: "Wiring up source maps...",
2434
2440
  estimatedDurationMinutes: 3,
2435
2441
  abortCases: SOURCE_MAPS_ABORT_CASES,
@@ -2455,14 +2461,363 @@ const errorTrackingUploadSourceMapsConfig = {
2455
2461
  return {
2456
2462
  kind: "success",
2457
2463
  message: "Source maps wired up!",
2458
- reportFile: REPORT_FILE,
2459
- docsUrl: DOCS_URL
2464
+ reportFile: REPORT_FILE$1,
2465
+ docsUrl: DOCS_URL$1
2460
2466
  };
2461
2467
  }
2462
2468
  });
2463
2469
  }
2464
2470
  };
2465
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
2466
2821
  //#region src/lib/programs/mcp/index.ts
2467
2822
  const mcpAddConfig = {
2468
2823
  id: "mcp-add",
@@ -2584,6 +2939,7 @@ const PROGRAM_REGISTRY = [
2584
2939
  posthogDoctorConfig,
2585
2940
  webAnalyticsDoctorConfig,
2586
2941
  migrationConfig,
2942
+ selfDrivingConfig,
2587
2943
  agentSkillConfig,
2588
2944
  mcpAddConfig,
2589
2945
  mcpRemoveConfig,
@@ -2604,6 +2960,7 @@ const Program = {
2604
2960
  EventsAudit: eventsAuditConfig.id,
2605
2961
  PosthogDoctor: posthogDoctorConfig.id,
2606
2962
  WebAnalyticsDoctor: webAnalyticsDoctorConfig.id,
2963
+ SelfDriving: selfDrivingConfig.id,
2607
2964
  AgentSkill: agentSkillConfig.id,
2608
2965
  McpAdd: mcpAddConfig.id,
2609
2966
  McpRemove: mcpRemoveConfig.id,
@@ -2648,7 +3005,7 @@ function runMcpAdd(argv) {
2648
3005
  const debug = argv.debug;
2649
3006
  const localMcp = argv.local;
2650
3007
  try {
2651
- const { startTUI } = await import("./start-tui-WNb3ET14.js");
3008
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2652
3009
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2653
3010
  const tui = startTUI(VERSION, Program.McpAdd);
2654
3011
  tui.store.session = buildSession({
@@ -2660,7 +3017,7 @@ function runMcpAdd(argv) {
2660
3017
  } catch (error) {
2661
3018
  if (!isTUIUnavailable(error)) throw error;
2662
3019
  setUI(new LoggingUI());
2663
- const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-CfwEQT_z.js").then((n) => n.r);
3020
+ const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-C58l_KpV.js").then((n) => n.r);
2664
3021
  await addMCPServerToClientsStep({
2665
3022
  local: localMcp,
2666
3023
  features,
@@ -2699,7 +3056,7 @@ function runMcpRemove(argv) {
2699
3056
  const debug = argv.debug;
2700
3057
  const localMcp = argv.local;
2701
3058
  try {
2702
- const { startTUI } = await import("./start-tui-WNb3ET14.js");
3059
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2703
3060
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2704
3061
  const tui = startTUI(VERSION, Program.McpRemove);
2705
3062
  tui.store.session = buildSession({
@@ -2708,7 +3065,7 @@ function runMcpRemove(argv) {
2708
3065
  });
2709
3066
  } catch {
2710
3067
  setUI(new LoggingUI());
2711
- const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-CfwEQT_z.js").then((n) => n.r);
3068
+ const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-C58l_KpV.js").then((n) => n.r);
2712
3069
  await removeMCPServerFromClientsStep({ local: localMcp });
2713
3070
  }
2714
3071
  })();
@@ -2730,7 +3087,7 @@ function runMcpTutorial(argv) {
2730
3087
  const debug = argv.debug;
2731
3088
  const localMcp = argv.local;
2732
3089
  try {
2733
- const { startTUI } = await import("./start-tui-WNb3ET14.js");
3090
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2734
3091
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2735
3092
  const tui = startTUI(VERSION, Program.McpTutorial);
2736
3093
  tui.store.session = buildSession({
@@ -2785,9 +3142,9 @@ function runWizard(config, options) {
2785
3142
  (async () => {
2786
3143
  try {
2787
3144
  const installDir = options.installDir || process.cwd();
2788
- const { startTUI } = await import("./start-tui-WNb3ET14.js");
3145
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
2789
3146
  const { buildSession, RunPhase } = await import("./wizard-session-wPJtNl4c.js");
2790
- const { TaskStreamPush } = await import("./task-stream-CZawuzlz.js");
3147
+ const { TaskStreamPush } = await import("./task-stream-BQNSp0qR.js");
2791
3148
  const { PostHogDestination } = await import("./posthog-Cr37rnla.js");
2792
3149
  tui = startTUI(WIZARD_VERSION, config.id);
2793
3150
  const activeTui = tui;
@@ -2841,7 +3198,7 @@ function runWizard(config, options) {
2841
3198
  await activeTui.store.getGate("health-check");
2842
3199
  const skipAgent = config.run == null;
2843
3200
  if (skipAgent) {
2844
- const { getOrAskForProjectData } = await import("./setup-utils-0U-_Md2G.js").then((n) => n.r);
3201
+ const { getOrAskForProjectData } = await import("./setup-utils-CNWIMZ-d.js").then((n) => n.r);
2845
3202
  const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
2846
3203
  signup: session.signup,
2847
3204
  ci: session.ci,
@@ -2856,7 +3213,7 @@ function runWizard(config, options) {
2856
3213
  projectId
2857
3214
  });
2858
3215
  } else {
2859
- const { runAgent } = await import("./agent-runner-CBbkS0Ro.js");
3216
+ const { runAgent } = await import("./agent-runner-BNGW3osc.js");
2860
3217
  await runAgent(config, activeTui.store.session);
2861
3218
  }
2862
3219
  const isDone = () => skipAgent ? activeTui.store.session.outroDismissed : activeTui.store.session.skillsComplete;
@@ -2933,10 +3290,10 @@ function runWizardCI(config, options) {
2933
3290
  (async () => {
2934
3291
  const path = await import("path");
2935
3292
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
2936
- const { readEnvironment } = await import("./environment-CyS37cmM.js").then((n) => n.t);
3293
+ const { readEnvironment } = await import("./environment-DS5Pq9Wm.js").then((n) => n.t);
2937
3294
  const { readApiKeyFromEnv } = await import("./env-api-key-MlzJYAvt.js").then((n) => n.t);
2938
- const { configureLogFileFromEnvironment, logToFile } = await import("./debug-zMvpNYb2.js");
2939
- const { wizardAbort, WizardError } = await import("./wizard-abort-DWXyJdws.js");
3295
+ const { configureLogFileFromEnvironment, logToFile } = await import("./debug-fg4BAKKA.js");
3296
+ const { wizardAbort, WizardError } = await import("./wizard-abort-CR3w2Efg.js");
2940
3297
  configureLogFileFromEnvironment();
2941
3298
  const env = readEnvironment();
2942
3299
  const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
@@ -2987,7 +3344,7 @@ function runWizardCI(config, options) {
2987
3344
  })
2988
3345
  });
2989
3346
  }
2990
- const { runAgent } = await import("./agent-runner-CBbkS0Ro.js");
3347
+ const { runAgent } = await import("./agent-runner-BNGW3osc.js");
2991
3348
  await runAgent(config, session);
2992
3349
  } catch (error) {
2993
3350
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -3205,6 +3562,34 @@ const PromptLabel = ({ message }) => {
3205
3562
  }) });
3206
3563
  };
3207
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
3208
3593
  //#region src/ui/tui/hooks/keyboard-hints-utils.ts
3209
3594
  /** Default priorities by key type. */
3210
3595
  const DEFAULT_PRIORITY = {
@@ -3340,8 +3725,10 @@ function useKeyBindings(id, bindings) {
3340
3725
  //#region src/ui/tui/primitives/PickerMenu.tsx
3341
3726
  /**
3342
3727
  * PickerMenu — Single and multi select.
3343
- * Single mode: custom renderer with small triangle indicator.
3344
- * 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.
3345
3732
  *
3346
3733
  * Key bindings are declared via useKeyBindings, which auto-registers
3347
3734
  * hints in the KeyboardHintsBar.
@@ -3367,6 +3754,12 @@ function firstEnabled(options) {
3367
3754
  const idx = options.findIndex((o) => !o.disabled);
3368
3755
  return idx === -1 ? 0 : idx;
3369
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
+ }
3370
3763
  const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
3371
3764
  if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
3372
3765
  message,
@@ -3470,60 +3863,90 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, opt
3470
3863
  })]
3471
3864
  });
3472
3865
  };
3473
- /** 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
+ */
3474
3878
  const MultiPickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
3475
3879
  const [focused, setFocused] = useState(() => firstEnabled(options));
3880
+ const [onButton, setOnButton] = useState(false);
3476
3881
  const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
3477
3882
  const rows = Math.ceil(options.length / columns);
3478
3883
  useEffect(() => {
3479
3884
  if (focused >= options.length || options[focused]?.disabled) setFocused(firstEnabled(options));
3480
3885
  }, [options, focused]);
3481
- const bindings = [
3482
- {
3483
- match: ["upArrow", "downArrow"],
3484
- label: "↑↓",
3485
- action: "navigate",
3486
- handler: (_input, key) => {
3487
- if (key.upArrow) setFocused(stepEnabled(options, rows, focused, -1));
3488
- 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);
3489
3905
  }
3490
- },
3491
- {
3492
- match: "space",
3493
- label: "space",
3494
- action: "toggle",
3495
- handler: () => {
3496
- if (options[focused]?.disabled) return;
3497
- setSelected((prev) => {
3498
- const next = new Set(prev);
3499
- if (next.has(focused)) {
3500
- next.delete(focused);
3501
- return next;
3502
- }
3503
- if (options[focused]?.exclusive) return new Set([focused]);
3504
- for (const i of next) if (options[i]?.exclusive) next.delete(i);
3505
- next.add(focused);
3506
- return next;
3507
- });
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);
3508
3919
  }
3509
- },
3510
- {
3511
- match: "return",
3512
- label: "enter",
3513
- action: "confirm",
3514
- handler: () => {
3515
- if (selected.size === 0) {
3516
- const hovered = options[focused];
3517
- if (hovered && !hovered.disabled) onSelect(hovered.value);
3518
- } 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;
3519
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
+ });
3520
3942
  }
3521
- ];
3943
+ }];
3522
3944
  if (columns > 1) bindings.splice(1, 0, {
3523
3945
  match: ["leftArrow", "rightArrow"],
3524
3946
  label: "←→",
3525
3947
  action: "navigate",
3526
3948
  handler: (_input, key) => {
3949
+ if (onButton) return;
3527
3950
  const col = Math.floor(focused / rows);
3528
3951
  const row = focused % rows;
3529
3952
  let next = focused;
@@ -3545,43 +3968,65 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, opti
3545
3968
  return /* @__PURE__ */ jsxs(Box, {
3546
3969
  flexDirection: "column",
3547
3970
  alignItems: centered ? "center" : void 0,
3548
- children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsx(Box, {
3549
- flexDirection: "row",
3550
- gap: 4,
3551
- marginLeft: centered ? 0 : 2,
3552
- marginTop: 1,
3553
- children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
3554
- flexDirection: "column",
3555
- children: colOpts.map((opt, rowIdx) => {
3556
- const flatIdx = colIdx * rows + rowIdx;
3557
- const isFocused = flatIdx === focused;
3558
- const isSelected = selected.has(flatIdx);
3559
- const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
3560
- const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
3561
- return /* @__PURE__ */ jsxs(Box, {
3562
- gap: 1,
3563
- marginBottom: optionMarginBottom,
3564
- children: [
3565
- /* @__PURE__ */ jsx(Text, {
3566
- color: isSelected ? "white" : Colors.muted,
3567
- dimColor: !isFocused && !isSelected,
3568
- children: checkbox
3569
- }),
3570
- opt.icon && /* @__PURE__ */ jsx(Text, {
3571
- color: opt.icon.color,
3572
- children: opt.icon.glyph
3573
- }),
3574
- /* @__PURE__ */ jsx(Text, {
3575
- color: opt.disabled ? Colors.muted : isFocused ? Colors.accent : void 0,
3576
- bold: isFocused && !opt.disabled,
3577
- dimColor: !isFocused || opt.disabled,
3578
- children: label
3579
- })
3580
- ]
3581
- }, 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
3582
4027
  })
3583
- }, colIdx))
3584
- })]
4028
+ })
4029
+ ]
3585
4030
  });
3586
4031
  };
3587
4032
  //#endregion
@@ -3766,7 +4211,7 @@ async function runDoctorCI(options) {
3766
4211
  getUI().intro("Welcome to the PostHog setup wizard");
3767
4212
  getUI().log.info("Running posthog-doctor in CI mode");
3768
4213
  try {
3769
- const { getOrAskForProjectData } = await import("./setup-utils-0U-_Md2G.js").then((n) => n.r);
4214
+ const { getOrAskForProjectData } = await import("./setup-utils-CNWIMZ-d.js").then((n) => n.r);
3770
4215
  const { host, accessToken, projectId } = await getOrAskForProjectData({
3771
4216
  signup: false,
3772
4217
  ci: true,
@@ -3783,7 +4228,7 @@ async function runDoctorCI(options) {
3783
4228
  for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
3784
4229
  process.exit(1);
3785
4230
  } catch (error) {
3786
- const { ApiError } = await import("./api-CI3Z74NG.js").then((n) => n.n);
4231
+ const { ApiError } = await import("./api-DCHci5SD.js").then((n) => n.n);
3787
4232
  const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
3788
4233
  getUI().log.error(`Doctor failed: ${message}`);
3789
4234
  process.exit(1);
@@ -3831,6 +4276,30 @@ const migrateCommand = nativeCommandFactory(migrationConfig);
3831
4276
  */
3832
4277
  const revenueCommand = nativeCommandFactory(revenueAnalyticsConfig);
3833
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
3834
4303
  //#region src/commands/slack.ts
3835
4304
  const slackCommand = {
3836
4305
  name: "slack",
@@ -3846,7 +4315,7 @@ function runSlackConnect(argv) {
3846
4315
  (async () => {
3847
4316
  const debug = argv.debug;
3848
4317
  try {
3849
- const { startTUI } = await import("./start-tui-WNb3ET14.js");
4318
+ const { startTUI } = await import("./start-tui-CS802Ww9.js");
3850
4319
  const { buildSession } = await import("./wizard-session-wPJtNl4c.js");
3851
4320
  const tui = startTUI(VERSION, Program.SlackConnect);
3852
4321
  tui.store.session = buildSession({ debug });
@@ -3967,6 +4436,247 @@ const skillCommand = {
3967
4436
  }
3968
4437
  };
3969
4438
  //#endregion
4439
+ //#region src/steps/install-cli-steering/index.ts
4440
+ const dirExists = (...segments) => {
4441
+ try {
4442
+ return fs$1.existsSync(path$1.join(os.homedir(), ...segments));
4443
+ } catch {
4444
+ return false;
4445
+ }
4446
+ };
4447
+ const CLI_STEERING_TARGETS = [
4448
+ {
4449
+ id: "claude-code",
4450
+ name: "Claude Code",
4451
+ instructionsPath: () => path$1.join(os.homedir(), ".claude", "CLAUDE.md"),
4452
+ isDetected: () => dirExists(".claude")
4453
+ },
4454
+ {
4455
+ id: "codex",
4456
+ name: "Codex",
4457
+ instructionsPath: () => path$1.join(os.homedir(), ".codex", "AGENTS.md"),
4458
+ isDetected: () => dirExists(".codex")
4459
+ },
4460
+ {
4461
+ id: "gemini",
4462
+ name: "Gemini CLI",
4463
+ instructionsPath: () => path$1.join(os.homedir(), ".gemini", "GEMINI.md"),
4464
+ isDetected: () => dirExists(".gemini")
4465
+ }
4466
+ ];
4467
+ function findTarget(id) {
4468
+ return CLI_STEERING_TARGETS.find((target) => target.id === id);
4469
+ }
4470
+ function detectTargets() {
4471
+ return CLI_STEERING_TARGETS.filter((target) => target.isDetected());
4472
+ }
4473
+ const spawnOptions = {
4474
+ encoding: "utf-8",
4475
+ shell: process.platform === "win32"
4476
+ };
4477
+ /**
4478
+ * Install or update the PostHog CLI in the user's environment. `npm install
4479
+ * --global @posthog/cli@latest` covers both first-time installs and upgrades
4480
+ * for existing npm-installed CLIs.
4481
+ */
4482
+ function installOrUpdatePostHogCli() {
4483
+ const args = [
4484
+ "install",
4485
+ "--global",
4486
+ "@posthog/cli@latest"
4487
+ ];
4488
+ debug(`Running npm ${args.join(" ")}`);
4489
+ const result = spawnSync("npm", args, spawnOptions);
4490
+ if (result.error) return {
4491
+ success: false,
4492
+ error: `Failed to run npm: ${result.error.message}. Is Node.js installed?`
4493
+ };
4494
+ if (result.status !== 0) return {
4495
+ success: false,
4496
+ error: (result.stderr || result.stdout || "").trim() || `npm install --global @posthog/cli@latest exited with status ${result.status ?? "unknown"}`
4497
+ };
4498
+ return { success: true };
4499
+ }
4500
+ /**
4501
+ * Delegate the actual write to the installed `posthog-cli api agents-md
4502
+ * install`. The steering snippet lives in the CLI (its single source of truth),
4503
+ * so the command should run only after `installOrUpdatePostHogCli` refreshes
4504
+ * the CLI to the latest release.
4505
+ */
4506
+ function installSteeringSnippet(filePath) {
4507
+ const args = [
4508
+ "api",
4509
+ "agents-md",
4510
+ "install",
4511
+ "--path",
4512
+ filePath
4513
+ ];
4514
+ debug(`Running posthog-cli ${args.join(" ")}`);
4515
+ const result = spawnSync("posthog-cli", args, {
4516
+ ...spawnOptions,
4517
+ env: {
4518
+ ...process.env,
4519
+ POSTHOG_CLI_EXPERIMENTAL_API: "1"
4520
+ }
4521
+ });
4522
+ if (result.error) return {
4523
+ success: false,
4524
+ filePath,
4525
+ error: `Failed to run posthog-cli: ${result.error.message}. Make sure npm's global bin directory is on your PATH.`
4526
+ };
4527
+ if (result.status !== 0) return {
4528
+ success: false,
4529
+ filePath,
4530
+ error: (result.stderr || result.stdout || "").trim() || `posthog-cli exited with status ${result.status ?? "unknown"}`
4531
+ };
4532
+ return {
4533
+ success: true,
4534
+ filePath
4535
+ };
4536
+ }
4537
+ //#endregion
4538
+ //#region src/commands/cli/add.ts
4539
+ const cliAddCommand = {
4540
+ name: "add",
4541
+ description: "Install or update PostHog CLI and add steering instructions to your coding agent's global instructions file",
4542
+ options: {
4543
+ agent: {
4544
+ describe: "Agent to install the instructions for",
4545
+ choices: CLI_STEERING_TARGETS.map((target) => target.id),
4546
+ type: "string"
4547
+ },
4548
+ path: {
4549
+ describe: "Write to an explicit instructions file instead of a detected agent",
4550
+ type: "string"
4551
+ },
4552
+ all: {
4553
+ default: false,
4554
+ describe: "Install for every detected agent without prompting",
4555
+ type: "boolean"
4556
+ }
4557
+ },
4558
+ examples: [
4559
+ ["wizard cli add", "Detect your coding agents and pick one"],
4560
+ ["wizard cli add --agent claude-code", "Install for Claude Code (~/.claude/CLAUDE.md)"],
4561
+ ["wizard cli add --all", "Install for every detected agent"],
4562
+ ["wizard cli add --path ./AGENTS.md", "Install into a specific instructions file"]
4563
+ ],
4564
+ check: (argv) => {
4565
+ if (argv.all && (argv.agent || argv.path)) throw new Error("--all cannot be combined with --agent or --path");
4566
+ return true;
4567
+ },
4568
+ handler: (argv) => {
4569
+ runCliAdd(argv);
4570
+ }
4571
+ };
4572
+ async function runCliAdd(argv) {
4573
+ setUI(new LoggingUI());
4574
+ const ui = getUI();
4575
+ ui.intro("PostHog CLI setup");
4576
+ const files = await resolveTargetFiles(argv);
4577
+ if (files.length === 0) {
4578
+ process.exit(1);
4579
+ return;
4580
+ }
4581
+ ui.log.info("Installing or updating PostHog CLI...");
4582
+ const cliInstallResult = installOrUpdatePostHogCli();
4583
+ if (!cliInstallResult.success) {
4584
+ ui.log.error(`Failed to install or update PostHog CLI: ${cliInstallResult.error ?? ""}`);
4585
+ analytics.wizardCapture("cli steering installed", {
4586
+ files: files.length,
4587
+ failures: files.length,
4588
+ cli_install_failed: true,
4589
+ agent: typeof argv.agent === "string" ? argv.agent : void 0
4590
+ });
4591
+ process.exit(1);
4592
+ return;
4593
+ }
4594
+ ui.log.success("Installed or updated PostHog CLI.");
4595
+ let failures = 0;
4596
+ for (const file of files) {
4597
+ const result = installSteeringSnippet(file);
4598
+ if (result.success) ui.log.success(`Installed PostHog steering instructions in ${file}`);
4599
+ else {
4600
+ failures += 1;
4601
+ ui.log.error(`Failed to update ${file}: ${result.error ?? ""}`);
4602
+ }
4603
+ }
4604
+ analytics.wizardCapture("cli steering installed", {
4605
+ files: files.length,
4606
+ failures,
4607
+ agent: typeof argv.agent === "string" ? argv.agent : void 0
4608
+ });
4609
+ if (failures > 0) {
4610
+ process.exit(1);
4611
+ return;
4612
+ }
4613
+ ui.outro("Done. PostHog CLI is installed and your agent will now use `posthog-cli api` for PostHog tasks.");
4614
+ process.exit(0);
4615
+ }
4616
+ /** Resolve which instruction files to write, from flags, detection, or a prompt. */
4617
+ async function resolveTargetFiles(argv) {
4618
+ const ui = getUI();
4619
+ if (typeof argv.path === "string" && argv.path.trim()) return [path$1.resolve(argv.path.trim())];
4620
+ if (typeof argv.agent === "string") {
4621
+ const target = findTarget(argv.agent);
4622
+ if (!target) {
4623
+ ui.log.error(`Unsupported agent: ${argv.agent}`);
4624
+ return [];
4625
+ }
4626
+ return [target.instructionsPath()];
4627
+ }
4628
+ const detected = detectTargets();
4629
+ if (detected.length === 0) {
4630
+ ui.log.error("No supported coding agents detected. Pass --agent <id> or --path <file> to choose a target.");
4631
+ ui.log.info(`Supported agents: ${CLI_STEERING_TARGETS.map((t) => t.id).join(", ")}`);
4632
+ return [];
4633
+ }
4634
+ if (argv.all === true) {
4635
+ ui.log.info(`Installing for all detected agents: ${detected.map((t) => t.name).join(", ")}`);
4636
+ return detected.map((target) => target.instructionsPath());
4637
+ }
4638
+ if (detected.length === 1) {
4639
+ ui.log.info(`Detected ${detected[0].name} (${detected[0].instructionsPath()})`);
4640
+ return [detected[0].instructionsPath()];
4641
+ }
4642
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
4643
+ ui.log.info(`Installing for all detected agents: ${detected.map((t) => t.name).join(", ")}`);
4644
+ return detected.map((target) => target.instructionsPath());
4645
+ }
4646
+ return (await promptForTargets(detected)).map((target) => target.instructionsPath());
4647
+ }
4648
+ /** Minimal numbered selection — this command is intentionally not a TUI flow. */
4649
+ async function promptForTargets(detected) {
4650
+ const ui = getUI();
4651
+ ui.log.info("Which coding agent are you using?");
4652
+ detected.forEach((target, index) => {
4653
+ ui.log.info(` ${index + 1}) ${target.name} (${target.instructionsPath()})`);
4654
+ });
4655
+ ui.log.info(` ${detected.length + 1}) All of the above`);
4656
+ const rl = readline.createInterface({
4657
+ input: process.stdin,
4658
+ output: process.stdout
4659
+ });
4660
+ try {
4661
+ for (;;) {
4662
+ const answer = (await rl.question(`Select 1-${detected.length + 1}: `)).trim();
4663
+ const choice = Number(answer);
4664
+ if (Number.isInteger(choice) && choice >= 1 && choice <= detected.length) return [detected[choice - 1]];
4665
+ if (choice === detected.length + 1) return detected;
4666
+ ui.log.warn(`Enter a number between 1 and ${detected.length + 1}.`);
4667
+ }
4668
+ } finally {
4669
+ rl.close();
4670
+ }
4671
+ }
4672
+ //#endregion
4673
+ //#region src/commands/cli/index.ts
4674
+ const cliCommand = {
4675
+ name: "cli",
4676
+ description: "PostHog CLI agent integration commands",
4677
+ children: [cliAddCommand]
4678
+ };
4679
+ //#endregion
3970
4680
  //#region bin.ts
3971
4681
  const NODE_VERSION_RANGE = ">=18.17.0";
3972
4682
  if (!satisfies(process.version, NODE_VERSION_RANGE)) {
@@ -3982,8 +4692,8 @@ function resolveInstallDir() {
3982
4692
  if (inline) return inline.slice(14);
3983
4693
  return process.env.POSTHOG_WIZARD_INSTALL_DIR ?? process.cwd();
3984
4694
  }
3985
- Wizard.use(basicIntegrationCommand).use(mcpCommand).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();
3986
4696
  //#endregion
3987
- 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 };
3988
4698
 
3989
4699
  //# sourceMappingURL=bin.js.map