@posthog/wizard 2.79.0 → 2.79.1

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 (142) hide show
  1. package/dist/PickerMenu-dG5DQCZA.js +758 -0
  2. package/dist/PickerMenu-dG5DQCZA.js.map +1 -0
  3. package/dist/{add-mcp-server-to-clients-amlJWW7W.js → add-mcp-server-to-clients-DIaTRWMY.js} +7 -18
  4. package/dist/{add-mcp-server-to-clients-amlJWW7W.js.map → add-mcp-server-to-clients-DIaTRWMY.js.map} +1 -1
  5. package/dist/add-mcp-server-to-clients-artmN7mz.js +2 -0
  6. package/dist/agent-BEjARD2a.js +6 -0
  7. package/dist/{agent-CNrEC-9v.js → agent-BWFhuLgo.js} +18 -24
  8. package/dist/{agent-CNrEC-9v.js.map → agent-BWFhuLgo.js.map} +1 -1
  9. package/dist/{agent-interface-BXvmAm4c.js → agent-interface-FkbaBJwz.js} +29 -17
  10. package/dist/agent-interface-FkbaBJwz.js.map +1 -0
  11. package/dist/{agent-prompt-loader-DiKUbhi_.js → agent-prompt-loader-D0weQ-7k.js} +5 -5
  12. package/dist/{agent-prompt-loader-DiKUbhi_.js.map → agent-prompt-loader-D0weQ-7k.js.map} +1 -1
  13. package/dist/analytics-DFtbRjYh.js +2 -0
  14. package/dist/{analytics-VRYdebXW.js → analytics-DXK1gc12.js} +4 -4
  15. package/dist/{analytics-VRYdebXW.js.map → analytics-DXK1gc12.js.map} +1 -1
  16. package/dist/api-Cm9UtKZh.js +2 -0
  17. package/dist/{api-DGuJTzjr.js → api-CrbCIQOz.js} +4 -16
  18. package/dist/{api-DGuJTzjr.js.map → api-CrbCIQOz.js.map} +1 -1
  19. package/dist/bin.js +5 -2960
  20. package/dist/bin.js.map +1 -1
  21. package/dist/{bounded-fs-Cpn129A4.js → bounded-fs-vAq-T-K7.js} +3 -3
  22. package/dist/{bounded-fs-Cpn129A4.js.map → bounded-fs-vAq-T-K7.js.map} +1 -1
  23. package/dist/{ci-install-BPA6gzEq.js → ci-install-COcV8SY2.js} +8 -7
  24. package/dist/{ci-install-BPA6gzEq.js.map → ci-install-COcV8SY2.js.map} +1 -1
  25. package/dist/{constants-CuJfQEcC.js → constants-DHCBSfnx.js} +3 -3
  26. package/dist/{constants-CuJfQEcC.js.map → constants-DHCBSfnx.js.map} +1 -1
  27. package/dist/{debug-B9k4ideV.js → debug-BuYHGr6_.js} +1 -1
  28. package/dist/{debug-Cj-8vfXY.js → debug-kAHwWGg7.js} +2 -2
  29. package/dist/{debug-Cj-8vfXY.js.map → debug-kAHwWGg7.js.map} +1 -1
  30. package/dist/{defaults-C6J7yL4O.js → defaults-a9TelDhJ.js} +1 -1
  31. package/dist/{defaults-C6J7yL4O.js.map → defaults-a9TelDhJ.js.map} +1 -1
  32. package/dist/emit-BSetXzDV.js +69 -0
  33. package/dist/emit-BSetXzDV.js.map +1 -0
  34. package/dist/{endpoints-DnIX0pZH.js → endpoints-BVMYm9F4.js} +3 -3
  35. package/dist/{endpoints-DnIX0pZH.js.map → endpoints-BVMYm9F4.js.map} +1 -1
  36. package/dist/{env-api-key-BWdKpGEr.js → env-api-key-BP-NFFXt.js} +3 -5
  37. package/dist/{env-api-key-BWdKpGEr.js.map → env-api-key-BP-NFFXt.js.map} +1 -1
  38. package/dist/env-api-key-CaWEDj_9.js +2 -0
  39. package/dist/{environment-DZYUSCpV.js → environment-BATlQxcQ.js} +5 -10
  40. package/dist/{environment-DZYUSCpV.js.map → environment-BATlQxcQ.js.map} +1 -1
  41. package/dist/environment-CwD8OPYj.js +2 -0
  42. package/dist/{errors-ddDE6MbA.js → errors-C2domSkK.js} +3 -67
  43. package/dist/errors-C2domSkK.js.map +1 -0
  44. package/dist/file-C5JMsyR1.js +2 -0
  45. package/dist/{file-dEhr-l3n.js → file-DcRKycV8.js} +3 -3
  46. package/dist/{file-dEhr-l3n.js.map → file-DcRKycV8.js.map} +1 -1
  47. package/dist/{file-watcher-CPM-Nn9L.js → file-watcher-Brq2nnHV.js} +2 -2
  48. package/dist/{file-watcher-CPM-Nn9L.js.map → file-watcher-Brq2nnHV.js.map} +1 -1
  49. package/dist/{gateway-CVU1G0H3.js → gateway-C9-GlLO9.js} +8 -7
  50. package/dist/{gateway-CVU1G0H3.js.map → gateway-C9-GlLO9.js.map} +1 -1
  51. package/dist/{headless-ui-pY2xB5ug.js → headless-ui-CqI2gXlD.js} +2 -2
  52. package/dist/{headless-ui-pY2xB5ug.js.map → headless-ui-CqI2gXlD.js.map} +1 -1
  53. package/dist/{interactive-BOGlinZe.js → interactive-B_Ti_CsS.js} +3 -3
  54. package/dist/{interactive-BOGlinZe.js.map → interactive-B_Ti_CsS.js.map} +1 -1
  55. package/dist/{logging-ui-CUxNYF2t.js → logging-ui-Dza4D0MK.js} +5 -5
  56. package/dist/{logging-ui-CUxNYF2t.js.map → logging-ui-Dza4D0MK.js.map} +1 -1
  57. package/dist/main-CgKxbwMP.js +1396 -0
  58. package/dist/main-CgKxbwMP.js.map +1 -0
  59. package/dist/{mcp-JcsBBnWv.js → mcp-DnAyW0-w.js} +4 -4
  60. package/dist/{mcp-JcsBBnWv.js.map → mcp-DnAyW0-w.js.map} +1 -1
  61. package/dist/{mcp-prompt-streaming-B-p_Pvb2.js → mcp-prompt-streaming-DDWQ-FCA.js} +6 -6
  62. package/dist/{mcp-prompt-streaming-B-p_Pvb2.js.map → mcp-prompt-streaming-DDWQ-FCA.js.map} +1 -1
  63. package/dist/{mint-failure-CZFZpHpW.js → mint-failure-u8xQKNOn.js} +23 -22
  64. package/dist/{mint-failure-CZFZpHpW.js.map → mint-failure-u8xQKNOn.js.map} +1 -1
  65. package/dist/{models-DnAY67Lg.js → models-WcwUnSKb.js} +2 -2
  66. package/dist/{models-DnAY67Lg.js.map → models-WcwUnSKb.js.map} +1 -1
  67. package/dist/{non-interactive-DR1CWXY1.js → non-interactive-DyVC1AGc.js} +4 -3
  68. package/dist/{non-interactive-DR1CWXY1.js.map → non-interactive-DyVC1AGc.js.map} +1 -1
  69. package/dist/{oauth-session-CQTFOk-I.js → oauth-session-DYM2Hrl2.js} +2 -2
  70. package/dist/{oauth-session-CQTFOk-I.js.map → oauth-session-DYM2Hrl2.js.map} +1 -1
  71. package/dist/{orchestrator-tools-CBdt-SXO.js → orchestrator-tools-DbgNBFGA.js} +3 -3
  72. package/dist/{orchestrator-tools-CBdt-SXO.js.map → orchestrator-tools-DbgNBFGA.js.map} +1 -1
  73. package/dist/{package-json-DY70kkNp.js → package-json-Atg34fa4.js} +2 -8
  74. package/dist/{package-json-DY70kkNp.js.map → package-json-Atg34fa4.js.map} +1 -1
  75. package/dist/package-json-D8Ga03tK.js +2 -0
  76. package/dist/{pi-BtqcLX3y.js → pi-8KsYsPmM.js} +17 -16
  77. package/dist/{pi-BtqcLX3y.js.map → pi-8KsYsPmM.js.map} +1 -1
  78. package/dist/{playground-Dz-ZVjBk.js → playground-BlYRBMj7.js} +11 -11
  79. package/dist/{playground-Dz-ZVjBk.js.map → playground-BlYRBMj7.js.map} +1 -1
  80. package/dist/{posthog-6Uqw_Eux.js → posthog-Bt0Aplb8.js} +3 -8
  81. package/dist/{posthog-6Uqw_Eux.js.map → posthog-Bt0Aplb8.js.map} +1 -1
  82. package/dist/posthog-CUgCEhH0.js +2 -0
  83. package/dist/{provisioning-Zf7yPcEs.js → provisioning-Bo2NRxFz.js} +5 -11
  84. package/dist/{provisioning-Zf7yPcEs.js.map → provisioning-Bo2NRxFz.js.map} +1 -1
  85. package/dist/provisioning-OGFGlzf7.js +2 -0
  86. package/dist/{queue-tools-B7_aCyd-.js → queue-tools-C8mXOh7e.js} +3 -3
  87. package/dist/{queue-tools-B7_aCyd-.js.map → queue-tools-C8mXOh7e.js.map} +1 -1
  88. package/dist/registry-C06e_x-p.js +2 -0
  89. package/dist/{registry-B6iLGorH.js → registry-CD5RQt38.js} +7 -9
  90. package/dist/{registry-B6iLGorH.js.map → registry-CD5RQt38.js.map} +1 -1
  91. package/dist/run-agent-legacy-ChacZImU.js +2 -0
  92. package/dist/{run-agent-legacy-6WcYMaR9.js → run-agent-legacy-DOlM6DQu.js} +20 -21
  93. package/dist/{run-agent-legacy-6WcYMaR9.js.map → run-agent-legacy-DOlM6DQu.js.map} +1 -1
  94. package/dist/runners-ov3EXF1n.js +834 -0
  95. package/dist/runners-ov3EXF1n.js.map +1 -0
  96. package/dist/{security-lkBwWbrK.js → security-C71pkwqH.js} +5 -5
  97. package/dist/{security-lkBwWbrK.js.map → security-C71pkwqH.js.map} +1 -1
  98. package/dist/{setup-utils-dCViCC6s.js → setup-utils-B26Lf2yF.js} +11 -21
  99. package/dist/{setup-utils-dCViCC6s.js.map → setup-utils-B26Lf2yF.js.map} +1 -1
  100. package/dist/setup-utils-CVS86NYR.js +2 -0
  101. package/dist/{start-tui-oCWMgsDP.js → start-tui-Bo2L0bar.js} +28 -26
  102. package/dist/{start-tui-oCWMgsDP.js.map → start-tui-Bo2L0bar.js.map} +1 -1
  103. package/dist/{steps-gx0Pq4I4.js → steps-BPcg3_3Q.js} +7 -7
  104. package/dist/{steps-gx0Pq4I4.js.map → steps-BPcg3_3Q.js.map} +1 -1
  105. package/dist/{store-xYy_6Ruv.js → store-BTzcvRGT.js} +11 -17
  106. package/dist/{store-xYy_6Ruv.js.map → store-BTzcvRGT.js.map} +1 -1
  107. package/dist/store-BpqySPXm.js +5 -0
  108. package/dist/{subagent-Bfy0sfhU.js → subagent-D1TrTchE.js} +3 -3
  109. package/dist/{subagent-Bfy0sfhU.js.map → subagent-D1TrTchE.js.map} +1 -1
  110. package/dist/{task-BOfw7GPc.js → task-DqRlQBfs.js} +18 -16
  111. package/dist/task-DqRlQBfs.js.map +1 -0
  112. package/dist/{task-stream-kYlm9qd0.js → task-stream-Bw6JaLCX.js} +8 -8
  113. package/dist/{task-stream-kYlm9qd0.js.map → task-stream-Bw6JaLCX.js.map} +1 -1
  114. package/dist/{tasks-Bbv_lOBG.js → tasks-ChWufsdO.js} +1 -1
  115. package/dist/{tasks-Bbv_lOBG.js.map → tasks-ChWufsdO.js.map} +1 -1
  116. package/dist/{telemetry-Bpv-Mh5B.js → telemetry-B7zJdRmA.js} +3 -3
  117. package/dist/{telemetry-Bpv-Mh5B.js.map → telemetry-B7zJdRmA.js.map} +1 -1
  118. package/dist/{terminal-D07EQ2y0.js → terminal-zgkEfoCL.js} +15 -15
  119. package/dist/{terminal-D07EQ2y0.js.map → terminal-zgkEfoCL.js.map} +1 -1
  120. package/dist/{tools-Cc41af5W.js → tools-CkJ2y9Wt.js} +9 -9
  121. package/dist/tools-CkJ2y9Wt.js.map +1 -0
  122. package/dist/{ui-CGXtC7Vn.js → ui-Q6-RvERF.js} +3 -3
  123. package/dist/{ui-CGXtC7Vn.js.map → ui-Q6-RvERF.js.map} +1 -1
  124. package/dist/{version-Cadof0Z_.js → version-CHhJjz_H.js} +2 -2
  125. package/dist/{version-Cadof0Z_.js.map → version-CHhJjz_H.js.map} +1 -1
  126. package/dist/{wizard-abort-HDbP3YcS.js → wizard-abort-BPdv07np.js} +7 -6
  127. package/dist/{wizard-abort-HDbP3YcS.js.map → wizard-abort-BPdv07np.js.map} +1 -1
  128. package/dist/wizard-abort-Br1qMDNa.js +3 -0
  129. package/dist/{wizard-ui-DoyOZxK8.js → wizard-ui-DVqrYaOM.js} +1 -1
  130. package/dist/{wizard-ui-DoyOZxK8.js.map → wizard-ui-DVqrYaOM.js.map} +1 -1
  131. package/dist/{yara-hooks-DCfROpZt.js → yara-hooks-BnxejtXK.js} +5 -5
  132. package/dist/{yara-hooks-DCfROpZt.js.map → yara-hooks-BnxejtXK.js.map} +1 -1
  133. package/dist/yara-hooks-DSuExwb4.js +2 -0
  134. package/package.json +1 -1
  135. package/dist/agent-interface-BXvmAm4c.js.map +0 -1
  136. package/dist/analytics-DxwRi2Hf.js +0 -2
  137. package/dist/errors-ddDE6MbA.js.map +0 -1
  138. package/dist/rolldown-runtime-B_-DWIq7.js +0 -15
  139. package/dist/task-BOfw7GPc.js.map +0 -1
  140. package/dist/tools-Cc41af5W.js.map +0 -1
  141. package/dist/wizard-abort-Y-Ip2K4z.js +0 -3
  142. package/dist/yara-hooks-B49RIk5d.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"mint-failure-CZFZpHpW.js","names":["buildPrompt","needsSetup","getContentBlocks","getContentBlocks","DOCS_URL","SETUP_REPORT_FILE","REPORT_FILE","DOCS_URL","getContentBlocks","getContentBlocks","fs","path","REPORT_FILE","DOCS_URL","getContentBlocks","abortUnsupportedPlatform","getTips","agentSkillContentBlocks"],"sources":["../src/programs/detection/framework.ts","../src/programs/detection/context.ts","../src/programs/detection/agentic.ts","../src/programs/detection/project-scope.ts","../src/programs/shared/health-check-step.ts","../src/programs/shared/package-scanning.ts","../src/programs/posthog-integration/detect.ts","../src/programs/posthog-integration/steps.ts","../src/ui/tui/styles.ts","../src/ui/tui/primitives/text-helpers.ts","../src/ui/tui/primitives/content-types.ts","../src/ui/tui/primitives/layout-helpers.ts","../src/ui/tui/primitives/TextBlock.tsx","../src/ui/tui/components/StatusPeekTrigger.tsx","../src/ui/tui/decks/posthog-integration/data-flow.tsx","../src/ui/tui/decks/posthog-integration/product-suite.tsx","../src/ui/tui/decks/posthog-integration/line-chart.tsx","../src/ui/tui/decks/posthog-integration/funnel.tsx","../src/ui/tui/decks/posthog-integration/index.tsx","../src/programs/posthog-integration/handoff.ts","../src/programs/posthog-integration/index.ts","../src/programs/revenue-analytics/detect.ts","../src/programs/revenue-analytics/steps.ts","../src/ui/tui/decks/agent-skill/index.tsx","../src/programs/revenue-analytics/index.ts","../src/programs/warehouse-source/steps.ts","../src/programs/warehouse-source/index.ts","../src/programs/agent-skill/steps.ts","../src/programs/agent-skill/index.ts","../src/programs/audit/detect.ts","../src/programs/audit/seed.ts","../src/programs/audit/index.ts","../src/programs/events-audit/steps.ts","../src/programs/events-audit/seed.ts","../src/programs/events-audit/index.ts","../src/programs/posthog-doctor/steps.ts","../src/programs/posthog-doctor/types.ts","../src/programs/posthog-doctor/fetch.ts","../src/programs/posthog-doctor/kind-metadata.ts","../src/programs/posthog-doctor/index.ts","../src/programs/web-analytics-doctor/detect.ts","../src/programs/web-analytics-doctor/steps.ts","../src/programs/web-analytics-doctor/index.ts","../src/programs/migration/steps.ts","../src/ui/tui/decks/migration/vendor-stack.tsx","../src/ui/tui/decks/migration/free-tier.tsx","../src/ui/tui/decks/migration/pricing-structure.tsx","../src/ui/tui/decks/migration/index.tsx","../src/programs/migration/index.ts","../src/programs/error-tracking-upload-source-maps/detect.ts","../src/programs/error-tracking-upload-source-maps/steps.ts","../src/programs/error-tracking-upload-source-maps/prompt.ts","../src/ui/tui/decks/error-tracking-upload-source-maps/index.tsx","../src/steps/install-cli-steering/index.ts","../src/programs/shared/posthog-cli-preinstall.ts","../src/programs/error-tracking-upload-source-maps/index.ts","../src/ui/tui/decks/error-tracking/index.tsx","../src/programs/replay-vision/index.ts","../src/ui/tui/decks/error-tracking/tips.ts","../src/programs/error-tracking/detect-agentic.ts","../src/programs/error-tracking/index.ts","../src/programs/self-driving/detect.ts","../src/programs/self-driving/detect-agentic.ts","../src/programs/self-driving/steps.ts","../src/programs/self-driving/prompt.ts","../src/programs/self-driving/step-keys.ts","../src/ui/tui/decks/self-driving/pricing.ts","../src/ui/tui/decks/self-driving/tips.ts","../src/ui/tui/decks/self-driving/pipeline-diagram.tsx","../src/ui/tui/decks/self-driving/index.tsx","../src/programs/self-driving/index.ts","../src/programs/mcp/index.ts","../src/programs/mcp-analytics/index.ts","../src/programs/ai-observability/index.ts","../src/programs/metrics/index.ts","../src/programs/slack/index.ts","../src/programs/program-registry.ts","../src/ui/mint-failure.ts"],"sourcesContent":["/**\n * Framework detection — identify which PostHog-supported framework\n * is present in the project directory.\n *\n * Pure function: takes an install dir, returns the detected integration\n * (or undefined). No store mutations, no UI calls.\n */\n\nimport { Integration, DETECTION_TIMEOUT_MS } from '@shared/constants';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\n\n/**\n * Loop through all registered frameworks and return the first one\n * whose `detect()` predicate matches the given directory.\n * Returns undefined if no framework is detected or detection times out.\n */\nexport async function detectFramework(\n installDir: string,\n): Promise<Integration | undefined> {\n for (const integration of Object.values(Integration)) {\n const config = FRAMEWORK_REGISTRY[integration];\n try {\n const detected = await Promise.race([\n config.detection.detect({ installDir }),\n new Promise<false>((resolve) =>\n setTimeout(() => resolve(false), DETECTION_TIMEOUT_MS),\n ),\n ]);\n if (detected) {\n return integration;\n }\n } catch {\n // Skip frameworks whose detection throws\n }\n }\n}\n","/**\n * Framework context gathering — run gatherContext and version checks\n * for a detected framework.\n *\n * Pure functions: take a framework config and options, return results.\n * No store mutations, no UI calls.\n */\n\nimport * as semver from 'semver';\nimport { DETECTION_TIMEOUT_MS } from '@shared/constants';\nimport type { FrameworkConfig } from '@programs/framework-config';\nimport type { WizardRunOptions } from '@utils/types';\n\n/**\n * Run a framework's `gatherContext()` to collect variant-specific\n * metadata (e.g., router type for Next.js, Expo vs bare for React Native).\n *\n * Returns the gathered context, or an empty object on failure/timeout.\n */\nexport async function gatherFrameworkContext(\n config: FrameworkConfig,\n options: WizardRunOptions,\n): Promise<Record<string, unknown>> {\n if (!config.metadata.gatherContext) return {};\n\n try {\n return await Promise.race([\n config.metadata.gatherContext(options),\n new Promise<Record<string, never>>((resolve) =>\n setTimeout(() => resolve({}), DETECTION_TIMEOUT_MS),\n ),\n ]);\n } catch {\n return {};\n }\n}\n\nexport interface VersionCheckResult {\n /** Whether the installed version is supported */\n supported:\n | true\n | {\n current: string;\n minimum: string;\n docsUrl: string;\n };\n}\n\n/**\n * Check whether the installed framework version meets the minimum requirement.\n *\n * Returns `{ supported: true }` if the version is fine (or no check is needed).\n * Returns the version details if unsupported.\n */\nexport async function checkFrameworkVersion(\n config: FrameworkConfig,\n options: WizardRunOptions,\n): Promise<VersionCheckResult> {\n if (\n !config.detection.minimumVersion ||\n !config.detection.getInstalledVersion\n ) {\n return { supported: true };\n }\n\n const version = await config.detection.getInstalledVersion(options);\n if (!version) return { supported: true };\n\n const coerced = semver.coerce(version);\n if (coerced && semver.lt(coerced, config.detection.minimumVersion)) {\n return {\n supported: {\n current: version,\n minimum: config.detection.minimumVersion,\n docsUrl:\n config.metadata.unsupportedVersionDocsUrl ?? config.metadata.docsUrl,\n },\n };\n }\n\n return { supported: true };\n}\n","/**\n * Agentic project detection.\n *\n * A reusable detection tool that drives an agent over the repo: it finds\n * project roots, resolves monorepo/workspace markers, reads package manifests,\n * classifies each project against a caller-supplied set of targets, and reports\n * which projects already have a PostHog SDK installed.\n *\n * Product-knowledge-free by design — the caller passes the targets to classify\n * into (e.g. source-map skill variants) and maps the result back. Sits next to\n * the other detection tools (framework, features, package-manager) so it's\n * discoverable. Runs AFTER auth: it goes through the same `runAgent` every\n * program uses, which needs credentials.\n */\n\nimport {\n buildRunTags,\n runAgent,\n RunOutcome,\n resolveScanBindings,\n} from '@agent';\nimport type {\n AgentProgress,\n AgentRunDefinition,\n SwitchboardCtx,\n RunConfig,\n RunInput,\n} from '@agent/types';\nimport { isAbsolute, resolve, sep } from 'path';\nimport { z } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { ErrorCodes } from '@shared/errors';\nimport { detectNodePackageManagers } from './package-manager.js';\nimport {\n AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS,\n AGENTIC_DETECTION_RETRY_TIMEOUT_MS,\n CallType,\n getSkillsBaseUrl,\n POSTHOG_DOCS_URL,\n} from '@shared/constants';\nimport { analytics } from '@utils/analytics';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { getUI } from '@ui';\nimport { createUiReducer } from '@ui/agent-progress';\n\n/** A category the agent classifies each project into (id the agent returns). */\nexport type DetectTarget = { id: string; name: string };\n\n/** One project the agent found in the repo. */\nexport type AgenticProject = {\n /** Path relative to the working directory (\".\" for the repo root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A target id when the project matches one of the supplied targets, else null. */\n targetId: string | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** True on the one project picked as the main user-facing app; only present when the scan set `recommend`. */\n recommended?: boolean;\n};\n\nexport type AgenticDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: AgenticProject[];\n};\n\nexport class AgenticDetectionTimeoutError extends Error {\n constructor(attempt: number, timeoutMs: number) {\n super(\n `Project scan attempt ${attempt} timed out after ${timeoutMs / 1000}s`,\n );\n this.name = 'AgenticDetectionTimeoutError';\n }\n}\n\n/** Streaming progress callback — one short activity line per agent step. */\nexport type DetectEvent = (line: string) => void;\n\n/**\n * Every project-manifest / workspace-marker filename the wizard's frameworks\n * are identified by. One brace-expansion Glob over all of these locates every\n * project root in the repo in a single call, regardless of language.\n *\n * Counterpart: POSTHOG_MANIFESTS in @programs/self-driving/detect (SDK\n * grep); keep the two in sync.\n */\nexport const PROJECT_MANIFESTS: readonly string[] = [\n // JS/TS + workspace markers\n 'package.json',\n 'pnpm-workspace.yaml',\n 'turbo.json',\n 'nx.json',\n 'lerna.json',\n // Python\n 'requirements.txt',\n 'pyproject.toml',\n 'setup.py',\n 'Pipfile',\n 'manage.py',\n // Ruby / PHP\n 'Gemfile',\n 'composer.json',\n // Java\n 'pom.xml',\n // Rust\n 'Cargo.toml',\n // Elixir\n 'mix.exs',\n // Go\n 'go.mod',\n // .NET: no framework targets yet, but found so\n // an existing PostHog SDK is reported (feeds self-driving's \"continue\" path).\n '*.csproj',\n // Mobile / native\n 'Package.swift',\n 'Podfile',\n 'project.yml',\n 'project.pbxproj',\n 'pubspec.yaml',\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n // Gradle version catalog (holds dependency coordinates).\n 'gradle/libs.versions.toml',\n];\n\n/** The single brace-expansion glob covering every supported manifest. */\nexport function manifestGlob(): string {\n return `**/{${PROJECT_MANIFESTS.join(',')}}`;\n}\n\nexport type AgenticDetectOptions = {\n /**\n * Categories to classify each project into. Order matters: list targets by\n * priority, most specific first. When a project could match more than one,\n * the agent keeps the earliest — so the caller encodes precedence purely\n * through ordering (e.g. a bundler target before a generic framework target).\n */\n targets: readonly DetectTarget[];\n /** The program this scan bills to. Required, not optional: the scan drives a\n * real agent through the gateway, and a caller that forgets leaves that\n * spend unattributed. */\n programId: string;\n /** One short clause describing what the scan is for (frames the prompt). */\n purpose?: string;\n /** Ask the agent to label exactly one project `recommended` (the main client app). Off by default. */\n recommend?: boolean;\n /**\n * Target ids whose technologies co-occur in one manifest (e.g. the JS\n * family). Among these, the enumeration's priority winner overrides the\n * model's pick; elsewhere a valid pick always wins — see coerceAgenticReport.\n */\n rerankIds?: readonly string[];\n /** Streaming activity callback for the UI. */\n onEvent?: DetectEvent;\n};\n\nfunction buildPrompt(\n cwd: string,\n targets: readonly DetectTarget[],\n purpose: string,\n recommend: boolean,\n): string {\n const targetList = targets.map((t) => `- ${t.id} → ${t.name}`).join('\\n');\n // matchingTargets precedes targetId: the model enumerates before it picks.\n const projectShape = `{\"path\":string,\"framework\":string,\"matchingTargets\":string[],\"targetId\":string|null,\"hasPostHog\":boolean,\"evidence\":string${\n recommend ? ',\"recommended\":boolean' : ''\n }}`;\n return [\n `You are scanning a code repository to ${purpose}. Be fast and mechanical — do not over-explore. Keep any reasoning to one short sentence, then run the tool calls and output the JSON.`,\n '',\n `Working directory: ${cwd}`,\n '',\n 'A \"project\" is a directory containing one or more of the manifest files below. Find projects by their manifests, NOT by walking directories — never report a directory that has no manifest.',\n '',\n 'Do exactly this:',\n `1. Run Glob ONCE with this pattern to find every project manifest in the repo in a single call: \"${manifestGlob()}\". Discard any result whose path contains node_modules/, dist/, build/, .next/, out/, coverage/, vendor/, .venv/, site-packages/, target/, Pods/, Carthage/, or DerivedData/. Group the remaining results by directory — each directory is one project. Three exceptions to \"directory = project\": a project.pbxproj lives inside a \"<Name>.xcodeproj/\" wrapper, so the project root is the PARENT of that .xcodeproj directory; a project.yml at a directory root is an XcodeGen-generated Xcode app rooted at that directory; a gradle/libs.versions.toml is a version catalog belonging to the gradle project rooted at the PARENT of that gradle/ directory (read it alongside the build.gradle when deciding hasPostHog), never its own project.`,\n '2. Decide repoType: \"monorepo\" if the root package.json has a \"workspaces\" field OR a pnpm-workspace.yaml / turbo.json / nx.json / lerna.json was found at the root, else \"single\".',\n `3. For EACH project directory, ONE AT A TIME: Read its manifest(s) ONCE, decide the fields below, then IMMEDIATELY — before reading any other project — write that project's verdict as one JSON line of shape ${projectShape}. Never write a verdict from memory of an earlier Read; the manifest you just read is the only source. Decide from its dependency lists:`,\n ' - the human-readable framework name (e.g. \"Next.js\", \"Django\", \"Rails\"),',\n ' - matchingTargets: EVERY target id from the list below whose technology appears in THIS manifest, in the priority order of the list. An id belongs here only if you can point at the dependency or setting in the manifest you just read — never carry one over from another project,',\n ' - targetId: the FIRST entry of matchingTargets (the list is ordered by priority, most specific first). null when matchingTargets is empty,',\n ` - hasPostHog: true if any dependency is a PostHog SDK. This includes: a name containing \"posthog\" in any ecosystem (e.g. posthog-js, posthog-node, @posthog/*, posthog for pip/gem/hex, a com.posthog:* gradle/maven coordinate, a PostHog NuGet PackageReference); an SPM package named \"PostHog\" or a repositoryURL of github.com/PostHog/posthog-ios (in Package.swift or a .pbxproj); or a \"pod 'PostHog'\" line in a Podfile. Else false.`,\n ' - evidence: the manifest fact that decided targetId, with the file it came from (e.g. \"rollup in devDependencies of backend/package.json\"). When targetId is null, name the closest fact you saw. One short clause, quoted from the manifest you just read.',\n ' Do NOT read any file other than these manifests.',\n ...(recommend\n ? [\n '4. Pick exactly ONE project as recommended — the main user-facing client application. Prefer a frontend web app or a mobile app over backend servers/APIs, libraries, CLIs, tooling, and example/demo/docs apps. If several client apps exist, pick the primary production app; if no client app exists, pick the primary application project.',\n ]\n : []),\n '',\n 'Target ids (id → name), in priority order — most specific first; if several match a project, keep the one listed earliest:',\n targetList,\n '',\n 'Output requirements:',\n '- After the last verdict line, respond with ONLY a single JSON object assembling the verdict lines you wrote, copied VERBATIM — same path, framework, targetId and evidence per project. No prose, no markdown code fences.',\n `- Shape: {\"repoType\":\"monorepo\"|\"single\",\"projects\":[${projectShape}]}`,\n '- \"path\" is the project directory relative to the working directory; use \".\" for the repo root.',\n '- \"targetId\" MUST be exactly one of the target ids above when it matches; if several match, use the one listed earliest; otherwise null.',\n '- Include projects whose stack matches no target too (targetId: null).',\n ...(recommend\n ? [\n '- Exactly one project has \"recommended\": true; every other project has \"recommended\": false.',\n ]\n : []),\n '- If there are no manifests at all, return {\"repoType\":\"single\",\"projects\":[]}.',\n ].join('\\n');\n}\n\n/**\n * Build the detection report from the agent's output, or null when it holds\n * neither a project verdict nor a `projects` array. Exported for testing.\n *\n * The verdict lines are far more reliable than the model's final assembly\n * (prose, pretty-printing, per-object fences), so every parseable line\n * contributes: objects with a `path` merge by path (last wins), and a\n * one-line assembly's `projects` merge the same way. `repoType` is\n * approximated from the count — it only feeds telemetry and display.\n */\nexport function deriveReportJson(text: string): unknown | null {\n const byPath = new Map<string, Record<string, unknown>>();\n let sawReport = false;\n for (const line of text.split('\\n')) {\n const start = line.indexOf('{');\n const end = line.lastIndexOf('}');\n if (start === -1 || end <= start) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line.slice(start, end + 1));\n } catch {\n continue; // not JSON — prose, shape echoes, pretty-printed fragments\n }\n const obj = (parsed ?? {}) as Record<string, unknown>;\n sawReport ||= Array.isArray(obj.projects);\n const candidates = Array.isArray(obj.projects) ? obj.projects : [obj];\n for (const candidate of candidates) {\n const p = (candidate ?? {}) as Record<string, unknown>;\n if (typeof p.path === 'string') byPath.set(p.path, p);\n }\n }\n if (byPath.size === 0 && !sawReport) return null;\n const projects = [...byPath.values()];\n return { repoType: projects.length > 1 ? 'monorepo' : 'single', projects };\n}\n\n/**\n * Clamp an agent-reported project path to a safe repo-relative dir. The path\n * is LLM output: absolute paths or `..` segments could steer later phases\n * (e.g. integrate-run's targetDir) outside the repo, so they clamp to '.'.\n */\nfunction coercePath(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '.';\n if (isAbsolute(raw) || raw.split(/[\\\\/]/).includes('..')) return '.';\n return raw;\n}\n\n/** Resolve an agent-reported path to an absolute dir clamped to the root — the caller-side counterpart of coercePath. */\nexport function resolveProjectDir(installDir: string, rel: unknown): string {\n if (typeof rel !== 'string' || rel === '.') return installDir;\n const root = resolve(installDir);\n const dir = resolve(root, rel);\n return dir === root || dir.startsWith(root + sep) ? dir : root;\n}\n\n/**\n * Validate the agent's raw JSON into a report, clamping each targetId to the\n * supplied set and each path to a repo-relative dir. Exported for testing.\n * `recommended` is stripped unless requested; at most one (the first) survives.\n */\nexport function coerceAgenticReport(\n parsed: unknown,\n validTargetIds: readonly string[],\n options?: { recommend?: boolean; rerankIds?: readonly string[] },\n): AgenticDetectionReport {\n const recommend = options?.recommend === true;\n const rerankIds = options?.rerankIds ?? [];\n const obj = (parsed ?? {}) as Record<string, unknown>;\n const repoType = obj.repoType === 'monorepo' ? 'monorepo' : 'single';\n const rawProjects = Array.isArray(obj.projects) ? obj.projects : [];\n let recommendedSeen = false;\n const projects: AgenticProject[] = rawProjects.map((raw) => {\n const p = (raw ?? {}) as Record<string, unknown>;\n // Trust the model's pick; an invalid one falls back to the enumeration's\n // highest-priority member (validTargetIds IS the priority order). The\n // winner overrides a valid pick only when both sit in rerankIds — stacks\n // that co-occur in one manifest, where a misordered enumeration is the\n // common miss. Elsewhere enumerations are padded across exclusive stacks\n // (a Flutter app listing react-native) and must not beat a correct pick.\n const pick =\n typeof p.targetId === 'string' && validTargetIds.includes(p.targetId)\n ? p.targetId\n : null;\n const enumerated = Array.isArray(p.matchingTargets)\n ? validTargetIds.find((id) =>\n (p.matchingTargets as unknown[]).includes(id),\n ) ?? null\n : null;\n const targetId =\n pick === null\n ? enumerated\n : enumerated !== null &&\n rerankIds.includes(enumerated) &&\n rerankIds.includes(pick)\n ? enumerated\n : pick;\n const recommended = recommend && !recommendedSeen && p.recommended === true;\n recommendedSeen ||= recommended;\n return {\n path: coercePath(p.path),\n framework: typeof p.framework === 'string' ? p.framework : 'Unknown',\n targetId,\n hasPostHog: p.hasPostHog === true,\n ...(recommend ? { recommended } : {}),\n };\n });\n return { repoType, projects };\n}\n\nfunction detectionReportSchema(recommend: boolean) {\n return z\n .object({\n repoType: z.enum(['monorepo', 'single']),\n projects: z.array(\n z\n .object({\n path: z.string(),\n framework: z.string(),\n matchingTargets: z.array(z.string()),\n targetId: z.string().nullable(),\n hasPostHog: z.boolean(),\n evidence: z.string(),\n ...(recommend ? { recommended: z.boolean() } : {}),\n })\n .strict(),\n ),\n })\n .strict();\n}\n\n/** Read-only scan with a typed result and transcript recovery. */\nfunction detectionRunDefinition(prompt: string): AgentRunDefinition {\n return {\n integrationLabel: 'agentic-detect',\n readOnly: true,\n prompt: () => prompt,\n collectTranscript: true,\n requestRemark: false,\n detectPackageManager: detectNodePackageManagers,\n spinnerMessage: 'Scanning the repo...',\n successMessage: 'Detection complete',\n errorMessage: 'Detection failed',\n estimatedDurationMinutes: 1,\n reportFile: '',\n docsUrl: POSTHOG_DOCS_URL,\n };\n}\n\n/** What the UI saw before `runAgent`: no run lifecycle, spinner, outro, or setup logs below warn. */\nfunction reachesUi(event: AgentProgress): boolean {\n switch (event.kind) {\n case 'lifecycle':\n case 'completion':\n case 'spinner':\n return false;\n case 'log':\n return event.level === 'warn' || event.level === 'error';\n default:\n return true;\n }\n}\n\n/** Scan through `runAgent` with the bound triage model, then the SDK fallback. */\nexport async function detectProjectsWithAgent(\n session: WizardSession,\n options: AgenticDetectOptions,\n): Promise<AgenticDetectionReport> {\n if (!session.credentials) {\n throw new Error('Detection requires authenticated credentials.');\n }\n const {\n targets,\n programId,\n purpose = 'set up a PostHog integration',\n recommend = false,\n rerankIds,\n onEvent,\n } = options;\n\n // Built here: the scan runs before the program's own run tags exist.\n const wizardMetadata = {\n ...buildRunTags({\n programId,\n integration: 'agentic-detect',\n runId: analytics.runId,\n build: analytics.build,\n }),\n call_type: CallType.detection,\n };\n const wizardFlags = await analytics.getAllFlagsForWizard();\n const wizardFlagPayloads = analytics.getWizardFlagPayloads();\n const switchboard: SwitchboardCtx = {\n program: programId,\n composed: true,\n flags: wizardFlags,\n flagPayloads: wizardFlagPayloads,\n cliHarness: session.harness,\n };\n const bindings = resolveScanBindings(switchboard);\n const reportSchema = detectionReportSchema(recommend);\n const schema = zodToJsonSchema(reportSchema);\n const config: RunConfig = {\n programId,\n run: detectionRunDefinition(\n buildPrompt(session.installDir, targets, purpose, recommend),\n ),\n composed: true,\n binding: bindings[0],\n switchboard,\n skillsBaseUrl: getSkillsBaseUrl(),\n wizardFlags,\n wizardFlagPayloads,\n wizardMetadata,\n // The scan's scans count toward the program run's report.\n scanReport: 'defer',\n };\n const input: RunInput = {\n installDir: session.installDir,\n credentials: session.credentials,\n project: null,\n apiUser: null,\n // No benchmark pipeline and no AIO capture: the scan never had either.\n flags: {\n ci: session.ci,\n signup: session.signup,\n debug: session.debug,\n e2eAsk: false,\n localMcp: session.localMcp,\n captureAio: false,\n benchmark: false,\n yaraReport: session.yaraReport,\n },\n host: { projectId: session.projectId, apiKey: session.apiKey },\n };\n const reduceUi = createUiReducer(getUI());\n const forward = (event: AgentProgress): void => {\n if (event.kind === 'activity') onEvent?.(event.line);\n if (reachesUi(event)) reduceUi(event);\n };\n\n for (let attempt = 0; attempt < 2; attempt++) {\n const timeoutMs =\n attempt === 0\n ? AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS\n : AGENTIC_DETECTION_RETRY_TIMEOUT_MS;\n const result = await runAgent(\n {\n ...config,\n binding: bindings[attempt],\n run: { ...config.run, structured: { schema, timeoutMs } },\n },\n input,\n { onProgress: forward },\n );\n\n if (\n result.outcome !== RunOutcome.Success &&\n result.failure.code === ErrorCodes.AgenticDetectionTimeout\n ) {\n if (attempt === 0) {\n onEvent?.('Project scan timed out; retrying...');\n continue;\n }\n throw new AgenticDetectionTimeoutError(attempt + 1, timeoutMs);\n }\n if (result.outcome !== RunOutcome.Success) {\n if (\n result.failure.code === ErrorCodes.AgentInvalidStructuredOutput &&\n attempt === 0\n ) {\n onEvent?.('Project scan returned invalid output; retrying...');\n continue;\n }\n throw result.failure.error ?? new Error(result.failure.message);\n }\n if (result.structuredOutput !== undefined) {\n const structured = reportSchema.safeParse(result.structuredOutput);\n if (structured.success) {\n return coerceAgenticReport(\n structured.data,\n targets.map((t) => t.id),\n { recommend, rerankIds },\n );\n }\n // A typed report that misses the schema is invalid output, not a transcript.\n if (attempt === 0) {\n onEvent?.('Project scan returned invalid output; retrying...');\n }\n continue;\n }\n\n // Transcript first, final message last — its verdicts win path conflicts.\n const output = result.snapshot.transcriptTail ?? '';\n const derived = deriveReportJson(output);\n if (derived !== null) {\n return coerceAgenticReport(\n derived,\n targets.map((t) => t.id),\n { recommend, rerankIds },\n );\n }\n if (attempt === 0) onEvent?.('Retrying project scan...');\n }\n throw new Error('Agent did not return a JSON object after retry');\n}\n","/** Non-interactive project scoping — scan the repo agentically and pick which project the run integrates. */\n\nimport {\n detectProjectsWithAgent,\n AgenticDetectionTimeoutError,\n resolveProjectDir,\n type AgenticDetectionReport,\n type AgenticProject,\n type DetectEvent,\n type DetectTarget,\n} from './agentic.js';\nimport { authenticate } from '@programs/authenticate';\nimport type { CiRunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport {\n Integration,\n WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY,\n} from '@shared/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { analytics } from '@utils/analytics';\nimport { logToFile } from '@utils/debug';\n\n/** Integration framework targets for the agentic detector (id → display name). */\nconst INTEGRATION_TARGETS: DetectTarget[] = Object.entries(\n FRAMEWORK_REGISTRY,\n).map(([id, config]) => ({ id, name: config.metadata.name }));\n\nconst INTEGRATION_IDS = new Set<string>(Object.values(Integration));\n\n/** A scanned project matched to a wizard framework. Each program classifies it with its own rule. */\nexport type IntegrationCandidate = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** The wizard framework the project matches, else null. */\n integration: Integration | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** The scan's pick for the main app. Always false unless the scan set `recommend`. */\n recommended: boolean;\n};\n\n/** Match each project of an integration scan to a wizard framework. */\nexport function toIntegrationCandidates(\n report: AgenticDetectionReport,\n): IntegrationCandidate[] {\n return report.projects.map((p) => ({\n path: p.path,\n framework: p.framework,\n integration:\n p.targetId && INTEGRATION_IDS.has(p.targetId)\n ? (p.targetId as Integration)\n : null,\n hasPostHog: p.hasPostHog,\n recommended: p.recommended === true,\n }));\n}\n\n/** Run the agentic detector for the wizard's integration frameworks — the single home of targets + purpose. */\nexport async function detectIntegrationProjects(\n session: WizardSession,\n options: {\n /** Program the scan bills to. Required so no caller can go unattributed. */\n programId: string;\n recommend?: boolean;\n onEvent?: DetectEvent;\n },\n): Promise<AgenticDetectionReport> {\n // Spread first so the targets and purpose this function owns always win.\n return detectProjectsWithAgent(session, {\n ...options,\n targets: INTEGRATION_TARGETS,\n purpose: 'set up a PostHog SDK integration',\n });\n}\n\n/** Pick the recommended-if-supported project (even with PostHog — the main app wins), else the first supported PostHog-free one. */\nexport function chooseIntegrationProject(\n projects: AgenticProject[],\n): AgenticProject | undefined {\n return (\n projects.find((p) => p.recommended === true && p.targetId != null) ??\n projects.find((p) => p.targetId != null && !p.hasPostHog)\n );\n}\n\n/** Every run fires exactly one `wizard: agentic detection` event with one of these outcomes — no untracked exits. */\nexport type AgenticDetectionOutcome =\n | 'flag-off'\n | 'error'\n | 'timeout'\n | 'no-project'\n | 'recommended'\n | 'first-instrumentable';\n\nfunction captureOutcome(\n outcome: AgenticDetectionOutcome,\n properties: Record<string, unknown> = {},\n): void {\n analytics.wizardCapture('agentic detection', { outcome, ...properties });\n}\n\n/** Flag-gated non-interactive monorepo phase: scan, auto-choose the recommended project, re-point session.installDir; every failure leaves the session untouched. */\nexport async function scopeInstallDirToProject(\n session: WizardSession,\n runner: CiRunnerContext,\n): Promise<void> {\n // Idempotent early auth: the detector needs credentials and the flag must evaluate as the logged-in user.\n await authenticate(session, 'posthog-integration');\n const flags = await analytics.getAllFlagsForWizard();\n if (flags[WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY] !== 'true') {\n // A failed flag fetch surfaces as an empty map, so flag-off also covers \"flags unavailable\".\n captureOutcome('flag-off');\n return;\n }\n\n runner.log.info('Scanning the repo for projects...');\n const startedAt = Date.now();\n let report: AgenticDetectionReport;\n try {\n report = await detectIntegrationProjects(session, {\n // Literal, like the `authenticate` call above: importing the program\n // registry here would cycle.\n programId: 'posthog-integration',\n recommend: true,\n onEvent: (line) => logToFile('[agentic detect]', line),\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (error instanceof AgenticDetectionTimeoutError) {\n captureOutcome('timeout', { duration_ms: Date.now() - startedAt });\n runner.log.warn(\n `${error.message}; continuing with the install dir as-is.`,\n );\n return;\n }\n analytics.captureException(error, { step: 'agentic_detection' });\n captureOutcome('error', {\n duration_ms: Date.now() - startedAt,\n error_message: error.message,\n });\n runner.log.warn(\n `Project scan failed (${error.message}); continuing with the install dir as-is.`,\n );\n return;\n }\n\n const { projects } = report;\n const recommended = projects.find((p) => p.recommended === true);\n // The event carries at most this many projects; project_count is the true total.\n const MAX_PROJECTS_CAPTURED = 25;\n const scanProperties = {\n duration_ms: Date.now() - startedAt,\n repo_type: report.repoType,\n project_count: projects.length,\n supported_count: projects.filter((p) => p.targetId != null).length,\n has_recommendation: recommended !== undefined,\n recommended_path: recommended?.path ?? null,\n projects: projects.slice(0, MAX_PROJECTS_CAPTURED),\n };\n\n const project = chooseIntegrationProject(projects);\n if (!project) {\n captureOutcome('no-project', scanProperties);\n runner.log.info(\n 'The scan found no supported project; continuing with the install dir as-is.',\n );\n return;\n }\n\n session.installDir = resolveProjectDir(session.installDir, project.path);\n captureOutcome(project.recommended ? 'recommended' : 'first-instrumentable', {\n ...scanProperties,\n chosen_framework: project.targetId,\n chosen_path: project.path,\n });\n runner.log.info(`Continuing with ${project.path} (${project.framework}).`);\n}\n","/**\n * Shared health-check step used by every program that runs an agent.\n *\n * Renders the HealthCheckScreen between intro and auth, kicks off the\n * readiness probe in onInit, and gates the screen on either a clean\n * readiness result or an explicit user dismissal of the outage.\n *\n * Programs without this step that hit a blocking outage gridlock the\n * router: agent-runner calls wizardAbort, which awaits outroDismissed,\n * but the router can't advance past the still-incomplete auth step to\n * render the OutroScreen.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport {\n evaluateWizardReadiness,\n WizardReadiness,\n SIGNUP_WIZARD_READINESS_CONFIG,\n getBlockingServiceKeys,\n} from '@shared/health-checks/readiness';\nimport { logToFile } from '@utils/debug';\n\nexport function healthCheckReady(session: WizardSession): boolean {\n if (!session.readinessResult) return false;\n\n if (session.signup) {\n const hardBlocking = getBlockingServiceKeys(\n session.readinessResult.health,\n SIGNUP_WIZARD_READINESS_CONFIG,\n );\n const defaultBlocking = getBlockingServiceKeys(\n session.readinessResult.health,\n );\n if (hardBlocking.length === 0 && defaultBlocking.length === 0) return true;\n return session.outageDismissed;\n }\n\n if (session.readinessResult.decision === WizardReadiness.No) {\n return session.outageDismissed;\n }\n return true;\n}\n\nexport const HEALTH_CHECK_STEP: ProgramStep = {\n id: 'health-check',\n label: 'Health check',\n screenId: 'health-check',\n gate: healthCheckReady,\n onInit: (ctx) => {\n evaluateWizardReadiness()\n .then((readiness) => {\n logToFile(\n `[health-checks] TUI pre-flight complete: decision=${readiness.decision}`,\n );\n ctx.setReadinessResult(readiness);\n })\n .catch((err) => {\n logToFile('[health-checks] TUI pre-flight failed:', err);\n ctx.setReadinessResult({\n decision: WizardReadiness.Yes,\n health: {} as never,\n reasons: [],\n });\n });\n },\n};\n","import { relative } from 'path';\nimport { safeReadFile, walkProjectFiles } from '@utils/bounded-fs';\n\nexport const POSTHOG_SDKS = new Set([\n 'posthog-js',\n 'posthog-node',\n 'posthog-react-native',\n 'posthog-android',\n 'posthog-ios',\n]);\n\nexport const STRIPE_SDKS = new Set([\n 'stripe',\n '@stripe/stripe-js',\n '@stripe/react-stripe-js',\n]);\n\nexport interface PackageMatch {\n /** Path to the package.json relative to installDir */\n path: string;\n posthogSdks: string[];\n stripeSdks: string[];\n}\n\n// At most this many matches retained, regardless of monorepo package count.\nconst MAX_PACKAGE_MATCHES = 500;\n\n/**\n * Find all package.json files under installDir via the shared bounded walk\n * (depth cap, ignored dirs, symlink-loop protection, per-dir entry cap).\n * Returns matches with detected SDKs.\n */\nexport function findPackageJsons(\n installDir: string,\n maxDepth = 3,\n): PackageMatch[] {\n const matches: PackageMatch[] = [];\n\n walkProjectFiles(\n installDir,\n (name, fullPath) => {\n if (name !== 'package.json' || matches.length >= MAX_PACKAGE_MATCHES) {\n return;\n }\n const content = safeReadFile(fullPath);\n if (content === null) return;\n try {\n const pkg = JSON.parse(content) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const depNames = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n const posthogSdks = depNames.filter((d) => POSTHOG_SDKS.has(d));\n const stripeSdks = depNames.filter((d) => STRIPE_SDKS.has(d));\n matches.push({\n path: relative(installDir, fullPath) || 'package.json',\n posthogSdks,\n stripeSdks,\n });\n } catch {\n // Skip malformed package.json\n }\n },\n maxDepth,\n );\n\n return matches;\n}\n","/**\n * Core integration detection step.\n *\n * Runs framework detection, context gathering, version checking,\n * and feature discovery. Writes results to the store via the\n * ProgramReadyContext so the IntroScreen can display them.\n *\n * This is the same work that bin.ts $0 handler does inline —\n * extracted here so the `integrate` subcommand can reuse it.\n */\n\nimport type { ProgramReadyContext } from '@programs/program-step';\nimport {\n mayReportScanResults,\n ScanConsent,\n type WizardSession,\n} from '@lib/wizard-session';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport {\n detectFramework,\n discoverFeatures,\n gatherFrameworkContext,\n checkFrameworkVersion,\n} from '@programs/detection/index';\nimport { analytics } from '@utils/analytics';\nimport { detectWarehouseSources } from '@programs/warehouse-sources/detect';\nimport {\n DETECTED_WAREHOUSE_SOURCES_KEY,\n getDetectedWarehouseSources,\n} from '@programs/warehouse-source/detect';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\nimport { stampAiSdkDetected } from '@programs/posthog-integration/ai-sdk-stamp';\n\n// Session-free, so runProgram can stamp without loading the session.\nexport {\n stampAiSdkDetected,\n type AiSdkStampEvidence,\n} from '@programs/posthog-integration/ai-sdk-stamp';\n\nexport async function detectPostHogIntegration(\n ctx: ProgramReadyContext,\n): Promise<void> {\n const session = ctx.session;\n const installDir = session.installDir;\n\n const detectedIntegration = await detectFramework(installDir);\n\n if (detectedIntegration) {\n const config = FRAMEWORK_REGISTRY[detectedIntegration];\n\n const sessionOptions = {\n installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n };\n\n // Gather framework-specific context (e.g., router type)\n const context = await gatherFrameworkContext(config, sessionOptions);\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n ctx.setFrameworkContext(key, value);\n }\n }\n\n ctx.setFrameworkConfig(detectedIntegration, config);\n // Must go through the store setter: setFrameworkConfig (above) calls\n // nanostore setKey, which replaces the session with a shallow copy —\n // a direct `session.skillId = ...` here would write to the stale\n // pre-copy object and the live session would never see it.\n ctx.setSkillId(detectedIntegration);\n\n if (!session.detectedFrameworkLabel) {\n ctx.setDetectedFramework(config.metadata.name);\n }\n\n // Version check\n const versionResult = await checkFrameworkVersion(config, sessionOptions);\n if (versionResult.supported !== true) {\n ctx.setUnsupportedVersion(versionResult.supported);\n }\n }\n\n // Feature discovery\n for (const feature of discoverFeatures(installDir)) {\n ctx.addDiscoveredFeature(feature);\n }\n\n detectWarehouseSourcesForSuggestion(ctx, installDir);\n detectExistingPostHog(ctx, installDir);\n\n ctx.setDetectionComplete();\n}\n\n/**\n * How the scan went, for the reporter to read later. Three states, and the\n * absent one carries the most weight:\n *\n * absent this program never scanned. Only `posthog-integration` runs the\n * scan below, but every program's intro screen resolves consent\n * through the same store method, so the reporter is reached on runs\n * where no scan ever happened.\n * 'failed' the scan threw. A zero-count row would read as \"scanned, found\n * nothing\", which is a different fact.\n * 'ok' the scan ran. Zero sources is a real result and gets reported.\n */\nconst WAREHOUSE_SCAN_STATE_KEY = 'warehouseScanState';\ntype WarehouseScanState = 'ok' | 'failed';\n\n/**\n * Scan for data warehouse source signals (Postgres, Stripe, Hubspot, …) and,\n * when found, stash them for a `wizard warehouse` suggestion on the outro.\n * Runs unconditionally during `detect`, before the intro screen has asked\n * about sharing. Reporting what this finds is a separate, later concern.\n * See `reportWarehouseSourcesDetected` below.\n *\n * Deliberately a suggestion, not an inline agent run: a second credential-\n * collecting agent run before the outro could `process.exit()` on any of its\n * failure paths and cost the user the success outro on a run where PostHog\n * installed fine.\n *\n * Calls `detectWarehouseSources` (a pure scan) rather than the warehouse\n * program's `detectWarehousePrerequisites`, which writes a `detectError` when\n * it finds nothing — that's right for `wizard warehouse` but here would show a\n * spurious error and trip the CI prerequisites abort. No sources is a no-op.\n *\n * Best-effort: never let a scan failure break the wizard flow.\n */\nfunction detectWarehouseSourcesForSuggestion(\n ctx: ProgramReadyContext,\n installDir: string,\n): void {\n try {\n const sources = detectWarehouseSources(installDir);\n // Before the empty-list return: a scan that found nothing still ran, and\n // those rows are the denominator.\n ctx.setFrameworkContext(WAREHOUSE_SCAN_STATE_KEY, 'ok');\n if (sources.length === 0) return;\n ctx.setFrameworkContext(DETECTED_WAREHOUSE_SOURCES_KEY, sources);\n } catch (error) {\n ctx.setFrameworkContext(WAREHOUSE_SCAN_STATE_KEY, 'failed');\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectWarehouseSourcesForSuggestion' },\n );\n }\n}\n\n/**\n * Fires the org stamp once per session, right after `authenticate()` succeeds\n * — never from the consent path, since consent on the intro screen resolves\n * before login and `session.apiUser` is unset there. Called right after\n * `authenticate()` from run-wizard.ts's auth step and from bootstrap.ts,\n * whichever completes it first for a given program; a no-op on every call\n * after that. In the `--ci` path, project-scope.ts authenticates first as a\n * prerequisite (no evidence gathered yet, so nothing would stamp there\n * anyway) and this only ever runs from the later, idempotent bootstrap.ts\n * call — still correctly finding no evidence, since CI skips the detect step.\n */\nexport function maybeStampAiSdkDetected(session: WizardSession): void {\n // Direct mutation, not a store setter: unlike `warehouseSourcesReported`\n // (latched only from TUI-only consent screens), this runs from\n // `authenticate()`, which also fires in `--ci` mode, where the session is a\n // plain object with no nanostore — see run-non-interactive.ts. A setter\n // routed through WizardStore would silently never latch there.\n //\n // Depends on consent resolving before auth, same as every program's step\n // list orders 'intro' before 'auth' today; a program that reversed that\n // would latch this before consent exists and never stamp even once granted.\n if (session.aiSdkStampReported) return;\n session.aiSdkStampReported = true;\n stampAiSdkDetected({\n apiUser: session.apiUser,\n discoveredFeatures: session.discoveredFeatures,\n warehouseSources: getDetectedWarehouseSources(session),\n mayReportScanResults: mayReportScanResults(session),\n });\n}\n\n/**\n * The single place scan results become telemetry. Called from\n * `WizardStore.completeSetup()`, the point consent becomes final — the privacy\n * panel's choice is reversible until then, so nothing may report earlier.\n * The org stamp is a separate concern: see `maybeStampAiSdkDetected`, which\n * runs post-auth rather than at consent resolution.\n *\n * Returns true when consent has resolved and the caller should latch\n * `warehouseSourcesReported`, which is not the same as \"this sent something\":\n * a decline, a failed scan, and a program that never scanned all resolve\n * without sending.\n */\nexport function reportWarehouseSourcesDetected(\n session: WizardSession,\n): boolean {\n if (session.warehouseSourcesReported) return false;\n // 'undecided' means come back later, not no.\n if (session.scanConsent === ScanConsent.Undecided) return false;\n\n if (mayReportScanResults(session)) {\n const sources = getDetectedWarehouseSources(session);\n\n // Only an 'ok' scan has something to say. Absent means this program never\n // scanned, and reporting a zero count there would invent a scan that never\n // ran; 'failed' already fired captureException.\n const scanState = session.frameworkContext[WAREHOUSE_SCAN_STATE_KEY] as\n | WarehouseScanState\n | undefined;\n if (scanState !== 'ok') return true;\n\n // Captured on every run, including the empty case. The denominator is\n // the whole point: without the zero rows, \"20% of runs have a Postgres\"\n // is unanswerable.\n analytics.wizardCapture('warehouse sources detected', {\n warehouse_source_count: sources.length,\n warehouse_source_kinds: sources.map((s) => s.kind),\n warehouse_source_modes: sources.map((s) => s.mode),\n });\n\n if (sources.length > 0) {\n // Tag subsequent events too, so any downstream funnel can slice on what the\n // project had available without re-joining to the event above.\n analytics.setTag(\n 'warehouse_source_kinds',\n sources.map((s) => s.kind).join(','),\n );\n analytics.setTag('warehouse_source_count', sources.length);\n }\n }\n // Nothing on 'declined': saved insights read every row of this event as a\n // scan that ran. Decline rate comes from 'intro menu selected' instead.\n\n return true;\n}\n\n/** Dependency-level signal, not a verified install. A failed scan reports false. */\nexport function detectExistingPostHog(\n ctx: Pick<ProgramReadyContext, 'setPosthogSdkDetected'>,\n installDir: string,\n): void {\n try {\n const pkgJsons = findPackageJsons(installDir);\n ctx.setPosthogSdkDetected(pkgJsons.some((p) => p.posthogSdks.length > 0));\n } catch (error) {\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectExistingPosthog' },\n );\n ctx.setPosthogSdkDetected(false);\n }\n}\n","/**\n * PostHog integration program — the default wizard flow.\n *\n * Steps define their own gate predicates and onInit callbacks.\n * The store derives gate promises and fires init work from these\n * definitions — no hardcoded per-flow logic in the store.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { detectPostHogIntegration } from './detect.js';\n\nfunction needsSetup(session: WizardSession): boolean {\n const config = session.frameworkConfig;\n if (!config?.metadata.setup?.questions) return false;\n\n return config.metadata.setup.questions.some(\n (q: { key: string }) => !(q.key in session.frameworkContext),\n );\n}\n\nexport const POSTHOG_INTEGRATION_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting framework',\n // Headless step: no screen. onReady fires after bin.ts assigns the\n // session — runs framework detection, context gathering, version\n // check, and feature discovery. Results are written to the store\n // for the IntroScreen to render.\n onReady: (ctx) => detectPostHogIntegration(ctx),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'setup',\n label: 'Setup',\n screenId: 'setup',\n show: needsSetup,\n isComplete: (session) => !needsSetup(session),\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Integration',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'mcp',\n label: 'MCP servers',\n screenId: 'mcp',\n isComplete: (session) => session.mcpComplete,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n // Always shown — the user declines via Skip/esc, never bypassed.\n isComplete: (session) => session.slackStepDismissed,\n },\n {\n id: 'keep-skills',\n label: 'Keep Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Shared style constants for TUI layout primitives.\n */\n\nexport enum HAlign {\n Left = 'flex-start',\n Center = 'center',\n Right = 'flex-end',\n}\n\nexport enum VAlign {\n Top = 'flex-start',\n Center = 'center',\n Bottom = 'flex-end',\n}\n\nexport const Colors = {\n primary: 'cyan',\n accent: '#DC9300',\n titleColor: '#3D2800',\n success: 'green',\n error: 'red',\n muted: 'gray',\n} as const;\n\nexport const Icons = {\n diamond: '\\u25C6',\n diamondOpen: '\\u25C7',\n check: '\\u2714',\n warning: '\\u26A0',\n squareFilled: '\\u25FC',\n squareOpen: '\\u25FB',\n triangleRight: '\\u25B6',\n triangleSmallRight: '\\u25B8',\n bullet: '\\u2022',\n} as const;\n","/**\n * Text-splitting helpers for sentence boundary detection.\n * Used by TextBlock for animation pauses at punctuation.\n */\n\n/** Split text into sentences (keeps the delimiter attached) */\nexport function splitSentences(text: string): string[] {\n const parts: string[] = [];\n const re = /[^.!?]*[.!?]+\\s*/g;\n let match: RegExpExecArray | null;\n let lastIndex = 0;\n while ((match = re.exec(text)) !== null) {\n parts.push(match[0]);\n lastIndex = re.lastIndex;\n }\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n return parts;\n}\n\n/** Build a set of character indices where sentences end (for typewriter pause) */\nexport function sentenceEndChars(text: string): Set<number> {\n const ends = new Set<number>();\n const sentences = splitSentences(text);\n let pos = 0;\n for (const s of sentences) {\n pos += s.length;\n ends.add(pos - 1);\n }\n return ends;\n}\n\n/** Build a set of word indices where sentences end (for word-by-word pause) */\nexport function sentenceEndWords(text: string): Set<number> {\n const ends = new Set<number>();\n const sentences = splitSentences(text);\n let wordCount = 0;\n for (const s of sentences) {\n const words = s.trim().split(/\\s+/).filter(Boolean);\n wordCount += words.length;\n ends.add(wordCount - 1);\n }\n return ends;\n}\n","/**\n * Content block types and type guards.\n *\n * Extracted from ContentSequencer so that pure-logic modules (like\n * layout-helpers) can import them without pulling in Ink/React.\n */\n\nimport type { ReactNode } from 'react';\nimport type { TextRevealMode } from './TextBlock.js';\n\n/** Object form — string or ReactNode content with per-block overrides. */\nexport interface ContentObjectBlock {\n content: string | ReactNode;\n mode?: TextRevealMode;\n animationInterval?: number;\n sentenceInterval?: number;\n pause?: number;\n persist?: boolean;\n /**\n * When the sequencer advances past this block, should it render dim?\n * Defaults to `true` — the standard \"completed step\" treatment. Set to\n * `false` to keep the block at full opacity even after it's complete\n * (useful for headings or greetings that should stay readable while\n * later blocks animate in).\n *\n * Only meaningful for blocks whose `content` is a string (TextBlock\n * renders the dim treatment). NodeBlocks render whatever JSX you pass\n * verbatim regardless of completion state.\n */\n dimWhenComplete?: boolean;\n}\n\n/** Lines block — reveals ReactNode lines one at a time. */\nexport interface ContentLinesBlock {\n type: 'lines';\n lines: ReactNode[];\n interval?: number;\n pause?: number;\n}\n\n/** Clear block — page break that hides all prior blocks. */\nexport interface ContentClearBlock {\n type: 'clear';\n pause?: number;\n}\n\n/** A content block in the sequence. Bare strings are sugar for { content: '...' }. */\nexport type ContentBlock =\n | string\n | ContentObjectBlock\n | ContentLinesBlock\n | ContentClearBlock;\n\n/** Type guard for lines blocks. */\nexport function isLinesBlock(block: ContentBlock): block is ContentLinesBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'lines';\n}\n\n/** Type guard for clear blocks. */\nexport function isClearBlock(block: ContentBlock): block is ContentClearBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'clear';\n}\n\n/** Type guard for object blocks (text or node content). */\nexport function isObjectBlock(\n block: ContentBlock,\n): block is ContentObjectBlock {\n return typeof block !== 'string' && !('type' in block);\n}\n","/**\n * Layout helpers — pure functions for height estimation and viewport eviction.\n *\n * These are the core of the responsive content system. They estimate how many\n * terminal rows a content block will occupy and determine which blocks fit\n * within a given height budget.\n */\n\nimport type { ContentBlock } from './content-types.js';\nimport { isLinesBlock, isClearBlock, isObjectBlock } from './content-types.js';\n\n/**\n * Estimate the number of terminal rows a content block will occupy,\n * including 1 row of marginBottom.\n */\nexport function estimateBlockHeight(\n block: ContentBlock,\n availableWidth: number,\n): number {\n if (typeof block === 'string') {\n return wordWrap(block, availableWidth).length + 1; // +1 for marginBottom\n }\n\n if (isClearBlock(block)) return 0;\n\n if (isLinesBlock(block)) {\n return block.lines.length + 1;\n }\n\n if (isObjectBlock(block)) {\n if (typeof block.content === 'string') {\n return wordWrap(block.content, availableWidth).length + 1;\n }\n return 4; // conservative fixed estimate for ReactNode\n }\n\n return 1;\n}\n\n/**\n * Given all blocks, the active index, available width, and a row budget,\n * return [startIdx, endIdx] — the range of blocks to render.\n *\n * Always includes activeIdx. Walks backward to include as many completed\n * blocks as fit within maxHeight.\n */\nexport function computeVisibleRange(\n blocks: ContentBlock[],\n activeIdx: number,\n availableWidth: number,\n maxHeight: number,\n): [number, number] {\n // Reserve a 2-row buffer so resize-induced estimate drift doesn't\n // cause overflow=\"hidden\" to clip the margin between blocks.\n const budget = Math.max(4, maxHeight - 2);\n\n let totalHeight = estimateBlockHeight(blocks[activeIdx], availableWidth);\n let start = activeIdx;\n\n for (let i = activeIdx - 1; i >= 0; i--) {\n // Clear blocks act as a hard boundary — don't show anything before them\n if (isClearBlock(blocks[i])) break;\n const h = estimateBlockHeight(blocks[i], availableWidth);\n if (totalHeight + h > budget) break;\n totalHeight += h;\n start = i;\n }\n\n return [start, activeIdx];\n}\n\n/**\n * Word-wrap text at clean word boundaries. Always returns pre-wrapped text\n * joined with \\n — this avoids Ink's native wrap which can leave leading\n * spaces on continuation lines.\n *\n * Uses a 1-char safety margin so slight width estimate mismatches don't\n * cause Ink to re-wrap our already-wrapped lines.\n */\nexport function wordWrap(text: string, availableWidth: number): string[] {\n const safeWidth = Math.max(10, availableWidth - 1);\n const words = text.split(/\\s+/);\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n if (\n currentLine.length + word.length + 1 > safeWidth &&\n currentLine.length > 0\n ) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine += (currentLine.length > 0 ? ' ' : '') + word;\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n return lines;\n}\n\n/**\n * Word-wrap text and return only the last `maxRows` lines.\n * Used for intra-block truncation when a single text block exceeds the viewport.\n * Also used for normal rendering to avoid Ink's leading-space wrap artifacts.\n */\nexport function wrapAndTruncate(\n text: string,\n availableWidth: number,\n maxRows: number,\n): string {\n const lines = wordWrap(text, availableWidth);\n\n if (lines.length <= maxRows) {\n return lines.join('\\n');\n }\n\n return lines.slice(-maxRows).join('\\n');\n}\n","/**\n * TextBlock — Animates a single string paragraph.\n *\n * Self-contained: owns its own animIdx and timer.\n * Calls onComplete() when the animation finishes.\n *\n * Five animation modes:\n * 1. Typewriter — character-by-character reveal\n * 2. Word by word — each word appears in order\n * 3. Sentence by sentence — sentences appear one at a time\n * 4. Paragraph fade — paragraph appears at full opacity immediately\n * 5. Sentence fade — paragraph dim, sentences light up in order\n */\n\nimport { Text } from 'ink';\nimport { useState, useEffect, useRef, useMemo, type ReactNode } from 'react';\nimport { Colors } from '@ui/tui/styles';\nimport {\n splitSentences,\n sentenceEndChars,\n sentenceEndWords,\n} from './text-helpers.js';\nimport { wrapAndTruncate } from './layout-helpers.js';\n\nexport enum TextRevealMode {\n Typewriter = 0,\n WordByWord = 1,\n SentenceBySentence = 2,\n ParagraphFade = 3,\n SentenceFade = 4,\n}\n\nexport const TEXT_REVEAL_MODE_LABELS = [\n 'Typewriter',\n 'Word by word',\n 'Sentence by sentence',\n 'Paragraph fade',\n 'Sentence fade',\n];\n\nexport const TEXT_REVEAL_MODE_COUNT = 5;\n\n/** Default interval per mode (ms) */\nexport const TEXT_REVEAL_MODE_DEFAULTS: Record<TextRevealMode, number> = {\n [TextRevealMode.WordByWord]: 240,\n [TextRevealMode.Typewriter]: 32,\n [TextRevealMode.SentenceBySentence]: 1800,\n [TextRevealMode.ParagraphFade]: 4800,\n [TextRevealMode.SentenceFade]: 2400,\n};\n\ninterface TextBlockProps {\n text: string;\n active: boolean;\n completed: boolean;\n onComplete: () => void;\n mode: TextRevealMode;\n bullet?: ReactNode;\n animationInterval?: number;\n sentenceInterval?: number;\n /** Max rows this block may occupy. When exceeded, top lines are truncated. */\n maxHeight?: number;\n /** Available text width in columns (for truncation wrapping). */\n availableWidth?: number;\n /**\n * When `completed === true`, render the text dim. Defaults to `true` —\n * the standard \"completed step\" treatment. Pass `false` to keep the\n * text at full opacity even after the sequencer has moved on.\n */\n dimWhenComplete?: boolean;\n}\n\nexport const TextBlock = ({\n text,\n active,\n completed,\n onComplete,\n mode,\n bullet,\n animationInterval,\n sentenceInterval = 1600,\n maxHeight,\n availableWidth,\n dimWhenComplete = true,\n}: TextBlockProps) => {\n const speed = animationInterval ?? TEXT_REVEAL_MODE_DEFAULTS[mode];\n\n const [animIdx, setAnimIdx] = useState(\n mode === TextRevealMode.SentenceFade ? 1 : 0,\n );\n\n // Reset synchronously during render to avoid a one-frame flash\n const resetRef = useRef(0);\n const prevMode = useRef(mode);\n if (prevMode.current !== mode) {\n prevMode.current = mode;\n resetRef.current += 1;\n setAnimIdx(mode === TextRevealMode.SentenceFade ? 1 : 0);\n }\n\n const words = text.split(/\\s+/);\n const sentences = splitSentences(text);\n\n const sentenceCharEnds = useMemo(() => sentenceEndChars(text), [text]);\n const sentenceWordEnds = useMemo(() => sentenceEndWords(text), [text]);\n\n const isDone =\n mode === TextRevealMode.Typewriter\n ? animIdx >= text.length\n : mode === TextRevealMode.ParagraphFade\n ? true\n : mode === TextRevealMode.WordByWord\n ? animIdx >= words.length\n : mode === TextRevealMode.SentenceFade ||\n mode === TextRevealMode.SentenceBySentence\n ? animIdx >= sentences.length\n : true;\n\n // Fire onComplete when done\n useEffect(() => {\n if (isDone && active) onComplete();\n }, [isDone, active, onComplete]);\n\n // Animate: single effect for all tick-based modes\n useEffect(() => {\n if (!active || mode === TextRevealMode.ParagraphFade || isDone) return;\n const token = resetRef.current;\n\n const isFirstTick = animIdx === 0;\n\n let delay = isFirstTick ? 0 : speed;\n if (\n !isFirstTick &&\n mode === TextRevealMode.Typewriter &&\n animIdx > 0 &&\n sentenceCharEnds.has(animIdx - 1)\n ) {\n delay = sentenceInterval;\n } else if (\n !isFirstTick &&\n mode === TextRevealMode.WordByWord &&\n animIdx > 0 &&\n sentenceWordEnds.has(animIdx - 1)\n ) {\n delay = sentenceInterval;\n }\n\n const timer = setTimeout(() => {\n if (token !== resetRef.current) return;\n setAnimIdx((c) => c + 1);\n }, delay);\n return () => clearTimeout(timer);\n }, [\n active,\n mode,\n animIdx,\n isDone,\n speed,\n sentenceInterval,\n sentenceCharEnds,\n sentenceWordEnds,\n ]);\n\n // Pre-wrap text ourselves to avoid Ink's native wrap leaving leading spaces\n // on continuation lines. When maxHeight is set, also truncates to last N rows.\n const wrap = (visibleText: string): string => {\n if (availableWidth == null) return visibleText;\n if (maxHeight == null) {\n return wrapAndTruncate(visibleText, availableWidth, Infinity);\n }\n return wrapAndTruncate(visibleText, availableWidth, maxHeight);\n };\n\n // Completed: dimmed by default, but can be overridden per-block.\n if (completed) {\n return (\n <Text dimColor={dimWhenComplete}>\n {bullet}\n {wrap(text)}\n </Text>\n );\n }\n\n // Active: mode-specific rendering\n if (mode === TextRevealMode.Typewriter) {\n const revealed = text.slice(0, animIdx);\n const atSentenceEnd = /[.!?]\\s*$/.test(revealed);\n const display = atSentenceEnd ? revealed.trimEnd() : revealed;\n return (\n <Text>\n {bullet}\n {wrap(display)}\n <Text color={Colors.muted}>{'\\u258C'}</Text>\n </Text>\n );\n }\n\n if (mode === TextRevealMode.WordByWord) {\n const visible = words.slice(0, animIdx).join(' ');\n return (\n <Text>\n {bullet}\n {wrap(visible)}\n </Text>\n );\n }\n\n if (mode === TextRevealMode.ParagraphFade) {\n return (\n <Text>\n {bullet}\n {wrap(text)}\n </Text>\n );\n }\n\n if (mode === TextRevealMode.SentenceBySentence) {\n const visible = sentences.slice(0, animIdx).join('');\n return (\n <Text>\n {bullet}\n {wrap(visible)}\n </Text>\n );\n }\n\n // SentenceFade\n return (\n <Text>\n {bullet}\n {sentences.map((s, si) => (\n <Text key={si} dimColor={si >= animIdx}>\n {s}\n </Text>\n ))}\n </Text>\n );\n};\n","/**\n * StatusPeekTrigger — Fires the status-bar expansion once, renders a hint.\n *\n * Module-level `peekedOnce` guards against re-mounts (resize, tab switch)\n * so the peek only happens a single time per process.\n */\n\nimport { Text } from 'ink';\nimport { useEffect } from 'react';\nimport type { WizardStore } from '@ui/tui/store';\n\nlet peekedOnce = false;\n\ninterface StatusPeekTriggerProps {\n store?: WizardStore;\n /** How long the status bar stays expanded, in ms. */\n duration?: number;\n}\n\nexport const StatusPeekTrigger = ({\n store,\n duration = 10000,\n}: StatusPeekTriggerProps) => {\n useEffect(() => {\n if (peekedOnce) return;\n peekedOnce = true;\n store?.setStatusExpanded(true);\n // No cleanup — the store call is safe after unmount and the component\n // may be evicted before the timer fires (non-persist NodeBlock).\n setTimeout(() => {\n store?.setStatusExpanded(false);\n }, duration);\n }, [store, duration]);\n\n return <Text>You can view the Wizard&apos;s status below.</Text>;\n};\n","/**\n * POSTHOG_DATA_FLOW — ASCII diagram of the PostHog event ingestion flow.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const POSTHOG_DATA_FLOW: ContentBlock = {\n type: 'lines',\n interval: 500,\n pause: 8000,\n // Box is 30 chars wide between │ borders.\n // Labels: 1-char indent. Arrows: \" ↓ \" (5). Sub-items: \" │ \" (7).\n lines: [\n <Text color=\"gray\">{' ┌──────────────────────────────┐'}</Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color=\"cyan\">\n Your App\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text>posthog.capture()</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>custom events</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>custom properties</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>person profiles</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text dimColor>groups</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color={Colors.accent}>\n PostHog SDK\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text>HTTP</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color={Colors.accent}>\n PostHog Cloud\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text>query + visualize</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color=\"green\">\n Dashboards & Insights\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text color=\"gray\">{' └──────────────────────────────┘'}</Text>,\n ],\n};\n","/**\n * PRODUCT_SUITE_BLOCK — Two-column listing of PostHog products.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PRODUCT_SUITE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 1000,\n pause: 15000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Error Tracking'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Web Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Session Replay'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Feature Flags '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Data Pipelines'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Experiments '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Data Warehouse'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'AI Observability '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Surveys'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Workflows '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Logs'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Tours '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Support'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Revenue Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Endpoints'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Customer Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'PostHog Desktop'}\n </Text>,\n ],\n};\n","/**\n * LINE_CHART_BLOCK — ASCII trends chart illustrating an insight.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const LINE_CHART_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 300,\n pause: 6000,\n lines: [\n <Text bold>{' Trends · user signups (monthly)'}</Text>,\n <Text> </Text>,\n // 10k\n <Text>\n <Text color=\"gray\">{' 10k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──'}</Text>\n <Text dimColor>{' 9,575'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭╯'}</Text>\n </Text>,\n // 7.5k\n <Text>\n <Text color=\"gray\">{' 7.5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭─╯'}</Text>\n </Text>,\n // 5k\n <Text>\n <Text color=\"gray\">{' 5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭─╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──╯'}</Text>\n </Text>,\n // 2.5k\n <Text>\n <Text color=\"gray\">{' 2.5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭───╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──────╯'}</Text>\n </Text>,\n // 0\n <Text>\n <Text color=\"gray\">{' 0 ┤'}</Text>\n <Text color=\"cyan\">{'──────╯'}</Text>\n </Text>,\n // X-axis\n <Text color=\"gray\">{' └┬─────┬─────┬─────┬─────┬──'}</Text>,\n <Text dimColor>{' May Aug Nov Feb May'}</Text>,\n ],\n};\n","/**\n * FUNNEL_BLOCK — ASCII funnel illustrating step conversion.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const FUNNEL_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 200,\n pause: 8000,\n lines: [\n <Text bold>{' Funnel · ride conversion'}</Text>,\n <Text> </Text>,\n // Step 1\n <Text>\n {' '}\n <Text bold>1</Text>\n {' app_launched'}\n {' '}\n <Text bold color=\"green\">\n 100.00%\n </Text>\n </Text>,\n <Text color=\"cyan\">{' ██████████████████████████████'}</Text>,\n <Text dimColor>{' → 1,200 users'}</Text>,\n <Text> </Text>,\n // Step 2\n <Text>\n {' '}\n <Text bold>2</Text>\n {' ride_requested'}\n {' '}\n <Text dimColor>{'avg 2m 30s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 72.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'██████████████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 864 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 336 (28%)'}</Text>\n </Text>,\n <Text> </Text>,\n // Step 3\n <Text>\n {' '}\n <Text bold>3</Text>\n {' ride_accepted'}\n {' '}\n <Text dimColor>{'avg 5m 12s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 51.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'██████████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 612 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 252 (29%)'}</Text>\n </Text>,\n <Text> </Text>,\n // Step 4\n <Text>\n {' '}\n <Text bold>4</Text>\n {' ride_started'}\n {' '}\n <Text dimColor>{'avg 1m 45s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 38.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'█████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 456 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 156 (25%)'}</Text>\n </Text>,\n ],\n};\n","/**\n * Integration learn-deck — the narrative script played while the agent\n * runs the PostHog integration flow. Weaves typewriter lines, pauses,\n * `clear` markers, and the diagram blocks into one sequence.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { POSTHOG_DATA_FLOW } from './data-flow.js';\nimport { PRODUCT_SUITE_BLOCK } from './product-suite.js';\nimport { LINE_CHART_BLOCK } from './line-chart.js';\nimport { FUNNEL_BLOCK } from './funnel.js';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => [\n {\n // Name comes from the login profile; falls back when absent (CI keys).\n content: store?.session.apiUser?.first_name\n ? `Welcome, ${store.session.apiUser.first_name}.`\n : 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content: 'It handles the entire PostHog setup process on your behalf.',\n pause: 5000,\n },\n\n {\n content:\n \"As we speak, it's building a plan to set up PostHog in your project.\",\n pause: 6000,\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n content: 'It takes about eight minutes.',\n pause: 2000,\n },\n\n {\n content: 'So grab some coffee ☕️.',\n pause: 2000,\n },\n\n {\n content: 'Or stick around and learn about PostHog.',\n pause: 5000,\n },\n\n { type: 'clear', pause: 3000 },\n\n {\n content: 'Events are the foundation of the PostHog platform.',\n pause: 4000,\n },\n\n {\n content:\n 'Every time an action is performed in your codebase — like button clicks, function calls, or thrown errors — we can capture an event.',\n pause: 6000,\n },\n\n {\n content: 'Events are sent to PostHog and joined with other product data.',\n pause: 6000,\n },\n\n { type: 'clear', pause: 1000 },\n\n { content: \"Here's the flow.\", pause: 1000 },\n\n POSTHOG_DATA_FLOW,\n\n { type: 'clear', pause: 2000 },\n\n {\n content:\n 'With enough event data, you can answer powerful questions about your product.',\n pause: 4000,\n },\n\n { content: 'And create insights.', pause: 4000 },\n\n { type: 'clear', pause: 500 },\n\n { content: 'Like trends to measure growth.', pause: 2500 },\n\n LINE_CHART_BLOCK,\n\n { type: 'clear', pause: 500 },\n\n { content: 'Or funnels to reveal bottlenecks.', pause: 2500 },\n\n FUNNEL_BLOCK,\n\n { type: 'clear', pause: 1000 },\n\n {\n content: 'Use those signals to decide what to build next.',\n pause: 4000,\n },\n\n { content: 'PostHog has all the dev tools you need.', pause: 3000 },\n\n PRODUCT_SUITE_BLOCK,\n\n { type: 'clear', pause: 2000 },\n\n {\n content: 'The wizard is wiring up your PostHog stack with...',\n pause: 2500,\n },\n\n {\n type: 'lines',\n interval: 1000,\n pause: 12000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Web Analytics'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Error Tracking '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Session Replay'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'AI Observability '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Logs'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Data Warehouse sources'}\n </Text>,\n ],\n },\n];\n","/**\n * The one-line prompt the operator pastes into their own coding agent after a\n * run: open the setup-report notebook, work its \"Verify before merging\"\n * checklist. Surfaced via `OutroData.handoffPrompt` (printed to scrollback on\n * exit); only exists when the run captured a notebook URL, since no report\n * file is written. The wording demands investigate-then-approval, and stops\n * there — the operator's own agent governs how changes land. Background:\n * https://github.com/PostHog/wizard/issues/447\n */\nexport function buildCodingAgentPrompt(notebookUrl: string): string {\n return `Open ${notebookUrl} (the PostHog setup report) and work through its \"Verify before merging\" checklist: investigate each item, then list the changes you'd make and get my approval before applying any of them.`;\n}\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { runProgramAgent } from '@programs/run-agent-legacy';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AgentSignals, shouldDisableAsk, WIZARD_TOOL_NAMES } from '@agent';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { mayReportScanResults, OutroKind, RunPhase } from '@lib/wizard-session';\nimport {\n DEFAULT_PACKAGE_INSTALLATION,\n SPINNER_MESSAGE,\n} from '@programs/framework-config';\nimport { tryGetPackageJson, isUsingTypeScript } from '@utils/setup-utils';\nimport { analytics } from '@utils/analytics';\nimport {\n detectFramework,\n gatherFrameworkContext,\n} from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport type { CiRunnerContext, RunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\nimport {\n WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY,\n WIZARD_INTERACTION_EVENT_NAME,\n} from '@shared/constants';\nimport { requestDeepLink } from '@utils/provisioning';\nimport { openTrackedLink, withUtm } from '@utils/links';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { getDetectedWarehouseSources } from '@programs/warehouse-source/detect';\nimport { POSTHOG_INTEGRATION_PROGRAM } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/posthog-integration/index.js';\nimport { buildCodingAgentPrompt } from './handoff.js';\nimport { EVENT_PLAN_FILE } from './constants.js';\n\nconst DASHBOARD_DEEP_LINK_KEY = 'dashboardDeepLink';\n\nconst WAREHOUSE_SOURCES_DOCS_URL =\n 'https://posthog.com/docs/data-warehouse/sources';\n\n/** Task type of the seeded step below, matched against the drain's result. */\nconst WAREHOUSE_SEED_TASK_TYPE = 'warehouse';\n\nfunction resolveContinueUrl(\n sess: WizardSession,\n host: HostResolution,\n deepLink: unknown,\n): string | undefined {\n if (!sess.signup) return undefined;\n if (typeof deepLink === 'string' && deepLink) return deepLink;\n return withUtm(`${host.appHost}/products?source=wizard`, 'outro-continue');\n}\n\n/** Sources listed with their own link before the outro falls back to a summary line. */\nconst WAREHOUSE_LINK_LIMIT = 3;\n\n/**\n * How many detected sources the seeded step is allowed to collect credentials\n * for.\n *\n * The step's cost to the user is one credential prompt per source, and the\n * wizard knows the count at seed time: detection keys off dependency and\n * `.env` key names, so an ordinary app can match a dozen sources it has no\n * intention of importing. Offering all of them asks a person to agree, up\n * front, to an interrogation whose length they cannot see — and the runs\n * offered more than a handful answer none of the prompts at all, while the\n * short ones are where every connected source comes from.\n *\n * So the step takes the first few and the outro carries the rest as links,\n * which is the same trade {@link WAREHOUSE_LINK_LIMIT} already makes for a\n * list too long to read. Ordering is the registry's, which groups databases\n * ahead of the API-key SaaS that inflates the tail.\n */\nconst WAREHOUSE_SEED_LIMIT = 3;\n\n/**\n * The app's new-source page, pre-selected to one source kind.\n *\n * `kind` is matched case-insensitively against the connector list, and a kind\n * the app cannot resolve lands on the source catalog rather than erroring. So a\n * source we detect but the app has not shipped a connector for still takes the\n * user somewhere useful.\n */\nfunction warehouseSourceUrl(\n host: HostResolution,\n projectId: number | string,\n kind: string,\n): string {\n const path = `${\n host.appHost\n }/project/${projectId}/data-warehouse/new-source?kind=${encodeURIComponent(\n kind,\n )}`;\n return withUtm(path, 'outro-warehouse');\n}\n\n/**\n * Outro suggestion for data sources found in the project but not connected.\n *\n * A pointer at the app, not an inline flow. Connecting a source needs\n * interactive credential collection, and chaining that as a second agent run\n * before the outro would let any of its terminal failure paths `process.exit()`\n * — costing the user the success outro and the post-outro MCP / Slack steps on\n * a run where PostHog installed fine.\n *\n * Each source gets its own pre-filled link, because the alternative we shipped\n * first — naming the `wizard warehouse` command — asks the user to start a\n * second CLI run before they can connect anything. A link opens the form for\n * that one source. The command line stays for anyone who wants every source in\n * one pass, and it is the only route offered once the list is too long to read.\n *\n * Returns undefined when nothing was detected, so the outro is unchanged for\n * projects with no connectable source — and when the run's own warehouse step\n * connected everything it was given, where every bullet here would ask the\n * user to redo work the wizard just did and send them at a new-source form\n * that would collide with the source already created. A completed step is\n * only ever given the first {@link WAREHOUSE_SEED_LIMIT} sources, so anything\n * past that is still unconnected and still belongs here.\n */\nfunction buildWarehouseNextSteps(\n sess: WizardSession,\n host: HostResolution,\n projectId: number | string,\n completedSeededTypes: readonly string[],\n): { heading: string; items: string[] } | undefined {\n const detected = getDetectedWarehouseSources(sess);\n const sources = completedSeededTypes.includes(WAREHOUSE_SEED_TASK_TYPE)\n ? detected.slice(WAREHOUSE_SEED_LIMIT)\n : detected;\n if (sources.length === 0) return undefined;\n\n const listed = sources.slice(0, WAREHOUSE_LINK_LIMIT);\n const items = listed.map(\n (s) => `Connect ${s.label}: ${warehouseSourceUrl(host, projectId, s.kind)}`,\n );\n\n const remaining = sources.length - listed.length;\n if (remaining > 0) {\n items.push(`And ${remaining} more we found in this project.`);\n }\n items.push('Connect them all at once with: npx @posthog/wizard warehouse');\n\n return { heading: 'Query your other data in PostHog:', items };\n}\n\n/**\n * Prompt fragment asking the agent to note the detected data sources in the\n * setup report's checklist.\n *\n * Empty string when nothing was detected, so the prompt is byte-identical to\n * today for projects with no connectable source. Best-effort by nature — the\n * agent may word it differently or skip it. That is acceptable here precisely\n * because it is a note in a report: the outro `nextSteps` bullet carries the\n * same information deterministically, so nothing is lost if the agent drops it.\n */\nfunction warehouseReportInstruction(sess: WizardSession): string {\n const sources = getDetectedWarehouseSources(sess);\n if (sources.length === 0) return '';\n\n const labels = sources.map((s) => s.label).join(', ');\n return `Finally: this project also contains data sources PostHog can import (${labels}). In the setup report's \"Verify before merging\" checklist, add one item noting these were found and that \\`npx @posthog/wizard warehouse\\` will connect them to PostHog's data warehouse. Do not attempt to set them up yourself in this run.`;\n}\n\n/**\n * The orchestrator task that connects the data sources detection found in the\n * project — the one step of the flow that stops to ask the user for\n * credentials. Queued here rather than by the planner: whether it belongs in\n * the run is a fact about the project the wizard already scanned, so a model\n * can neither invent it nor forget it.\n *\n * It runs at the end of the queue, not the start. Where exactly is the agent\n * prompt's business (`dependsOn` in the warehouse agent's frontmatter, resolved\n * by `seeded-deps.ts` once the planner has run) — this file only decides whether\n * the task belongs in the run at all.\n *\n * Empty when nothing was detected, and in CI, signup, and any other run where\n * `wizard_ask` is disabled — a credential prompt nobody can answer would burn\n * the task's whole timeout and then fail the run. Capped at\n * {@link WAREHOUSE_SEED_LIMIT} sources, with the rest handed over as outro\n * links by {@link buildWarehouseNextSteps}.\n */\nconst warehouseSeedTasks: NonNullable<ProgramConfig['seedTasks']> = (sess) => {\n if (shouldDisableAsk(sess)) return [];\n const sources = getDetectedWarehouseSources(sess);\n if (sources.length === 0) return [];\n\n const offered = sources.slice(0, WAREHOUSE_SEED_LIMIT);\n const deferred = sources.length - offered.length;\n\n // The task is queued either way. A decline withholds reporting, not the\n // feature. See the matching gate in reportWarehouseSourcesDetected.\n if (mayReportScanResults(sess)) {\n analytics.wizardCapture('orchestrator warehouse task queued', {\n warehouse_source_count: sources.length,\n warehouse_source_kinds: sources.map((s) => s.kind),\n // What the step was actually given, which is what its outcome is a rate\n // of. The two counts above stay the detection totals they have always\n // been, so the detection trend reads across this change.\n warehouse_offered_count: offered.length,\n });\n }\n return [\n {\n type: WAREHOUSE_SEED_TASK_TYPE,\n inputs: {\n sources: offered.map((s) => ({\n kind: s.kind,\n label: s.label,\n mode: s.mode,\n matchedSignal: s.matchedSignal,\n })),\n },\n notice: {\n title: 'Connect your data sources',\n // Two moments, and the copy has to name both. The answer is given here,\n // at the start of the run. The credential questions arrive at the end of\n // it, minutes later. So this must not read as \"expect a prompt any\n // moment now\", and equally must not read as \"walk away for the run\".\n body: [\n 'We detected some warehouse sources we can connect to enrich your PostHog data. Answer now, and we connect them at the end of the run, after your code changes. We will ask you for the credentials at that point, and beep when we do.',\n ...(deferred > 0\n ? [\n `We found ${deferred} more we can connect. Those are listed with a link each at the end, so this step stays short.`,\n ]\n : []),\n \"You can select [Skip] if you'd like to do this later in PostHog.\",\n ],\n items: offered.map((s) => s.label),\n docsLabel: 'Learn more about warehouse sources',\n docsUrl: WAREHOUSE_SOURCES_DOCS_URL,\n prompt: 'Connect these during setup?',\n confirmLabel: 'Continue [Enter]',\n cancelLabel: 'Skip [Esc]',\n },\n },\n ];\n};\n\nexport const SETUP_REPORT_FILE = 'posthog-setup-report.md';\nexport { EVENT_PLAN_FILE } from './constants.js';\n\nexport const posthogIntegrationConfig: ProgramConfig = {\n description: 'Set up PostHog SDK integration',\n id: 'posthog-integration',\n agentFlow: 'integration-v2',\n eventPlanFile: EVENT_PLAN_FILE,\n steps: POSTHOG_INTEGRATION_PROGRAM,\n getContentBlocks,\n // Basic integration runs without structured user input; drop wizard_ask\n // so the model can't pop modal prompts mid-run. The runner forwards this\n // list to the general-purpose subagent as well, so dispatched subagents\n // can't reach around the parent and ask either.\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n\n seedTasks: warehouseSeedTasks,\n\n // Kill switch over the shipped default: only an explicit 'false' excludes,\n // so a failed flag fetch keeps AI Observability and Logs in the run.\n excludedTaskTypes: (flags) =>\n flags[WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY] === 'false'\n ? ['ai-observability', 'logs']\n : [],\n\n // CI-mode prerequisite work: the headless equivalent of the detect step's\n // onReady hook. Auto-detect the framework, then gather context.\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n\n const frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.frameworkConfig = frameworkConfig;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: true,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n },\n\n run: async (\n session: WizardSession,\n runner: RunnerContext,\n ): Promise<ProgramRun> => {\n const config = session.frameworkConfig!;\n\n const typeScriptDetected = isUsingTypeScript({\n installDir: session.installDir,\n });\n session.typescript = typeScriptDetected;\n analytics.setTag('typescript', typeScriptDetected);\n\n // Read package.json and resolve framework version\n const usesPackageJson = config.detection.usesPackageJson !== false;\n let frameworkVersion: string | undefined;\n\n if (usesPackageJson) {\n const packageJson = await tryGetPackageJson({\n installDir: session.installDir,\n });\n if (packageJson) {\n const { hasDeclaredDependency } = await import('@utils/package-json');\n if (!hasDeclaredDependency(config.detection.packageName, packageJson)) {\n runner.log.warn(\n `${config.detection.packageDisplayName} does not seem to be installed. Continuing anyway — the agent will handle it.`,\n );\n }\n frameworkVersion = config.detection.getVersion(packageJson);\n } else {\n runner.log.warn(\n 'Could not find package.json. Continuing anyway — the agent will handle it.',\n );\n }\n } else {\n frameworkVersion = config.detection.getVersion(null);\n }\n\n // Analytics tags\n if (frameworkVersion && config.detection.getVersionBucket) {\n const versionBucket = config.detection.getVersionBucket(frameworkVersion);\n analytics.setTag(`${config.metadata.integration}-version`, versionBucket);\n }\n // The same kill switch the orchestrator applies via excludedTaskTypes,\n // gated here for linear/composed runs, which assemble their own prompt —\n // without this, disabling the flag would not reach this path and the\n // prompt would still instruct installing the AIO and Logs skills. Only an\n // explicit 'false' excludes, so a failed flag fetch keeps the default.\n const wizardFlags = await analytics.getAllFlagsForWizard();\n const skillCategoryInstruction =\n wizardFlags[WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY] !== 'false'\n ? `Choose a skill from the \\`integration\\` category that matches this project's framework. Start with this framework skill; load the AI Observability and Logs skills when its workflow calls for them, and follow each installed skill's own steps to completion — framework first, then AI Observability, then Logs — before verification and the setup report. Both are included by default where applicable; the skills define applicability and how to report skipped work. These three categories — \\`integration\\`, \\`ai-observability\\`, \\`logs\\` — are the only ones this run uses. Do NOT load or install skills from any other category (llm-analytics, error-tracking, feature-flags, audit, etc.) — those are handled separately. In particular, \\`ai-observability\\` is the category for AI Observability; do not substitute \\`llm-analytics\\`.`\n : `Choose a skill from the \\`integration\\` category that matches this project's framework. The \\`integration\\` category is the ONLY one this run uses. Do NOT load or install skills from any other category (ai-observability, logs, llm-analytics, error-tracking, feature-flags, audit, etc.) — those are handled separately. If the installed skill's workflow contains an \"AI Observability and Logs\" section, skip that entire section: this run excludes both products.`;\n\n const frameworkContext = session.frameworkContext;\n const contextTags = config.analytics.getTags(frameworkContext);\n Object.entries(contextTags).forEach(([key, value]) => {\n analytics.setTag(key, value);\n });\n\n return {\n integrationLabel: config.metadata.integration,\n additionalMcpServers: config.metadata.additionalMcpServers,\n detectPackageManager: config.detection.detectPackageManager,\n spinnerMessage: SPINNER_MESSAGE,\n successMessage: config.ui.successMessage,\n estimatedDurationMinutes: config.ui.estimatedDurationMinutes,\n reportFile: SETUP_REPORT_FILE,\n docsUrl: config.metadata.docsUrl,\n errorMessage: 'Integration failed',\n // The seeded warehouse task's fallback, when a user cannot hand over a\n // credential, is to give them the pre-filled new-source URL. That only\n // works if the overlay renders it as a link they can open or copy.\n richLinks: true,\n\n customPrompt: (ctx) => {\n const additionalLines = config.prompts.getAdditionalContextLines\n ? config.prompts.getAdditionalContextLines(frameworkContext)\n : [];\n const additionalContext =\n additionalLines.length > 0\n ? '\\n' + additionalLines.map((line) => `- ${line}`).join('\\n')\n : '';\n\n return `You have access to the PostHog MCP server which provides skills to integrate PostHog into this ${\n config.metadata.name\n } project.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- Framework: ${config.metadata.name} ${frameworkVersion || 'latest'}\n- TypeScript: ${typeScriptDetected ? 'Yes' : 'No'}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n- Project type: ${config.prompts.projectTypeDetection}\n- Package installation: ${\n config.prompts.packageInstallation ?? DEFAULT_PACKAGE_INSTALLATION\n }${additionalContext}\n\nInstructions (follow these steps IN ORDER - do not skip or reorder):\n\nSTEP 1: Call load_skill_menu (from the wizard-tools MCP server) with category: \"integration\" to see available framework skills.\n If the tool fails, emit: ${\n AgentSignals.ERROR_MCP_MISSING\n } Could not load skill menu and halt.\n\n ${skillCategoryInstruction}\n If no suitable integration skill is found, emit: ${\n AgentSignals.ERROR_RESOURCE_MISSING\n } Could not find a suitable skill for this project.\n\nSTEP 2: Call install_skill (from the wizard-tools MCP server) with the chosen skill ID (e.g., \"integration-nextjs-app-router\").\n Do NOT run any shell commands to install skills.\n If install_skill fails, emit on its own line: ${\n AgentSignals.SKILL_INSTALL_FAILED\n } <skill id — one-line reason>. Then CONTINUE and SKIP to STEP 5 the integration without the skill, following these steps and your knowledge of ${\n config.metadata.name\n } and PostHog's official docs, and note in the setup report that the skill could not be installed.\n\nSTEP 3: Load the installed skill's SKILL.md file to understand what references are available.\n\nSTEP 4: Follow the skill's program files in sequence. Look for numbered program files in the references (e.g., files with patterns like \"1-\", \"2-\", \"3-\"). Start with the first one and proceed through each step until completion. Each program file will tell you what to do and which file comes next. Never directly write PostHog tokens directly to code files; always use environment variables.\n\nSTEP 5: Set up environment variables for PostHog using the wizard-tools MCP server (this runs locally — secret values never leave the machine):\n - Use check_env_keys to see which keys the project already sets, and where. Omit filePath and it scans every .env file in the project, so you don't have to guess between .env, .env.local and a nested one. It answers { status, foundIn } per key: \"present\" means a real env file sets the key, while a key found only in a committed template (.env.example and friends) reads as \"missing\" — a template documents a key rather than setting it, and is never a file to write credentials into.\n - Use set_env_values to create or update the PostHog public token and host, using the appropriate environment variable naming convention for ${\n config.metadata.name\n }, which you'll find in example code. The tool will also ensure .gitignore coverage. Don't assume the presence of keys means the value is up to date. Write the correct value each time.\n - Reference these environment variables in the code files you create instead of hardcoding the public token and host.\n\nImportant: Use the detect_package_manager tool (from the wizard-tools MCP server) to determine which package manager the project uses, then run its install command to add the SDK. Do not manually search for lockfiles or config files. If a file already EXISTS, read it immediately before you edit or overwrite it — writing from a stale read causes a tool failure. Creating a brand-new file needs no prior read: never read a path that does not exist yet; just write it.\n\n${warehouseReportInstruction(session)}\n`;\n },\n\n postRun: async (sess, credentials) => {\n const envVars = config.environment.getEnvVars(\n credentials.projectApiKey,\n credentials.host.apiHost,\n );\n if (config.environment.uploadToHosting) {\n const { uploadEnvironmentVariablesStep } = await import(\n '@steps/index'\n );\n const uploadedEnvVars = await uploadEnvironmentVariablesStep(\n envVars,\n {\n integration: config.metadata.integration,\n session: sess,\n },\n );\n if (uploadedEnvVars.length > 0) {\n analytics.capture(WIZARD_INTERACTION_EVENT_NAME, {\n action: 'wizard_env_vars_uploaded',\n integration: config.metadata.integration,\n variable_count: uploadedEnvVars.length,\n variable_keys: uploadedEnvVars,\n });\n }\n }\n\n if (sess.signup) {\n const deepLink = await requestDeepLink(\n credentials.accessToken,\n credentials.host,\n );\n if (deepLink) {\n const taggedDeepLink = withUtm(deepLink, 'dashboard-deeplink');\n sess.frameworkContext[DASHBOARD_DEEP_LINK_KEY] = taggedDeepLink;\n openTrackedLink(taggedDeepLink, 'dashboard-deeplink', {\n auto: true,\n });\n }\n }\n },\n\n buildOutroNextSteps: (sess, credentials, completedSeededTypes) =>\n buildWarehouseNextSteps(\n sess,\n credentials.host,\n credentials.projectId,\n completedSeededTypes,\n ),\n\n buildOutroData: (sess, credentials) => {\n const envVars = config.environment.getEnvVars(\n credentials.projectApiKey,\n credentials.host.apiHost,\n );\n const deepLink = sess.frameworkContext[DASHBOARD_DEEP_LINK_KEY];\n const continueUrl = resolveContinueUrl(\n sess,\n credentials.host,\n deepLink,\n );\n\n const changes = [\n ...config.ui.getOutroChanges(frameworkContext),\n Object.keys(envVars).length > 0\n ? 'Added environment variables to .env file'\n : '',\n ].filter(Boolean);\n\n return {\n kind: OutroKind.Success as const,\n message: 'Successfully installed PostHog!',\n changes,\n docsUrl: config.metadata.docsUrl,\n continueUrl,\n // The linear sequence seeds no tasks, so nothing here was connected\n // during the run. `buildOutroNextSteps` carries the orchestrated case.\n nextSteps: buildWarehouseNextSteps(\n sess,\n credentials.host,\n credentials.projectId,\n [],\n ),\n // Set once the agent mirrors the report into a notebook and emits [NOTEBOOK_URL].\n notebookUrl: sess.notebookUrl ?? undefined,\n // No report file — the prompt points at the notebook, when the run captured one.\n handoffPrompt: sess.notebookUrl\n ? buildCodingAgentPrompt(sess.notebookUrl)\n : undefined,\n };\n },\n };\n },\n};\n\nexport { POSTHOG_INTEGRATION_PROGRAM } from './steps.js';\n\n/**\n * Self-contained run step that runs the integration agent. Other programs\n * import this and splice it into their own step list to compose the\n * integration's work as one of their run steps — self-driving sets up PostHog\n * this way before its own run. The host program supplies `show`/`onRunPrep`/\n * `targetDir`; this carries the run.\n */\nexport const integrationRunStep: ProgramStep = {\n id: 'run',\n label: 'Integration',\n screenId: 'run',\n // composed: runs inside the host program (self-driving), so skip the\n // integration's terminal outro + analytics shutdown of the shared client.\n run: (session) =>\n runProgramAgent(posthogIntegrationConfig, session, { composed: true }),\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n};\n","/**\n * Revenue analytics prerequisite detection.\n *\n * Scans the project for PostHog + Stripe SDKs and writes results\n * into frameworkContext for the intro screen to render.\n */\n\nimport { existsSync, statSync } from 'fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\n\nexport {\n findPackageJsons,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type PackageMatch,\n} from '@programs/shared/package-scanning';\n\n/**\n * Structured detection errors. The screen renders each kind into JSX\n * with proper formatting — keeps error data separate from presentation.\n */\nexport type RevenueDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-package-json' }\n | { kind: 'no-sdks'; scannedCount: number }\n | { kind: 'missing-posthog'; foundStripe: string[] }\n | { kind: 'missing-stripe'; foundPosthog: string[] };\n\n/** `[ABORT] <reason>` cases the revenue analytics skill can emit. */\nexport const REVENUE_ABORT_CASES: AbortCase[] = [\n {\n // Skill emits: [ABORT] Could not find a PostHog distinct_id\n match: /^could not find a posthog distinct_id$/i,\n message: 'Could not find a PostHog distinct_id',\n body:\n 'The agent could not find PostHog distinct_id usage in your codebase. ' +\n 'Your users must be identified in PostHog before they can be tagged in Stripe. ' +\n 'Please identify your users and try again.',\n docsUrl: 'https://posthog.com/docs/product-analytics/identify',\n },\n {\n // Skill emits: [ABORT] Could not find a Stripe integration\n match: /^could not find a stripe integration$/i,\n message: 'Could not find a Stripe integration',\n body:\n 'The Wizard could not find an existing Stripe customer, charge, ' +\n 'subscription, or other Stripe operations. Please run the Revenue ' +\n 'Analytics Wizard on a project with an existing Stripe integration.',\n docsUrl: 'https://posthog.com/docs/revenue-analytics',\n },\n];\n\n/**\n * Scan `session.installDir` for PostHog + Stripe SDKs. Writes detection\n * results into frameworkContext via the callback — either the detected\n * SDK lists (for the intro screen) or a `RevenueDetectError` on failure.\n *\n * The skill install happens later in the bootstrap runner, not here.\n */\nexport function detectRevenuePrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: RevenueDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n // Verify the install directory exists and is readable\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n // Find all package.json files (root + monorepo subpackages)\n const matches = findPackageJsons(installDir);\n\n if (matches.length === 0) {\n fail({ kind: 'no-package-json' });\n return;\n }\n\n // Aggregate detected SDKs across all package.json files\n const allPosthogSdks = new Set<string>();\n const allStripeSdks = new Set<string>();\n for (const match of matches) {\n for (const sdk of match.posthogSdks) allPosthogSdks.add(sdk);\n for (const sdk of match.stripeSdks) allStripeSdks.add(sdk);\n }\n\n const detectedPosthogSdks = [...allPosthogSdks];\n const detectedStripeSdks = [...allStripeSdks];\n\n if (detectedPosthogSdks.length === 0 && detectedStripeSdks.length === 0) {\n fail({ kind: 'no-sdks', scannedCount: matches.length });\n return;\n }\n\n if (detectedPosthogSdks.length === 0) {\n fail({ kind: 'missing-posthog', foundStripe: detectedStripeSdks });\n return;\n }\n\n if (detectedStripeSdks.length === 0) {\n fail({ kind: 'missing-stripe', foundPosthog: detectedPosthogSdks });\n return;\n }\n\n setFrameworkContext('detectedPosthogSdks', detectedPosthogSdks);\n setFrameworkContext('detectedStripeSdks', detectedStripeSdks);\n setFrameworkContext(\n 'detectedPackagePaths',\n matches\n .filter((m) => m.posthogSdks.length > 0 || m.stripeSdks.length > 0)\n .map((m) => m.path),\n );\n}\n","/**\n * Revenue analytics program step list.\n *\n * The detect step checks for PostHog + Stripe SDKs. The skill install\n * and agent run live in the program runner (see agent-runner.ts).\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { detectRevenuePrerequisites } from './detect.js';\n\nexport const REVENUE_ANALYTICS_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n // Headless step: no screen, no gate. onReady fires after bin.ts\n // assigns the session — the hook scans for PostHog + Stripe SDKs\n // and writes the results (or a detectError) to frameworkContext\n // for the intro screen to render.\n onReady: (ctx) =>\n detectRevenuePrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'revenue-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Revenue analytics',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Agent-skill learn-deck — the short three-line sequence shown while a\n * skill-based program (audit, revenue-analytics, agent-skill, etc.)\n * runs. Skill programs don't need the full PostHog onboarding narrative.\n */\n\nimport { Text } from 'ink';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => {\n const skillId = store?.session.skillId ?? 'unknown';\n return [\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n { content: 'The Wizard is an agent.', pause: 4000 },\n {\n pause: 60000,\n content: (\n <Text>\n Running the <Text color=\"cyan\">{skillId}</Text> skill...\n </Text>\n ),\n },\n ];\n};\n","import type { ProgramConfig } from '@programs/program-step';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { REVENUE_ANALYTICS_PROGRAM } from './steps.js';\nimport { REVENUE_ABORT_CASES } from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/revenue-analytics/index.js';\n\nexport const revenueAnalyticsConfig: ProgramConfig = {\n command: 'revenue-analytics',\n description: 'Set up PostHog for Revenue Analytics',\n id: 'revenue-analytics-setup',\n skillId: 'revenue-analytics-setup',\n steps: REVENUE_ANALYTICS_PROGRAM,\n getContentBlocks,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n run: {\n skillId: 'revenue-analytics-setup',\n integrationLabel: 'revenue-analytics-setup',\n customPrompt: () => 'Set up revenue analytics for this project.',\n successMessage: 'Revenue analytics configured!',\n reportFile: 'posthog-revenue-report.md',\n docsUrl: 'https://posthog.com/docs/revenue-analytics',\n spinnerMessage: 'Setting up revenue analytics...',\n estimatedDurationMinutes: 5,\n abortCases: REVENUE_ABORT_CASES,\n },\n requires: ['posthog-integration'],\n};\n\nexport { REVENUE_ANALYTICS_PROGRAM } from './steps.js';\nexport {\n detectRevenuePrerequisites,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type RevenueDetectError,\n} from './detect.js';\n","/**\n * Warehouse-source program step list.\n *\n * The detect step scans for warehouse-source signals. The skill install and\n * agent run live in the program runner (see agent-runner.ts). The skill drives\n * both in-CLI source creation and deep-link emission per detected source.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { detectWarehousePrerequisites } from './detect.js';\n\nexport const WAREHOUSE_SOURCE_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting data sources',\n // Headless step: no screen. onReady scans installDir and writes the\n // detected sources (or a detectError) to frameworkContext for the\n // intro screen to render.\n onReady: (ctx) =>\n detectWarehousePrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'warehouse-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Data warehouse',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { LONGER_ASK_TIMEOUT_MS } from '@agent';\nimport { WAREHOUSE_SOURCE_PROGRAM } from './steps.js';\nimport {\n WAREHOUSE_ABORT_CASES,\n getDetectedWarehouseSources,\n} from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/warehouse-source/index.js';\n\n/**\n * Inject the detected sources (and their creation mode) into the prompt so the\n * skill knows what to set up. The *how* — in-CLI creation vs deep-link, field\n * collection, validation — lives in the skill, not here.\n */\nfunction buildPrompt(session: WizardSession): string {\n const sources = getDetectedWarehouseSources(session);\n if (sources.length === 0) {\n return 'Set up a data warehouse source for this project.';\n }\n\n const lines = sources.map(\n (s) =>\n `- ${s.label} (kind: ${s.kind}, mode: ${s.mode}) — ${s.matchedSignal}`,\n );\n\n return [\n 'The wizard detected the following data warehouse sources in this project:',\n ...lines,\n '',\n 'Each signal names the file it came from. Trust that path — the wizard ' +\n 'read it. To confirm a key is set, call `check_env_keys` with the key ' +\n 'names and no `filePath`; it scans every `.env` file in the project.',\n '',\n 'Set these up in PostHog following the skill instructions: create `in-cli` ' +\n 'sources directly via the PostHog MCP after collecting credentials; for ' +\n '`deep-link` sources, provide the user the pre-filled new-source URL.',\n 'Use the `kind` string exactly as printed above for `source_type` — ' +\n 'PostHog rejects the display label.',\n ].join('\\n');\n}\n\nexport const warehouseSourceConfig: ProgramConfig = {\n command: 'warehouse',\n description: 'Detect and connect Data Warehouse sources',\n id: 'warehouse-source',\n skillId: 'data-warehouse-source-setup',\n steps: WAREHOUSE_SOURCE_PROGRAM,\n getContentBlocks,\n reportFile: 'posthog-warehouse-report.md',\n allowedTools: ['Agent'],\n run: (session: WizardSession): Promise<ProgramRun> =>\n Promise.resolve({\n skillId: 'data-warehouse-source-setup',\n integrationLabel: 'data-warehouse-source-setup',\n customPrompt: () => buildPrompt(session),\n successMessage: 'Data warehouse source connected!',\n reportFile: 'posthog-warehouse-report.md',\n docsUrl: 'https://posthog.com/docs/data-warehouse',\n spinnerMessage: 'Connecting your data source...',\n estimatedDurationMinutes: 5,\n // Same questions the orchestrator's seeded warehouse task asks, so the\n // same allowance. On the 5-minute default a user who went to fetch a\n // database password came back to a cancelled prompt and the browser\n // fallback — in the command the outro sends declines to.\n askTimeoutMs: LONGER_ASK_TIMEOUT_MS,\n abortCases: WAREHOUSE_ABORT_CASES,\n }),\n requires: ['posthog-integration'],\n};\n\nexport { WAREHOUSE_SOURCE_PROGRAM } from './steps.js';\nexport {\n detectWarehousePrerequisites,\n getDetectedWarehouseSources,\n DETECTED_WAREHOUSE_SOURCES_KEY,\n WAREHOUSE_ABORT_CASES,\n type WarehouseDetectError,\n} from './detect.js';\n","/**\n * Generic agent skill step list.\n *\n * Minimal flow: intro → health-check → auth → run → outro → skills.\n * No detection, no setup, no MCP.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const AGENT_SKILL_STEPS: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'agent-skill-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Running',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Generic agent skill program factory.\n *\n * Creates a ProgramConfig for any context-mill skill. Provide a\n * skill ID and basic UI config — the factory handles the rest.\n *\n * Usage:\n * createSkillProgram({\n * skillId: 'error-tracking-setup',\n * command: 'errors',\n * id: 'error-tracking',\n * description: 'Set up PostHog error tracking',\n * integrationLabel: 'error-tracking',\n * successMessage: 'Error tracking configured!',\n * reportFile: 'posthog-error-tracking-report.md',\n * docsUrl: 'https://posthog.com/docs/error-tracking',\n * spinnerMessage: 'Setting up error tracking...',\n * estimatedDurationMinutes: 5,\n * })\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\nimport type { AbortCase } from '@agent/types';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AGENT_SKILL_STEPS } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/agent-skill/index.js';\n\nexport interface SkillProgramOptions {\n /** Context-mill skill ID to install */\n skillId: string;\n /** CLI subcommand name */\n command: string;\n /** Unique flow key — must match a Program enum entry */\n id: string;\n /** CLI description shown in --help */\n description: string;\n /** Analytics integration label */\n integrationLabel: string;\n /** Custom prompt instruction. Appended after default project prompt. */\n customPrompt?: string;\n successMessage: string;\n reportFile: string;\n docsUrl: string;\n spinnerMessage: string;\n estimatedDurationMinutes: number;\n /** Other program ids that must be satisfied first */\n requires?: string[];\n /** Override the default outro. Receives the same args as ProgramRun.buildOutroData. */\n buildOutroData?: ProgramRun['buildOutroData'];\n /** Known `[ABORT] <reason>` cases the skill can emit. */\n abortCases?: AbortCase[];\n}\n\nexport function createSkillProgram(opts: SkillProgramOptions): ProgramConfig {\n return {\n command: opts.command,\n description: opts.description,\n id: opts.id,\n skillId: opts.skillId,\n steps: AGENT_SKILL_STEPS,\n reportFile: opts.reportFile,\n getContentBlocks,\n run: {\n skillId: opts.skillId,\n integrationLabel: opts.integrationLabel,\n customPrompt: opts.customPrompt ? () => opts.customPrompt! : undefined,\n successMessage: opts.successMessage,\n reportFile: opts.reportFile,\n docsUrl: opts.docsUrl,\n spinnerMessage: opts.spinnerMessage,\n estimatedDurationMinutes: opts.estimatedDurationMinutes,\n buildOutroData: opts.buildOutroData,\n abortCases: opts.abortCases,\n },\n requires: opts.requires,\n };\n}\n\nexport { AGENT_SKILL_STEPS } from './steps.js';\n","import type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\n\n/** `[ABORT] <reason>` cases the audit skill can emit. Reason strings are\n * defined in the skill's `Abort statuses` section. */\nexport const AUDIT_ABORT_CASES: AbortCase[] = [\n {\n match: /^no posthog sdk found$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'No PostHog SDK found',\n body:\n 'The audit needs an existing PostHog integration to review. No PostHog ' +\n 'SDK appears in this project’s dependency manifests. Run the basic ' +\n 'integration program to install PostHog first, then re-run the audit.',\n docsUrl: 'https://posthog.com/docs/getting-started/install',\n },\n];\n","import path from 'path';\nimport { writeJsonAtomic } from '@utils/atomic-ledger';\nimport { logToFile } from '@utils/debug';\nimport { AUDIT_CHECKS_FILE, type AuditCheck } from './types.js';\n\n/**\n * The 10 source-tree checks the audit runs, plus one row for the PostHog-side\n * sweep and two workflow rows at the end (so the skill's\n * `audit_resolve_checks` calls for `write-report` and `upload-notebook`\n * succeed — the skill writes the report to disk, then mirrors it into a\n * PostHog notebook as its final step).\n *\n * Every id the skill resolves needs a row here: a batch resolve rejects\n * atomically, so one missing id discards the whole call.\n *\n * `posthog-side-findings` is a sweep row, not a rule: the skill reads what\n * PostHog itself already computed for this project (error-tracking\n * recommendations, health issues) and appends one row per open finding via\n * `audit_add_checks`. That's why the seed can't enumerate them — the list is\n * whatever the project's data says today, and it grows as PostHog ships new\n * checks, with no wizard release needed.\n */\nexport const AUDIT_SEED_CHECKS: AuditCheck[] = [\n {\n id: 'sdk-installed',\n area: 'Installation',\n label: 'PostHog SDK installed',\n status: 'pending',\n },\n {\n id: 'sdk-up-to-date',\n area: 'Installation',\n label: 'SDK version up to date',\n status: 'pending',\n },\n {\n id: 'init-correct',\n area: 'Installation',\n label: 'Initialization is correct',\n status: 'pending',\n },\n {\n id: 'init-not-duplicated',\n area: 'Installation',\n label: 'One initialization per runtime',\n status: 'pending',\n },\n {\n id: 'identify-stable-distinct-id',\n area: 'Identification',\n label: 'Stable distinct_id (not session UUID)',\n status: 'pending',\n },\n {\n id: 'identify-not-late',\n area: 'Identification',\n label: 'identify() called before captures / flag evals',\n status: 'pending',\n },\n {\n id: 'cross-runtime-distinct-id',\n area: 'Identification',\n label: 'Same distinct_id across client and server',\n status: 'pending',\n },\n {\n id: 'identify-reset-on-logout',\n area: 'Identification',\n label: 'reset() called on logout / account switch',\n status: 'pending',\n },\n {\n id: 'capture-event-names-static',\n area: 'Event Capture',\n label: 'Event names are static and consistent',\n status: 'pending',\n },\n {\n id: 'capture-uses-proxy',\n area: 'Event Capture',\n label: 'Captures route through a reverse proxy',\n status: 'pending',\n },\n {\n id: 'capture-growth-events',\n area: 'Event Capture',\n label: 'Key activation events captured',\n status: 'pending',\n },\n {\n id: 'live-data-findings',\n // Area is capped at COL_AREA_WIDTH (18) in the checks viewer and never\n // flexes, so it has to fit. Labels get the flexed column.\n area: 'Live Data',\n label: 'Open findings in PostHog',\n status: 'pending',\n },\n {\n id: 'write-report',\n area: 'Write report',\n label: 'Create posthog-audit-report.md',\n status: 'pending',\n },\n {\n id: 'upload-notebook',\n area: 'Upload notebook',\n label: 'Write the report into a PostHog notebook',\n status: 'pending',\n },\n];\n\n/**\n * Atomically write a seeded ledger to the project's audit checks file.\n *\n * Each audit-flavored program (doctor, events-audit) owns its own seed\n * shape — pass the seed in so this writer stays program-agnostic.\n */\nexport function seedAuditLedger(\n installDir: string,\n checks: AuditCheck[] = AUDIT_SEED_CHECKS,\n): void {\n const target = path.join(installDir, AUDIT_CHECKS_FILE);\n writeJsonAtomic(target, checks);\n logToFile(`seedAuditLedger: wrote ${checks.length} entries to ${target}`);\n}\n","import {\n AGENT_SKILL_STEPS,\n createSkillProgram,\n} from '@programs/agent-skill/index';\nimport type { ProgramStep, ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { RunnerContext } from '@programs/runner-context';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { headlessOption, regionOption } from '@lib/headless-mode';\nimport { AUDIT_ABORT_CASES } from './detect.js';\nimport {\n AUDIT_CHECKS_FILE,\n AUDIT_CHECKS_KEY,\n AUDIT_REPORT_FILE,\n} from './types.js';\nimport { AUDIT_SEED_CHECKS, seedAuditLedger } from './seed.js';\n\n/** Audit-specific screens for the shared agent-skill pipeline. */\nconst AUDIT_SCREEN_BY_STEP: Record<string, string> = {\n intro: 'audit-intro',\n run: 'audit-run',\n outro: 'audit-outro',\n};\n\nconst seedBeforeAuditRun = (session: WizardSession): void => {\n seedAuditLedger(session.installDir);\n session.frameworkContext[AUDIT_CHECKS_KEY] = AUDIT_SEED_CHECKS;\n};\n\nconst withAuditScreens = (steps: ProgramStep[]): ProgramStep[] =>\n steps.map((step) => {\n const override = AUDIT_SCREEN_BY_STEP[step.id];\n return override ? { ...step, screenId: override } : step;\n });\n\nconst auditSteps: ProgramStep[] = withAuditScreens(AGENT_SKILL_STEPS);\n\nconst baseConfig = createSkillProgram({\n skillId: 'audit',\n command: 'audit',\n id: 'audit',\n description: 'Audit and improve your PostHog setup',\n integrationLabel: 'audit',\n customPrompt:\n 'Run a comprehensive audit of the existing PostHog integration. Follow the skill program steps in order. Do not modify any project files — only create the final audit report.',\n successMessage:\n 'Audit complete! You can view the audit report at ./posthog-audit-report.md',\n reportFile: AUDIT_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/product-analytics/best-practices',\n spinnerMessage: 'Auditing PostHog integration...',\n estimatedDurationMinutes: 5,\n requires: ['posthog-integration'],\n abortCases: AUDIT_ABORT_CASES,\n});\n\nconst auditRun = async (\n session: WizardSession,\n runner: RunnerContext,\n): Promise<ProgramRun> => {\n seedBeforeAuditRun(session);\n\n if (!baseConfig.run) {\n throw new Error('Audit program has no run configuration.');\n }\n\n const baseRun =\n typeof baseConfig.run === 'function'\n ? await baseConfig.run(session, runner)\n : baseConfig.run;\n\n return {\n ...baseRun,\n // Override the default outro so the dashboard + notebook URLs the\n // agent emits via `[DASHBOARD_URL]` / `[NOTEBOOK_URL]` are surfaced\n // on the post-run screen.\n buildOutroData: (sess, credentials) => {\n const cloudUrl = credentials.host.appHost;\n const continueUrl = sess.signup\n ? `${cloudUrl}/products?source=wizard`\n : undefined;\n\n // Note: `sess` here is the agent-runner's snapshot of session at\n // runAgent() invocation time. Any URL emissions during the run land\n // on the live store, NOT on this snapshot. The UI layer\n // (InkUI.setOutroData) merges live URLs in on top of this return\n // value, so it's safe to leave dashboardUrl/notebookUrl as undefined\n // here when the snapshot doesn't have them.\n return {\n kind: OutroKind.Success as const,\n message: baseRun.successMessage,\n reportFile: baseRun.reportFile,\n docsUrl: baseRun.docsUrl,\n continueUrl,\n dashboardUrl: session.dashboardUrl ?? undefined,\n notebookUrl: session.notebookUrl ?? undefined,\n };\n },\n };\n};\n\nexport const auditConfig: ProgramConfig = {\n ...baseConfig,\n steps: auditSteps,\n run: auditRun,\n auditLedgerFile: AUDIT_CHECKS_FILE,\n // Ledger tools are opt-in per program; pi matches on the short name.\n allowedTools: [\n 'Agent',\n WIZARD_TOOL_NAMES.auditSeedChecks,\n WIZARD_TOOL_NAMES.auditAddChecks,\n WIZARD_TOOL_NAMES.auditResolveChecks,\n ],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n // The experimental headless flag — declared on `audit` (and basic\n // integration) rather than globally. mergeCommandOptions lands it on the\n // `wizard audit` command; dispatchProgram routes it to runWizardHeadless.\n cliOptions: { ...headlessOption, ...regionOption },\n};\n","/**\n * Events-audit program.\n *\n * Mirrors the posthog-integration step list, except:\n * - The initial framework detection step is omitted — the events-audit\n * skill handles detection at agent run time.\n * - The intro step uses the audit intro screen (no framework selection\n * logic) instead of the integration intro.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nfunction needsSetup(session: WizardSession): boolean {\n const config = session.frameworkConfig;\n if (!config?.metadata.setup?.questions) return false;\n\n return config.metadata.setup.questions.some(\n (q: { key: string }) => !(q.key in session.frameworkContext),\n );\n}\n\nexport const EVENTS_AUDIT_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'audit-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'setup',\n label: 'Setup',\n screenId: 'setup',\n show: needsSetup,\n isComplete: (session) => !needsSetup(session),\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Events audit',\n screenId: 'audit-run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'mcp',\n label: 'MCP servers',\n screenId: 'mcp',\n isComplete: (session) => session.mcpComplete,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'audit-outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'keep-skills',\n label: 'Keep Skills',\n screenId: 'keep-skills',\n },\n];\n","import type { AuditCheck } from '@programs/audit/types';\n\n/**\n * The 7 phases the events-audit skill marches through. One check per area\n * so PendingChecksList renders a clean linear pipeline (area = bold header,\n * single row = the active spinner).\n *\n * Phase ids match what the skill's step files resolve via\n * `mcp__wizard-tools__audit_resolve_checks` as each phase completes. The\n * skill's step 1 also seeds these same ids — keep both in sync so the\n * wizard pre-seed and the skill's MCP seed agree.\n */\nexport const EVENTS_AUDIT_SEED_CHECKS: AuditCheck[] = [\n {\n id: 'detect-sdk',\n area: 'Detect SDK',\n label: 'Identify PostHog SDK(s) in dependencies',\n status: 'pending',\n },\n {\n id: 'scan-sites',\n area: 'Scan capture sites',\n label: 'Grep capture/identify/group call sites',\n status: 'pending',\n },\n {\n id: 'enrich-sites',\n area: 'Enrich',\n label: 'Subagent fan-out to read capture files',\n status: 'pending',\n },\n {\n id: 'query-volume',\n area: 'Query PostHog',\n label: '30-day volume + last_seen via MCP',\n status: 'pending',\n },\n {\n id: 'write-report',\n area: 'Write report',\n label: 'Create posthog-events-audit-report.md',\n status: 'pending',\n },\n {\n id: 'create-dashboard',\n area: 'Create dashboard',\n label: 'Optional: dashboard for resolved events',\n status: 'pending',\n },\n {\n id: 'upload-notebook',\n area: 'Upload notebook',\n label: 'Write the report into a PostHog notebook',\n status: 'pending',\n },\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport { SPINNER_MESSAGE } from '@programs/framework-config';\nimport { isUsingTypeScript } from '@utils/setup-utils';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { EVENTS_AUDIT_PROGRAM } from './steps.js';\nimport { AUDIT_CHECKS_FILE, AUDIT_CHECKS_KEY } from '@programs/audit/types';\nimport { seedAuditLedger } from '@programs/audit/seed';\nimport { EVENTS_AUDIT_SEED_CHECKS } from './seed.js';\n\n// SETUP_REPORT_FILE is also re-exported for backward compat with existing\n// imports from `@programs/events-audit`. EVENT_INVENTORY_FILE and\n// EVENT_INVENTORY_PART_PATTERN are only used by yara-hooks, which imports\n// them directly from `./constants` — no re-export needed.\nimport { SETUP_REPORT_FILE } from './constants.js';\nexport { SETUP_REPORT_FILE };\n\nconst DOCS_URL = 'https://posthog.com/docs/product-analytics/best-practices';\n\n/**\n * No CLI word of its own since the audit family took over: `wizard audit\n * events` is the live path, and it resolves to the context-mill `audit-events`\n * skill (whose id AuditRunScreen keys its slides on), not to this config.\n * Registered so its id stays resolvable; nothing dispatches to it today.\n */\nexport const eventsAuditConfig: ProgramConfig = {\n description: 'Audit PostHog event tracking in this project',\n id: 'events-audit',\n skillId: 'events-audit',\n steps: EVENTS_AUDIT_PROGRAM,\n // Top-level reportFile so AuditRunScreen can resolve the report path\n // synchronously without unwrapping the deferred `run` function.\n reportFile: SETUP_REPORT_FILE,\n auditLedgerFile: AUDIT_CHECKS_FILE,\n allowedTools: [\n 'Agent',\n WIZARD_TOOL_NAMES.auditSeedChecks,\n WIZARD_TOOL_NAMES.auditAddChecks,\n WIZARD_TOOL_NAMES.auditResolveChecks,\n ],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n\n run: (session: WizardSession): Promise<ProgramRun> => {\n const typeScriptDetected = isUsingTypeScript({\n installDir: session.installDir,\n });\n session.typescript = typeScriptDetected;\n\n // Seed the audit ledger so AuditRunScreen has something to render\n // before the agent emits its first check update. The events-audit\n // ledger is the 6-phase pipeline, not the doctor's 10 integrity checks.\n seedAuditLedger(session.installDir, EVENTS_AUDIT_SEED_CHECKS);\n session.frameworkContext[AUDIT_CHECKS_KEY] = EVENTS_AUDIT_SEED_CHECKS;\n\n return Promise.resolve({\n skillId: 'events-audit',\n integrationLabel: 'events-audit',\n spinnerMessage: SPINNER_MESSAGE,\n successMessage:\n 'Events audit complete! You can view the report at ./posthog-events-audit-report.md',\n estimatedDurationMinutes: 5,\n reportFile: SETUP_REPORT_FILE,\n docsUrl: DOCS_URL,\n errorMessage: 'Events audit failed',\n\n customPrompt: (ctx) =>\n `Audit PostHog event capture in this project. Do not modify any project files — produce a read-only report only.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- TypeScript: ${typeScriptDetected ? 'Yes' : 'No'}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n`,\n\n buildOutroData: (sess, credentials) => {\n const cloudUrl = credentials.host.appHost;\n const continueUrl = sess.signup\n ? `${cloudUrl}/products?source=wizard`\n : undefined;\n // The agent emits `[DASHBOARD_URL] <url>` once it creates the\n // dashboard; the SDK-message interceptor stores it on the session.\n // Fall back to the dashboards index if nothing was emitted.\n const dashboardUrl =\n sess.dashboardUrl ?? (cloudUrl ? `${cloudUrl}/dashboard` : undefined);\n\n // The agent emits `[NOTEBOOK_URL] <url>` once it uploads the report\n // to a PostHog notebook. No fallback: if the notebook upload was\n // skipped (e.g. MCP unavailable) we just don't show a link.\n const notebookUrl = sess.notebookUrl ?? undefined;\n\n return {\n kind: OutroKind.Success as const,\n message: 'Your events audit was successful',\n reportFile: SETUP_REPORT_FILE,\n changes: [],\n docsUrl: DOCS_URL,\n continueUrl,\n dashboardUrl,\n notebookUrl,\n };\n },\n });\n },\n};\n\nexport { EVENTS_AUDIT_PROGRAM } from './steps.js';\n","import type { ProgramStep } from '@programs/program-step';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const POSTHOG_DOCTOR_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'doctor-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'report',\n label: 'Doctor report',\n screenId: 'doctor-report',\n isComplete: (session) => session.outroData !== null,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n];\n","import { z } from 'zod';\n\nexport const HealthIssueSeveritySchema = z.enum([\n 'critical',\n 'warning',\n 'info',\n]);\nexport type HealthIssueSeverity = z.infer<typeof HealthIssueSeveritySchema>;\n\nexport const HealthIssueStatusSchema = z.enum(['active', 'resolved']);\n\nexport const HealthIssueSchema = z.object({\n id: z.string(),\n kind: z.string(),\n severity: HealthIssueSeveritySchema,\n status: HealthIssueStatusSchema,\n dismissed: z.boolean(),\n created_at: z.string(),\n updated_at: z.string(),\n resolved_at: z.string().nullable().optional(),\n});\nexport type HealthIssue = z.infer<typeof HealthIssueSchema>;\n\nexport const HealthIssueListResponseSchema = z.object({\n results: z.array(HealthIssueSchema),\n count: z.number().optional(),\n next: z.string().nullable().optional(),\n previous: z.string().nullable().optional(),\n});\nexport type HealthIssueListResponse = z.infer<\n typeof HealthIssueListResponseSchema\n>;\n\nexport interface HealthIssueSummary {\n total: number;\n by_severity: Record<HealthIssueSeverity, number>;\n}\n","import axios from 'axios';\nimport { analytics } from '@utils/analytics';\nimport { handleApiError } from '@shared/api';\nimport { WIZARD_USER_AGENT } from '@shared/constants';\nimport { HealthIssueListResponseSchema, type HealthIssue } from './types';\n\nexport async function fetchHealthIssues(\n accessToken: string,\n baseUrl: string,\n projectId: number,\n): Promise<HealthIssue[]> {\n const endpoint = `/api/environments/${projectId}/health_issues/`;\n const url = `${baseUrl}${endpoint}?status=active&dismissed=false&limit=250`;\n try {\n const response = await axios.get(url, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n return HealthIssueListResponseSchema.parse(response.data).results;\n } catch (error) {\n const apiError = handleApiError(error, 'fetch health issues');\n analytics.captureException(apiError, { endpoint, baseUrl, projectId });\n throw apiError;\n }\n}\n","import { POSTHOG_DOCS_URL } from '@shared/constants';\n\nexport interface KindMeta {\n title: string;\n description: string;\n docsUrl: string;\n}\n\nexport const KIND_METADATA: Record<string, KindMeta> = {\n ingestion_lag: {\n title: 'Ingestion is delayed',\n description:\n 'Events are being received but are taking longer than usual to appear.',\n docsUrl: `${POSTHOG_DOCS_URL}/support/troubleshooting`,\n },\n ingestion_warning: {\n title: 'Ingestion warnings on recent events',\n description:\n 'Some recent events were rejected or flagged by the ingestion pipeline.',\n docsUrl: `${POSTHOG_DOCS_URL}/support/troubleshooting`,\n },\n sdk_outdated: {\n title: 'SDK version is out of date',\n description:\n 'One or more SDKs are running an old version. Upgrade to get the latest fixes.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries`,\n },\n no_live_events: {\n title: 'No $pageview or $screen events in the last 30 days',\n description:\n 'PostHog is not receiving page or screen events from this project.',\n docsUrl: `${POSTHOG_DOCS_URL}/getting-started/install`,\n },\n no_pageleave_events: {\n title: '$pageleave events not being sent',\n description:\n 'Enable pageleave tracking to power bounce rate and session duration.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries/js#config`,\n },\n scroll_depth: {\n title: 'Scroll depth tracking disabled',\n description:\n 'Turn on scroll depth to capture how far users read each page.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries/js#config`,\n },\n authorized_urls: {\n title: 'No authorized URLs configured',\n description:\n 'Some web analytics filters require at least one authorized URL to work.',\n docsUrl: `${POSTHOG_DOCS_URL}/web-analytics/faq`,\n },\n reverse_proxy: {\n title: 'No reverse proxy detected',\n description: 'A reverse proxy reduces data loss from ad blockers.',\n docsUrl: `${POSTHOG_DOCS_URL}/advanced/proxy`,\n },\n web_vitals: {\n title: 'Web Vitals tracking disabled',\n description:\n 'Enable Web Vitals to capture LCP, CLS and other performance metrics.',\n docsUrl: `${POSTHOG_DOCS_URL}/web-analytics/web-vitals`,\n },\n materialized_view_failure: {\n title: 'A materialized view is failing',\n description: 'A data modeling pipeline failed its most recent run.',\n docsUrl: `${POSTHOG_DOCS_URL}/data-warehouse`,\n },\n external_data_failure: {\n title: 'External data source is failing',\n description: 'An external data source sync failed and data may be stale.',\n docsUrl: `${POSTHOG_DOCS_URL}/data-warehouse/sources`,\n },\n};\n\nexport const UNKNOWN_KIND_META: KindMeta = {\n title: 'Unknown issue',\n description:\n 'PostHog reported an issue kind the wizard does not yet recognize.',\n docsUrl: POSTHOG_DOCS_URL,\n};\n\nexport function getKindMeta(kind: string): KindMeta {\n return KIND_METADATA[kind] ?? { ...UNKNOWN_KIND_META, title: kind };\n}\n","import type { ProgramConfig } from '@programs/program-step';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { POSTHOG_DOCTOR_PROGRAM } from './steps.js';\n\nexport const posthogDoctorConfig: ProgramConfig = {\n command: 'doctor',\n description: 'Diagnose your PostHog project setup',\n id: 'posthog-doctor',\n requiresAi: false,\n steps: POSTHOG_DOCTOR_PROGRAM,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n};\n\nexport { POSTHOG_DOCTOR_PROGRAM } from './steps.js';\nexport { fetchHealthIssues } from './fetch.js';\nexport { getKindMeta, KIND_METADATA } from './kind-metadata.js';\nexport type { KindMeta } from './kind-metadata.js';\nexport type {\n HealthIssue,\n HealthIssueSeverity,\n HealthIssueSummary,\n} from './types.js';\n","import { existsSync, statSync } from 'fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\n\nexport type WebAnalyticsDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-package-json' }\n | { kind: 'no-posthog'; scannedCount: number };\n\nexport const WEB_ANALYTICS_ABORT_CASES: AbortCase[] = [\n {\n match: /^no web analytics events$/i,\n message: 'No web analytics events',\n body:\n 'The doctor found no $pageview events in the last 30 days, so there is ' +\n 'nothing to audit yet. Make sure PostHog is initialized and capturing ' +\n 'pageviews, then run the doctor again.',\n docsUrl: 'https://posthog.com/docs/web-analytics/getting-started',\n },\n {\n match: /^insufficient permissions$/i,\n errorCode: ErrorCodes.AuthMissingScope,\n message: 'Insufficient permissions',\n body:\n 'The doctor could not query your project — the authenticated token is ' +\n 'missing query access. Re-run the wizard to sign in again, or use a key ' +\n 'with read access to your events.',\n docsUrl: 'https://posthog.com/docs/web-analytics',\n },\n {\n match: /^posthog sdk not installed$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'PostHog SDK not installed',\n body:\n 'The doctor could not find a PostHog SDK in this project. Install and ' +\n 'configure PostHog first (run `npx @posthog/wizard`), then run the ' +\n 'doctor to check your web analytics setup.',\n docsUrl: 'https://posthog.com/docs/libraries/js',\n },\n];\n\nexport function detectWebAnalyticsPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: WebAnalyticsDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n const matches = findPackageJsons(installDir);\n\n if (matches.length === 0) {\n fail({ kind: 'no-package-json' });\n return;\n }\n\n const sdks = [...new Set(matches.flatMap((m) => m.posthogSdks))];\n\n if (sdks.length === 0) {\n fail({ kind: 'no-posthog', scannedCount: matches.length });\n return;\n }\n\n setFrameworkContext('detectedPosthogSdks', sdks);\n}\n","import type { ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { detectWebAnalyticsPrerequisites } from './detect.js';\n\nexport const WEB_ANALYTICS_DOCTOR_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n onReady: (ctx) =>\n detectWebAnalyticsPrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n ...AGENT_SKILL_STEPS,\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport { createSkillProgram } from '../agent-skill/index.js';\nimport { WEB_ANALYTICS_DOCTOR_PROGRAM } from './steps.js';\nimport { WEB_ANALYTICS_ABORT_CASES } from './detect.js';\n\nconst REPORT_FILE = 'posthog-web-analytics-report.md';\nconst DOCS_URL = 'https://posthog.com/docs/web-analytics';\n\nexport const webAnalyticsDoctorConfig: ProgramConfig = {\n ...createSkillProgram({\n skillId: 'web-analytics-doctor',\n command: 'web-analytics',\n id: 'web-analytics-doctor',\n description: 'Audit and fix your PostHog web analytics setup',\n integrationLabel: 'web-analytics-doctor',\n customPrompt:\n \"Run the web-analytics-doctor skill to check this project's PostHog web \" +\n 'analytics setup. Audit read-only first, then present the findings to the ' +\n 'user with a single wizard_ask multi-select and apply only the fixes they ' +\n 'choose — editing project code and/or PostHog project settings via the ' +\n 'MCP — before writing the report.',\n successMessage:\n 'Web analytics check complete! You can view the report at ./posthog-web-analytics-report.md',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Checking your web analytics setup...',\n estimatedDurationMinutes: 5,\n requires: ['posthog-integration'],\n abortCases: WEB_ANALYTICS_ABORT_CASES,\n }),\n steps: WEB_ANALYTICS_DOCTOR_PROGRAM,\n parentCommand: 'audit',\n};\n\nexport { WEB_ANALYTICS_DOCTOR_PROGRAM } from './steps.js';\nexport {\n detectWebAnalyticsPrerequisites,\n WEB_ANALYTICS_ABORT_CASES,\n type WebAnalyticsDetectError,\n} from './detect.js';\n","import type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const MIGRATION_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'migration-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Migration',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Vendor cost stack — the multi-tool baseline a typical migration target has\n * before consolidating onto PostHog. Numbers from each vendor's published\n * starter pricing.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const VENDOR_STACK_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 600,\n pause: 9000,\n lines: [\n <Text bold>{' Typical pre-migration stack'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color=\"gray\">{' Sentry'}</Text>\n <Text>{' error tracking '}</Text>\n <Text color=\"red\">{'$26/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' LaunchDarkly'}</Text>\n <Text>{' feature flags '}</Text>\n <Text color=\"red\">{'$8.33/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' Amplitude'}</Text>\n <Text>{' product analytics '}</Text>\n <Text color=\"red\">{'$49/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' Braintrust'}</Text>\n <Text>{' AI observability '}</Text>\n <Text color=\"red\">{'$50/mo+'}</Text>\n </Text>,\n <Text color=\"gray\">{' ─────────────────────────────────────'}</Text>,\n <Text>\n <Text>{' Total'}</Text>\n <Text>{' '}</Text>\n <Text bold color=\"red\">\n {'$133/mo+'}\n </Text>\n </Text>,\n <Text dimColor>{' plus ~450KB of JavaScript SDKs'}</Text>,\n ],\n};\n","/**\n * PostHog free-tier highlights — the numbers a migrating team gets back when\n * they consolidate. Sourced from posthog.com/pricing.md.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const FREE_TIER_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 9000,\n lines: [\n <Text bold>{' Free every month, on every product'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>events </Text>\n <Text dimColor>product analytics</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>requests </Text>\n <Text dimColor>feature flags + experiments</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 5,000 '}</Text>\n <Text>recordings </Text>\n <Text dimColor>session replay</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 100,000 '}</Text>\n <Text>exceptions </Text>\n <Text dimColor>error tracking</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 100,000 '}</Text>\n <Text>events </Text>\n <Text dimColor>AI observability</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 50 GB '}</Text>\n <Text>logs </Text>\n <Text dimColor>logs</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,500 '}</Text>\n <Text>responses </Text>\n <Text dimColor>surveys</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>rows </Text>\n <Text dimColor>data warehouse</Text>\n </Text>,\n ],\n};\n","/**\n * Pricing structure block — what happens after the free tier.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PRICING_STRUCTURE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 500,\n pause: 8000,\n lines: [\n <Text bold>{' After the free tier'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color={Colors.accent}>{' $0 '}</Text>\n <Text>base price · pay only for what you use</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>per-event prices decrease with volume</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>no per-seat charges — your whole team is included</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>web analytics bundled with product analytics</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>experiments bundled with feature flags</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>revenue analytics bundled with data warehouse</Text>\n </Text>,\n ],\n};\n","/**\n * Migration learn deck (statsig variant). Statsig is the only `migrate`\n * variant today, so this deck plays as-is when the wizard runs\n * `migrate --product=statsig`. Three movements:\n *\n * 1. Welcome and reassure.\n * 2. What to expect — the migration is replacement-only, takes a few\n * minutes, leaves the build green.\n * 3. What's a little different — how flags and experiments work in\n * PostHog, presented as right-way guidance rather than gotchas.\n *\n * FF/experiments guidance paraphrased from PostHog public docs:\n * - posthog.com/docs/feature-flags/best-practices\n * - posthog.com/docs/feature-flags/common-questions\n * - posthog.com/docs/experiments/best-practices\n */\n\nimport { Text } from 'ink';\nimport type { WizardStore } from '@ui/tui/store';\nimport { Colors } from '@ui/tui/styles';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { PRODUCT_SUITE_BLOCK } from '@ui/tui/decks/posthog-integration/product-suite';\nimport { LINE_CHART_BLOCK } from '@ui/tui/decks/posthog-integration/line-chart';\nimport { FUNNEL_BLOCK } from '@ui/tui/decks/posthog-integration/funnel';\nimport { VENDOR_STACK_BLOCK } from './vendor-stack.js';\nimport { FREE_TIER_BLOCK } from './free-tier.js';\nimport { PRICING_STRUCTURE_BLOCK } from './pricing-structure.js';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => [\n // ── Welcome ────────────────────────────────────────────────────────────\n {\n content: 'Hello.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content:\n 'As we speak, it’s making a plan to migrate from Statsig to PostHog.',\n pause: 6000,\n },\n\n {\n content: 'PostHog covers the cost of running this agent.',\n pause: 4000,\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n persist: true,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n { type: 'clear', pause: 2000 },\n\n // ── What to expect ─────────────────────────────────────────────────────\n { content: 'Here’s what to expect.', pause: 3000 },\n\n { content: 'The migration takes about ten minutes.', pause: 3000 },\n\n {\n content:\n 'Every Statsig call gets replaced in place with its PostHog equivalent.',\n pause: 5500,\n },\n\n {\n content:\n 'Nothing new gets added. No extra captures, no surprise instrumentation.',\n pause: 5500,\n },\n\n {\n content:\n 'The Statsig package gets removed at the end. We’ll run build and lint to clean up after ourselves.',\n pause: 6500,\n },\n\n { type: 'clear', pause: 2000 },\n\n // ── What's a little different ─────────────────────────────────────────\n {\n content: 'A few things work a little differently in PostHog.',\n pause: 4500,\n },\n\n {\n content: (\n <Text>\n Flags evaluate against a stable user. Call{' '}\n <Text bold color={Colors.accent}>\n identify()\n </Text>{' '}\n first, then check the flag.\n </Text>\n ),\n pause: 6000,\n persist: true,\n },\n\n {\n content:\n 'For anything in the first paint, evaluate server-side and bootstrap the values into the client.',\n pause: 6500,\n },\n\n {\n content: (\n <Text>\n In production, route requests through a reverse proxy to avoid ad\n blockers breaking your flags.{'\\n'}\n <Text dimColor>https://posthog.com/docs/advanced/proxy</Text>\n </Text>\n ),\n pause: 6500,\n persist: true,\n },\n\n {\n content:\n 'When a flag reaches 100% rollout, retire it. Flags are signals, not switches.',\n pause: 5500,\n },\n\n {\n content: (\n <Text>\n Name flags descriptively. No double negatives. Reflect the return type.{' '}\n <Text dimColor>For example </Text>\n <Text bold>show-new-checkout</Text>\n <Text dimColor>.</Text>\n </Text>\n ),\n pause: 6500,\n persist: true,\n },\n\n { type: 'clear', pause: 1500 },\n\n // ── Experiments ────────────────────────────────────────────────────────\n {\n content: (\n <Text bold color={Colors.accent}>\n Experiments\n </Text>\n ),\n pause: 2500,\n persist: true,\n },\n\n {\n content:\n 'Change one thing per variant. Multiple changes in one variant blur the result.',\n pause: 5500,\n },\n\n {\n content:\n 'Decide the running time up front. PostHog includes a sample-size and duration calculator in the setup flow.',\n pause: 6500,\n },\n\n {\n content: 'Roll out to 5–10% first. Watch the metrics. Then increase.',\n pause: 5000,\n },\n\n {\n content:\n 'Exclude users who already completed the flow. They can’t be affected by the test.',\n pause: 5500,\n },\n\n { type: 'clear', pause: 1500 },\n\n // ── Close ──────────────────────────────────────────────────────────────\n {\n content: 'Flags and experiments live alongside the rest of your data.',\n pause: 4500,\n },\n\n {\n content: 'Ship behind a flag, watch replays, check analytics for impact.',\n pause: 4500,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content:\n 'PostHog also provides every other analytics and AI tool to build your product.',\n pause: 4500,\n },\n\n PRODUCT_SUITE_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'And consolidating onto one platform saves real money.',\n pause: 4500,\n },\n\n { content: 'Here’s the math.', pause: 1500 },\n\n VENDOR_STACK_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'Pricing is usage-based, with a generous free tier.',\n pause: 4000,\n },\n\n FREE_TIER_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n PRICING_STRUCTURE_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'Gain clarity and really understand your users.',\n pause: 4000,\n },\n\n { content: 'Use trends to measure growth.', pause: 2500 },\n\n LINE_CHART_BLOCK,\n\n { type: 'clear', pause: 500 },\n\n { content: 'Use funnels to reveal bottlenecks.', pause: 2500 },\n\n FUNNEL_BLOCK,\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { AbortCase } from '@agent/types';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { MIGRATION_PROGRAM } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/migration/index.js';\n\nconst MIGRATION_REPORT_FILE = 'migration-report.md';\n\nconst MIGRATION_ABORT_CASES: AbortCase[] = [\n {\n match: /^no source-sdk calls found$/i,\n message: 'No source-SDK calls found',\n body:\n 'The migration needs an existing third-party SDK to migrate from. No ' +\n 'calls to the source SDK appear anywhere in this project. If you ' +\n \"haven't installed PostHog yet, you don't need this command — run \" +\n '`npx @posthog/wizard@latest` to add PostHog from scratch.',\n },\n];\n\n// Default skill id when nothing else picks one. The `wizard migrate <vendor>`\n// subcommands override this via skillCommandFactory using each manifest\n// entry's skillId, so this default only kicks in for legacy callers (e.g.\n// programmatic uses of migrationConfig directly).\nconst DEFAULT_MIGRATE_SKILL_ID = 'migrate-statsig';\n\nexport const migrationConfig: ProgramConfig = {\n command: 'migrate',\n description: 'Migrate to PostHog from another analytics provider',\n id: 'migration',\n skillId: DEFAULT_MIGRATE_SKILL_ID,\n steps: MIGRATION_PROGRAM,\n reportFile: MIGRATION_REPORT_FILE,\n getContentBlocks,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n run: {\n skillId: DEFAULT_MIGRATE_SKILL_ID,\n integrationLabel: 'migration',\n customPrompt: () =>\n 'Migrate this project from its existing third-party analytics, ' +\n 'feature-flag, and observability tools to PostHog. Run the `migrate` ' +\n 'skill end-to-end: follow the step chain starting at ' +\n 'references/1-presence.md. Only replace existing source-SDK call sites ' +\n 'with PostHog equivalents — make zero unrelated changes and no ' +\n `net-new instrumentation. The final report is written to ./${MIGRATION_REPORT_FILE}.`,\n successMessage: `Migration complete! View the report at ./${MIGRATION_REPORT_FILE}`,\n reportFile: MIGRATION_REPORT_FILE,\n docsUrl: '',\n spinnerMessage: 'Migrating to PostHog...',\n estimatedDurationMinutes: 8,\n abortCases: MIGRATION_ABORT_CASES,\n },\n requires: ['posthog-integration'],\n};\n\nexport { MIGRATION_PROGRAM } from './steps.js';\n","/**\n * Source maps upload prerequisite detection.\n *\n * Scans the project for signals that identify the platform and build system,\n * then maps to one of the context-mill `error-tracking-upload-source-maps-*`\n * skill variants. Results are written to frameworkContext for the intro\n * screen to render and for the agent prompt to consume.\n */\n\nimport type { Dirent } from 'fs';\nimport { readdirSync, existsSync, statSync } from 'fs';\nimport { join, relative } from 'path';\nimport {\n IGNORED_DIRS,\n MAX_DIR_ENTRIES,\n MAX_WALK_FILES,\n safeReadFile,\n} from '@utils/bounded-fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\n\n/**\n * Skill variants published under the `error-tracking-upload-source-maps`\n * category in context-mill. The agent loads\n * `error-tracking-upload-source-maps-<variant>`.\n */\nexport type SkillVariant =\n | 'web'\n | 'nextjs'\n | 'node'\n | 'react'\n | 'angular'\n | 'nuxt'\n | 'react-native'\n | 'android'\n | 'flutter'\n | 'ios'\n | 'vite'\n | 'webpack'\n | 'rollup'\n | 'go'\n | 'rust';\n\nconst DISPLAY_NAME: Record<SkillVariant, string> = {\n web: 'Web (JavaScript)',\n nextjs: 'Next.js',\n node: 'Node.js',\n react: 'React',\n angular: 'Angular',\n nuxt: 'Nuxt',\n 'react-native': 'React Native',\n android: 'Android',\n flutter: 'Flutter',\n ios: 'iOS',\n vite: 'Vite',\n webpack: 'Webpack',\n rollup: 'Rollup',\n go: 'Go',\n rust: 'Rust',\n};\n\n/**\n * Variants the wizard can wire up source-map upload for automatically. Go and\n * Rust upload native debug symbols (`posthog-cli symbol-sets upload`) instead\n * of source maps, but the wiring is the same shape.\n *\n * Invariant: every variant listed here must have a published\n * `error-tracking-upload-source-maps-<variant>` skill in context-mill —\n * STEP 2 of the run installs that exact id, so an entry without a shipped\n * skill fails every run for that stack.\n */\nexport const AUTOMATABLE_VARIANTS: readonly SkillVariant[] = [\n 'android',\n 'ios',\n 'react-native',\n 'flutter',\n 'go',\n 'rust',\n 'web',\n 'nextjs',\n 'node',\n 'react',\n 'angular',\n 'nuxt',\n 'vite',\n 'webpack',\n 'rollup',\n];\n\n/**\n * Variants the wizard pre-installs a machine-global `posthog-cli` for — their\n * build shells out to it with no npx / local-dep fallback. Web JS variants stay\n * out. React Native counts as native here — its Xcode build phases and Gradle\n * tasks invoke the global CLI. Flutter counts too despite emitting plain\n * `.js.map` files: a Flutter project has no `package.json`, so there is no npx\n * or local dependency to fall back to when the post-build step invokes the CLI.\n */\nexport const VARIANTS_REQUIRING_POSTHOG_CLI: ReadonlySet<SkillVariant> =\n new Set(['ios', 'android', 'react-native', 'flutter', 'go', 'rust']);\n\n/**\n * Automatable variants whose SDK the wizard's default flow cannot install —\n * remediation for a missing SDK is a manual install (posthog-go /\n * posthog-rs), not `npx @posthog/wizard`. Drives the missing-SDK copy in the\n * picker and the classifier.\n */\nexport const MANUAL_SDK_INSTALL: Partial<Record<SkillVariant, string>> = {\n go: 'add the posthog-go module first',\n rust: 'add the posthog-rs crate first',\n};\n\nexport const MANUAL_SDK_VARIANTS: readonly SkillVariant[] = Object.keys(\n MANUAL_SDK_INSTALL,\n) as SkillVariant[];\n\n/** Dependency string that identifies the Rust SDK in Cargo.toml. */\nexport const RUST_SDK_CRATE = 'posthog-rs';\n\n/** Module path that identifies the Go SDK in go.mod. */\nexport const GO_SDK_MODULE = 'github.com/posthog/posthog-go';\n\nconst POSTHOG_SDKS = new Set([\n 'posthog-js',\n 'posthog-node',\n 'posthog-react-native',\n 'posthog-android',\n 'posthog-ios',\n]);\n\nconst GRADLE_FILES = new Set([\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n]);\n\n/**\n * Structured detection errors. The screen renders each kind into JSX\n * with proper formatting — keeps error data separate from presentation.\n */\nexport type SourceMapsDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-project-files' }\n | { kind: 'unsupported-platform'; detected: string }\n | { kind: 'no-posthog-sdk'; platform: SkillVariant };\n\n/** `[ABORT] <reason>` cases the source maps skill can emit. */\nexport const SOURCE_MAPS_ABORT_CASES: AbortCase[] = [\n {\n match: /^no posthog sdk detected$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'No PostHog SDK detected',\n body:\n 'The agent could not find a PostHog SDK in your project. ' +\n 'Source map upload requires the SDK to already be installed so it can ' +\n 'report errors. Run `npx @posthog/wizard` first to install the SDK ' +\n '(for Go and Rust, add posthog-go / posthog-rs by hand first — the ' +\n 'wizard cannot install the SDK for those stacks yet).',\n docsUrl: 'https://posthog.com/docs/error-tracking',\n },\n {\n match: /^build command not found$/i,\n message: 'Build command not found',\n body:\n 'The agent could not identify how to build your project. Source map ' +\n 'upload runs as part of the production build. Add a build script to ' +\n 'your project and run this wizard again.',\n docsUrl: 'https://posthog.com/docs/error-tracking/upload-source-maps',\n },\n {\n match: /^bare react native not supported$/i,\n errorCode: ErrorCodes.DetectUnsupportedPlatform,\n message: 'Bare React Native is not supported',\n body:\n 'Source-map upload for React Native requires Expo, and this project ' +\n 'has no `expo` package. Bare React Native builds cannot inject the ' +\n 'chunk IDs PostHog needs to resolve stack traces.',\n docsUrl:\n 'https://posthog.com/docs/error-tracking/upload-source-maps/react-native',\n },\n];\n\n// ── File / dependency probes ─────────────────────────────────────────\n\ninterface ProjectSignals {\n packageJsons: Array<{ path: string; deps: Set<string> }>;\n hasXcodeProject: boolean;\n hasPodfile: boolean;\n hasSwiftPackage: boolean;\n hasGradle: boolean;\n hasPubspec: boolean;\n scannedFileCount: number;\n}\n\nfunction collectSignals(installDir: string, maxDepth = 3): ProjectSignals {\n const signals: ProjectSignals = {\n packageJsons: [],\n hasXcodeProject: false,\n hasPodfile: false,\n hasSwiftPackage: false,\n hasGradle: false,\n hasPubspec: false,\n scannedFileCount: 0,\n };\n\n // Explicit stack — no call-stack recursion.\n const stack: Array<{ dir: string; depth: number }> = [\n { dir: installDir, depth: 0 },\n ];\n\n while (stack.length > 0 && signals.scannedFileCount < MAX_WALK_FILES) {\n const { dir, depth } = stack.pop()!;\n\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n // A directory contributes at most MAX_DIR_ENTRIES entries.\n if (entries.length > MAX_DIR_ENTRIES) entries.length = MAX_DIR_ENTRIES;\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') && entry.name !== '.') continue;\n if (IGNORED_DIRS.has(entry.name)) continue;\n\n const fullPath = join(dir, entry.name);\n\n if (entry.isFile()) {\n signals.scannedFileCount += 1;\n if (entry.name === 'package.json') {\n const content = safeReadFile(fullPath);\n if (content === null) continue;\n try {\n const pkg = JSON.parse(content) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n signals.packageJsons.push({\n path: relative(installDir, fullPath) || 'package.json',\n deps,\n });\n } catch {\n // skip malformed package.json\n }\n } else if (entry.name === 'Podfile') {\n signals.hasPodfile = true;\n } else if (entry.name === 'Package.swift') {\n signals.hasSwiftPackage = true;\n } else if (entry.name === 'pubspec.yaml') {\n signals.hasPubspec = true;\n } else if (GRADLE_FILES.has(entry.name)) {\n signals.hasGradle = true;\n }\n } else if (entry.isDirectory()) {\n if (entry.name.endsWith('.xcodeproj')) {\n signals.hasXcodeProject = true;\n } else if (depth < maxDepth) {\n stack.push({ dir: fullPath, depth: depth + 1 });\n }\n }\n }\n }\n\n return signals;\n}\n\n// ── Skill selection ──────────────────────────────────────────────────\n\nfunction pickJsVariant(deps: Set<string>): SkillVariant {\n // Opinionated full-stack frameworks first — they own their build pipeline\n // and have dedicated skill variants, so bundler detection underneath\n // them is irrelevant.\n if (deps.has('react-native')) return 'react-native';\n if (deps.has('nuxt')) return 'nuxt';\n if (deps.has('next')) return 'nextjs';\n if (deps.has('@angular/core')) return 'angular';\n // Bundlers next — prefer these over the bare `react` variant because\n // their skills are simpler (one bundler-plugin config) than wiring\n // posthog-cli into an arbitrary React setup.\n if (deps.has('vite')) return 'vite';\n if (deps.has('webpack')) return 'webpack';\n if (deps.has('rollup')) return 'rollup';\n // Plain React with no recognised bundler.\n if (deps.has('react')) return 'react';\n // Server-only Node project\n if (deps.has('posthog-node')) return 'node';\n // Fallback: generic web\n return 'web';\n}\n\nfunction selectVariant(signals: ProjectSignals): SkillVariant | null {\n // Mobile / native first — they don't coexist with JS bundlers in the\n // detection signals we look at.\n if (signals.hasPubspec) return 'flutter';\n if (signals.hasXcodeProject || signals.hasPodfile || signals.hasSwiftPackage)\n return 'ios';\n if (signals.hasGradle) return 'android';\n\n if (signals.packageJsons.length > 0) {\n // Union all deps across package.json files (covers monorepos)\n const allDeps = new Set<string>();\n for (const pkg of signals.packageJsons) {\n for (const dep of pkg.deps) allDeps.add(dep);\n }\n return pickJsVariant(allDeps);\n }\n\n return null;\n}\n\nfunction hasPostHogSdk(signals: ProjectSignals): boolean {\n for (const pkg of signals.packageJsons) {\n for (const sdk of POSTHOG_SDKS) {\n if (pkg.deps.has(sdk)) return true;\n }\n }\n // For native platforms the PostHog SDK lives outside package.json and\n // is detected by the agent during the skill run. Assume present here.\n return (\n signals.hasXcodeProject ||\n signals.hasPodfile ||\n signals.hasSwiftPackage ||\n signals.hasGradle ||\n signals.hasPubspec\n );\n}\n\n// ── Entry point ──────────────────────────────────────────────────────\n\nexport const SOURCE_MAPS_CONTEXT_KEYS = {\n skillVariant: 'sourceMapsSkillVariant',\n displayName: 'sourceMapsDisplayName',\n packagePaths: 'sourceMapsPackagePaths',\n detectError: 'detectError',\n // Set by the agentic picker once the user chooses a project to instrument.\n selectedVariant: 'sourceMapsSelectedVariant',\n selectedDisplayName: 'sourceMapsSelectedDisplayName',\n selectedPath: 'sourceMapsSelectedPath',\n} as const;\n\n/**\n * Scan `session.installDir` for platform / build-system signals. Writes\n * detection results into frameworkContext via the callback — either the\n * picked skill variant + display name, or a `SourceMapsDetectError`.\n *\n * The skill install happens later in the agent run, not here. This step\n * only picks which variant the prompt should ask the agent to load.\n */\nexport function detectSourceMapsPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: SourceMapsDetectError) =>\n setFrameworkContext(SOURCE_MAPS_CONTEXT_KEYS.detectError, error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n const signals = collectSignals(installDir);\n const variant = selectVariant(signals);\n\n // The legacy filesystem detector does not automate native platforms. The\n // live source-map picker uses detectSourceMapsProjects instead.\n if (\n variant &&\n ['react-native', 'flutter', 'ios', 'android'].includes(variant)\n ) {\n fail({ kind: 'unsupported-platform', detected: variant });\n return;\n }\n\n if (!variant) {\n if (signals.scannedFileCount === 0) {\n fail({ kind: 'no-project-files' });\n } else {\n fail({ kind: 'unsupported-platform', detected: 'unknown' });\n }\n return;\n }\n\n if (!hasPostHogSdk(signals)) {\n fail({ kind: 'no-posthog-sdk', platform: variant });\n return;\n }\n\n setFrameworkContext(SOURCE_MAPS_CONTEXT_KEYS.skillVariant, variant);\n setFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.displayName,\n DISPLAY_NAME[variant],\n );\n setFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.packagePaths,\n signals.packageJsons.map((p) => p.path),\n );\n}\n\nexport { DISPLAY_NAME as VARIANT_DISPLAY_NAME };\n","/**\n * Error tracking source maps upload program step list.\n *\n * Flow: a static intro (no detection yet) → login → an agentic detect+pick\n * screen that scans the repo on Haiku and lets the user choose a project →\n * agent run → outro. Detection runs after auth because the detection agent\n * needs credentials.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { SOURCE_MAPS_CONTEXT_KEYS } from './detect.js';\n\nfunction projectSelected(session: WizardSession): boolean {\n return (\n session.frameworkContext[SOURCE_MAPS_CONTEXT_KEYS.selectedVariant] != null\n );\n}\n\nexport const ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'source-maps-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'detect',\n label: 'Detecting projects',\n // The Haiku agent scans the repo, surfaces an instrumentable / not-yet map,\n // and the user picks the project to wire up. Advances once a project is\n // chosen (its variant is written to frameworkContext). The gate lets the\n // agent runner park after auth until the pick lands, so the run prompt sees\n // the chosen variant.\n screenId: 'source-maps-detect',\n isComplete: projectSelected,\n gate: projectSelected,\n },\n {\n id: 'run',\n label: 'Upload source maps',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'source-maps-outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","import { AgentSignals } from '@agent';\nimport type { SkillVariant } from './detect.js';\n\nexport type SourceMapsUploadPromptParams = {\n displayName: string | undefined;\n variant: SkillVariant;\n skillId: string;\n /** Project to instrument, relative to the wizard working directory (\".\" or undefined = root). */\n projectPath?: string;\n projectId: number;\n host: string;\n settingsUrl: string;\n uiHost: string;\n /** Hand-off report the agent writes in STEP 9; the outro points the user at it. */\n reportFile: string;\n};\n\nexport const SOURCE_MAPS_DETECTION_FAILED_PROMPT = `Detection did not pick a source maps skill variant for this project.\nEmit: ${AgentSignals.ABORT} unsupported-platform\nThen halt.`;\n\nexport function buildSourceMapsUploadPrompt(\n params: SourceMapsUploadPromptParams,\n): string {\n const {\n displayName,\n variant,\n skillId,\n projectPath,\n projectId,\n host,\n settingsUrl,\n uiHost,\n reportFile,\n } = params;\n const platformLabel = displayName ?? variant;\n const inSubproject = projectPath != null && projectPath !== '.';\n const projectLine = inSubproject\n ? `- Project directory (relative to the wizard's working directory): ${projectPath}`\n : \"- Project directory: the wizard's working directory (even when it sits inside a larger git repo, this directory is the project root)\";\n const envFilePathGuidance = inSubproject\n ? `Tool filePaths are relative to the wizard's working directory, not the selected project directory. Treat the skill's env path as relative to the selected project and prefix it with \\`${projectPath}/\\` when calling the tools: for example, pass \\`${projectPath}/.env\\`, not \\`.env\\` (which would target the wizard's working directory).${\n variant === 'rust'\n ? ` Cargo workspace exception: when the skill places the env file at the workspace root instead, pass the path of the ROOT manifest's directory relative to the wizard's working directory (e.g. \\`rust/.env\\` for a workspace root at \\`rust/\\`, or plain \\`.env\\` when the workspace root IS the working directory) — not the member path.`\n : ''\n }`\n : `Tool filePaths are relative to the wizard's working directory. For an env file at this project root, pass \\`.env\\`; never prefix it with this directory's path inside an ancestor repository.`;\n\n const credentialSteps = `STEP 4 — Make the credentials readable at build time. (skill: \"Make credentials available at build time\")\n Follow the skill's step. Wizard-specific: if it calls for a loader (e.g.\n \\`dotenv\\`), install it SILENTLY — do NOT ask the user or call wizard_ask.\n Skip this step entirely if the platform already auto-loads .env.\n\nSTEP 5 — Write the credentials to the env file. (skill: \"Write credentials to the env file\")\n Use the wizard-tools MCP server. Reuse the env file the skill tells you to\n pick — the prerequisite PostHog integration usually already wrote\n POSTHOG_* vars to one, so seed your keys alongside them.\n - First call check_env_keys with the key names and that file as filePath.\n It answers { status: \"present\" | \"missing\", foundIn: [paths] } per key —\n names and paths only, never values, so don't read the file directly.\n \"present\" means a real env file sets the key; a key found only in a\n committed template (.env.example and friends) reads as \"missing\",\n because a template documents a key rather than setting it.\n - Env tool path rule: ${envFilePathGuidance}\n - Then call set_env_values, passing the STEP 1 secretRef as a value\n object, not a literal string. Take the three variable NAMES from the\n skill's per-uploader list in \"Write credentials to the env file\" — they\n differ by uploader, and only the shape below is fixed:\n values: {\n \"<key var for the uploader you wired>\": { secretRef: \"<the ref from STEP 1>\" },\n \"<project id var>\": \"${projectId}\",\n \"<host var>\": \"${uiHost}\"\n }\n Write that one set only. Do not also write another uploader's variables\n as a fallback — a credential nothing reads is an unused secret sitting\n in the user's env file.\n The wizard resolves the ref locally before writing, so you never see the\n key value.`;\n\n return `You are wiring up PostHog Error Tracking source map upload for this ${platformLabel} project.\n\nProject context:\n- PostHog Project ID: ${projectId}\n- PostHog Host: ${host}\n- Detected platform: ${platformLabel}\n- Skill to use: ${skillId}\n${projectLine}\n- Personal API keys settings page: ${settingsUrl}\n\nAll file changes, build/run commands, and config edits target the project directory above${\n inSubproject\n ? ` — this is a monorepo, so scope your work to \\`${projectPath}\\` and do not touch other packages`\n : ''\n }.${\n variant === 'rust' && inSubproject\n ? `\nCargo workspace exception: if the selected project is a member of a Cargo\nworkspace (resolve the root with \\`cargo locate-project --workspace\n--message-format plain\\`), the workspace root's Cargo.toml (for \\`[profile.*]\\`\nedits), the workspace-level \\`target/\\` directory, and a \\`.env\\` next to the\nroot manifest are IN SCOPE — treat them as part of the selected project, and\nfollow the skill's workspace guidance for which paths to use. Every other\npackage stays off-limits.`\n : ''\n }\n\nThe skill you install in STEP 2 is the source of truth for the HOW of every\nstep: its \"## Steps\" section has an overview, tips and per-technology\nexamples for each named step, and its reference files carry the exact\nper-framework API. The STEPS below give the order, the conditionals, and the\nwizard-specific mechanics (which MCP tool to call, signals to emit) — read\nthe matching skill step (named in parentheses) before doing the work, and do\nnot invent steps the skill doesn't describe.\n\nFollow these steps IN ORDER. Do not skip or reorder.\n\nYour FIRST message must contain ONLY parallel tool calls, in this order:\n - the STEP 1 wizard_ask call FIRST — tool calls execute as they stream,\n so this puts the API-key prompt in front of the user within seconds —\n then\n - one TaskCreate call PER task below, all in this same message (the tool\n takes a single task per call). The TUI shows only the subject, so keep\n every description to a few words — never a sentence.\nDo not read files, explore the project, or write any text first, and keep\nany thinking before the calls to a single short sentence.\n\nUse exactly these tasks, in this order — do not collapse, rename, or omit\nany of them. Getting the API key is NOT a task — its prompt is already on\nscreen by the time the list renders:\n 1. Install source maps skill\n 2. Apply build-config changes (per skill)\n 3. Make credentials readable at build time\n 4. Write keys to .env\n 5. Identify build & run commands\n 6. Set up CI for auto-upload\n 7. Test the local setup\n 8. Summarise & hand off\nDrive the list with TaskUpdate — mark a task in_progress when you start it\nand completed when done. ALWAYS keep task 7 (\"Test the local setup\") in the\nlist even if the user declines it in STEP 8: mark it completed rather than\ndeleting it, so the user can see it was offered.\n\nSTEP 1 — Get a personal API key from the user. (skill: \"Get a personal API key\")\n This wizard_ask call ships in your FIRST message, per the rule above.\n The wizard cannot mint keys — never call the PostHog API or any tool to\n create one. Ask the user with the wizard_ask MCP tool; you receive the\n answer as a vaulted secretRef (never the raw value), which you reuse in\n STEP 5:\n {\n id: \"api-key\",\n prompt: \"Paste your PostHog personal API key below.\\\\n\\\\nDon't have one yet? Create one here:\\\\n${settingsUrl}\\\\n\\\\nWhen creating the key, choose the 'Source map upload' preset, then come back and paste it here.\",\n kind: \"text\",\n sensitive: true\n }\n Keep the \\\\n line breaks exactly as written — Ink's <Text> renders them\n as separate lines. The answer is { secretRef: \"secret:...\" }.\n If wizard_ask is unavailable (CI / non-interactive), emit\n ${AgentSignals.ABORT} requires-interactive-mode and halt.\n\nSTEP 2 — Install the skill.\n Call install_skill (wizard-tools MCP server) with skillId \"${skillId}\".\n Do NOT run shell commands to install skills. Then read the installed\n SKILL.md and its reference files — they drive STEPS 3-9.\n If install fails, emit ${\n AgentSignals.ERROR_RESOURCE_MISSING\n } skill ${skillId} could not be installed.\n\nSTEP 3 — Apply build-config changes. (skill: \"Apply build-config changes\")\n Make the bundler / build-config changes the skill's step instructs. The\n skill and its reference are the source of truth for this platform.\n\n${credentialSteps}\n\nSTEP 6 — Identify the build AND run commands. (skill: \"Identify the build and run commands\")\n Per the skill, resolve the production BUILD command and the RUN command\n for THIS project (use detect_package_manager for the package manager). Do\n NOT run either yourself — the user runs them. If you cannot identify a\n build command, emit ${AgentSignals.ABORT} build command not found.\n\nSTEP 7 — Set up CI for automatic uploads. (skill: \"Set up CI for automatic uploads\")\n Source maps only upload when the production build runs, so the build's\n CI/CD must carry the same upload credentials you wrote in STEP 5. Do this\n step without asking — there is no opt-in question for it. Follow the\n skill's \"Set up CI for automatic uploads\" step — it is the source of\n truth for tracing where the production build runs and wiring the\n credentials through every layer, whatever the CI provider.\n Wizard-specific rules on top:\n - Trace the deploy path by reading the project's files — do NOT ask the\n user, and do NOT invent config that isn't there.\n - Carry every manual follow-up the skill has you hand off (secrets the\n user must create, an untraceable build path) into STEP 9.\n\nSTEP 8 — Offer to test the local setup. (skill: \"Test the local setup\")\n Call wizard_ask:\n {\n id: \"test-affordance\",\n prompt: \"Want me to help you test your local setup? I'll add a temporary test button (or route) to your app so you can confirm errors show up in Error Tracking with readable stack traces after your next build. I'll remove it once you've confirmed it works.\",\n kind: \"single\",\n options: [\n { label: \"Yes, help me test it\", value: \"yes\" },\n { label: \"No, I'll test on my own later\", value: \"no\" }\n ]\n }\n\n If \"no\", skip to STEP 9.\n\n If \"yes\", follow the skill's \"Test the local setup\" step for the\n platform-appropriate affordance, the captureException shape, the\n placement, and the read-before-edit / always-revert rules. Then pause for\n the user with wizard_ask, baking the build-and-run flow (and the exact\n button label / route) into the prompt as literal, copy-pasteable\n numbered steps:\n - Steps 1-2: the production build, then launching the app and\n triggering the test affordance. Source them from STEP 6 and the\n skill's \"Test the local setup\" step — when the skill gives the\n platform's test flow as verbatim steps (IDE-driven platforms like\n Xcode do), use its wording and do NOT invent CLI build commands,\n debugger steps, or relaunch steps it doesn't state. Quote CLI\n commands verbatim. When build and run are one action (an IDE Run),\n fold them into step 1 and make step 2 just triggering the affordance.\n - The last step is always the Error Tracking check, exactly as in the\n template.\n Separate each numbered step with \\\\n\\\\n so the TUI renders them as\n distinct lines:\n {\n id: \"test-done\",\n prompt: \"1) <production build step — it uploads source maps and builds the app with the test affordance>\\\\n\\\\n2) <run step>, then click the \\\\\"<your test button label>\\\\\" button (or hit \\`<your test route>\\`).\\\\n\\\\n3) Open Error Tracking in PostHog (${uiHost}/project/${projectId}/error_tracking) and confirm the test error appears with a source-resolved stack trace pointing at real source files (not minified bundle paths).\\\\n\\\\nWhen you're done, select Continue and I'll revert the test code.\",\n kind: \"single\",\n options: [{ label: \"Continue (revert test code)\", value: \"continue\" }]\n }\n After the user continues, revert the test code per the skill's rules and\n surface any failure in STEP 9.\n\nSTEP 9 — Summarise and hand off. (skill: \"Verify and hand off\")\n Follow the skill's \"Verify and hand off\" step. Write the hand-off to\n \\`${reportFile}\\` at the WIZARD'S WORKING DIRECTORY — pass exactly\n \\`${reportFile}\\` as the file path, never prefixed with the selected\n project directory; this file is the one exception to the project-scope\n rule above. Cover: the files you changed (paths only), the exact build\n and upload commands, every CI secret the user still has to create, and\n how to verify the upload — then give the same summary in chat. Never\n write secret values into the report, only variable names. The success\n screen points the user at this file, so do not skip it. The Symbol sets page for this project — where the user\n confirms the upload landed — is:\n ${uiHost}/project/${projectId}/error_tracking/configuration\n`;\n}\n","/**\n * Source-maps learn-deck — the narrative played in the run screen's left\n * pane (LearnCard) while the agent wires source-map upload into the build.\n *\n * It educates the user on what source maps are and why uploading them\n * matters, built around a before/after stack-trace contrast: a minified\n * production trace nobody can read, then the same trace resolved back to\n * real source. Program-owned; wired onto the program's getContentBlocks.\n *\n * Lines stay narrow (~36 cols) because this renders in the left half of a\n * split pane — see LearnCard's paneWidth math.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport {\n isClearBlock,\n type ContentBlock,\n} from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\n\n/**\n * Per-slide dwell multiplier. Each block stays on screen for `pause * SLIDE_PACE`\n * ms after it finishes animating, before the deck advances. Bump this single\n * knob to give every slide more reading time. Clear (page-break) blocks are\n * left untouched so the blank gap between slides stays snappy.\n */\nconst SLIDE_PACE = 1.5;\n\nconst withPace = (block: ContentBlock): ContentBlock => {\n if (typeof block === 'string' || isClearBlock(block) || block.pause == null) {\n return block;\n }\n return { ...block, pause: Math.round(block.pause * SLIDE_PACE) };\n};\n\n/** Apply the dwell multiplier to every block in a deck. */\nconst pace = (blocks: ContentBlock[]): ContentBlock[] => blocks.map(withPace);\n\n/**\n * A minified production stack trace — the problem source maps solve. Framed as\n * a labelled, muted example (no error-red ✘) so a glance reads it as\n * illustrative content, not as the wizard itself having errored mid-run.\n */\nconst MINIFIED_TRACE: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 7000,\n lines: [\n <Text dimColor>{'example — minified production trace'}</Text>,\n <Text color={Colors.muted}>{' TypeError: cart is undefined'}</Text>,\n <Text dimColor>{' at t.min.js:1:48213'}</Text>,\n <Text dimColor>{' at t.min.js:1:9402'}</Text>,\n <Text dimColor>{' at t.min.js:1:71150'}</Text>,\n ],\n};\n\n/** The same trace, resolved through uploaded source maps. */\nconst RESOLVED_TRACE: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 8000,\n lines: [\n <Text dimColor>{'example — resolved with source maps'}</Text>,\n <Text color={Colors.success}>{' ✔ TypeError: cart is undefined'}</Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">Cart.tsx:42</Text>\n <Text dimColor>{' loadCart'}</Text>\n </Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">App.tsx:88</Text>\n <Text dimColor>{' render'}</Text>\n </Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">index.tsx:5</Text>\n <Text dimColor>{' main'}</Text>\n </Text>,\n ],\n};\n\n/**\n * How a bundle is tied to its map: PostHog injects a chunk-ID marker into the\n * built JS and stamps the matching source map with the same ID.\n */\nconst CHUNK_ID_LINK: ContentBlock = {\n type: 'lines',\n interval: 450,\n pause: 7500,\n lines: [\n <Text dimColor>app.min.js</Text>,\n <Text dimColor>{' …minified code…'}</Text>,\n <Text>\n <Text color=\"cyan\">{' //# chunkId=a1b2c3d4'}</Text>\n <Text dimColor>{' ← injected'}</Text>\n </Text>,\n <Text dimColor>{' ↕ matched by id'}</Text>,\n <Text>\n <Text dimColor>app.min.js.map</Text>\n <Text dimColor>{' ← uploaded'}</Text>\n </Text>,\n ],\n};\n\n/** Many similar exceptions collapse into a single issue. */\nconst GROUPING: ContentBlock = {\n type: 'lines',\n interval: 450,\n pause: 7000,\n lines: [\n <Text dimColor>{'exception ─┐'}</Text>,\n <Text>\n <Text dimColor>{'exception ─┼──→ '}</Text>\n <Text color={Colors.accent} bold>\n 1 issue\n </Text>\n </Text>,\n <Text dimColor>{'exception ─┘'}</Text>,\n ],\n};\n\n/** The two lines that say what the run wires. The rest of the deck is shared. */\nexport type SourceMapsDeckCopy = { intro: string; wiring: string };\n\n/** The source-maps narrative. The error-tracking program reuses it with its own copy. */\nexport const buildSourceMapsDeck = (\n store: WizardStore | undefined,\n copy: SourceMapsDeckCopy,\n): ContentBlock[] =>\n pace([\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n {\n content: copy.intro,\n pause: 5000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'When you ship to production, your code gets minified.',\n pause: 5000,\n },\n {\n content: 'Thousands of readable lines collapse into one dense bundle.',\n pause: 5000,\n },\n {\n content: 'So a thrown error gives you a stack trace like this:',\n pause: 2000,\n },\n\n MINIFIED_TRACE,\n\n { content: 'Just offsets into a file no human can read.', pause: 5000 },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'Source maps are the key.', pause: 3500 },\n {\n content:\n 'They map every position in that bundle back to your original source — the real file, line, and function.',\n pause: 6000,\n },\n {\n content: copy.wiring,\n pause: 6000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'But how does PostHog know which map belongs to which build?',\n pause: 4500,\n },\n {\n content:\n 'During the build, it injects a unique chunk ID into each bundle:',\n pause: 2500,\n },\n\n CHUNK_ID_LINK,\n\n {\n content:\n 'The matching source map is stamped with that same ID before it ships to PostHog.',\n pause: 6000,\n },\n {\n content:\n 'When an error comes in, PostHog reads the chunk ID off the bundle, finds the map with the exact same ID, and uses it to map each frame back to your source — even for a release you shipped weeks ago.',\n pause: 8000,\n },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'So that same error becomes:', pause: 2000 },\n\n RESOLVED_TRACE,\n\n {\n content: 'Readable stack traces, straight from production.',\n pause: 5000,\n },\n {\n content: 'You debug a live error like it happened on your own machine.',\n pause: 6000,\n },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'Zooming out — this is how Error Tracking works.', pause: 4000 },\n {\n content: 'Every error your app throws is captured as an exception.',\n pause: 5000,\n },\n {\n content: 'PostHog groups similar exceptions into a single issue:',\n pause: 2500,\n },\n\n GROUPING,\n\n {\n content:\n 'So a bug that fires ten thousand times is one issue to triage — not ten thousand alerts.',\n pause: 6500,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'And this is where source maps earn their keep again.',\n pause: 4500,\n },\n {\n content:\n 'Grouping reads the stack trace. Minified frames all look alike — so unrelated crashes get merged, and one real bug scatters across many issues.',\n pause: 8000,\n },\n {\n content:\n 'With source maps, PostHog groups on your real frames — so each distinct bug lands as one clean issue.',\n pause: 7000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n {\n pause: 90000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to follow along — or sit tight, I'll let you know when it's done.\n </Text>\n ),\n },\n ]);\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] =>\n buildSourceMapsDeck(store, {\n intro: \"I'm wiring PostHog Error Tracking into your build.\",\n wiring:\n \"Right now I'm hooking source-map generation and upload into your build, tied to each release you ship.\",\n });\n","import { spawnSync } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { debug } from '@utils/debug';\n\n/**\n * A coding agent whose global instructions file the PostHog CLI steering\n * snippet can be installed into. Project-level installs are handled by\n * `posthog-cli api agents-md install` in the target repo; this step only\n * manages the per-user (global) instruction files.\n */\nexport interface CliSteeringTarget {\n id: string;\n name: string;\n /** Global agent instructions file the steering snippet is written to. */\n instructionsPath(): string;\n /** Whether the agent appears to be installed on this machine. */\n isDetected(): boolean;\n}\n\nconst dirExists = (...segments: string[]): boolean => {\n try {\n return fs.existsSync(path.join(os.homedir(), ...segments));\n } catch {\n return false;\n }\n};\n\nexport const CLI_STEERING_TARGETS: readonly CliSteeringTarget[] = [\n {\n id: 'claude-code',\n name: 'Claude Code',\n instructionsPath: () => path.join(os.homedir(), '.claude', 'CLAUDE.md'),\n isDetected: () => dirExists('.claude'),\n },\n {\n id: 'codex',\n name: 'Codex',\n instructionsPath: () => path.join(os.homedir(), '.codex', 'AGENTS.md'),\n isDetected: () => dirExists('.codex'),\n },\n {\n id: 'gemini',\n name: 'Gemini CLI',\n instructionsPath: () => path.join(os.homedir(), '.gemini', 'GEMINI.md'),\n isDetected: () => dirExists('.gemini'),\n },\n];\n\nexport function findTarget(id: string): CliSteeringTarget | undefined {\n return CLI_STEERING_TARGETS.find((target) => target.id === id);\n}\n\nexport function detectTargets(): CliSteeringTarget[] {\n return CLI_STEERING_TARGETS.filter((target) => target.isDetected());\n}\n\nexport interface SteeringInstallResult {\n success: boolean;\n filePath: string;\n error?: string;\n}\n\nexport interface CliInstallResult {\n success: boolean;\n error?: string;\n /**\n * The underlying failure as an Error, for callers that report to error\n * tracking. Carries the real spawn error (with its stack) when npm couldn't\n * launch; a synthesized Error for a non-zero exit. Set whenever success is\n * false.\n */\n errorObject?: Error;\n}\n\nconst spawnOptions = {\n encoding: 'utf-8' as const,\n // npm/posthog-cli are npm.cmd/posthog-cli.cmd on Windows; spawnSync only\n // resolves them through a shell.\n shell: process.platform === 'win32',\n};\n\n/**\n * Install or update the PostHog CLI in the user's environment. `npm install\n * --global @posthog/cli@latest` covers both first-time installs and upgrades\n * for existing npm-installed CLIs.\n */\nexport function installOrUpdatePostHogCli(): CliInstallResult {\n const args = ['install', '--global', '@posthog/cli@latest'];\n debug(`Running npm ${args.join(' ')}`);\n\n const result = spawnSync('npm', args, spawnOptions);\n\n if (result.error) {\n return {\n success: false,\n error: `Failed to run npm: ${result.error.message}. Is Node.js installed?`,\n errorObject: result.error,\n };\n }\n if (result.status !== 0) {\n const detail = (result.stderr || result.stdout || '').trim();\n const message =\n detail ||\n `npm install --global @posthog/cli@latest exited with status ${\n result.status ?? 'unknown'\n }`;\n return {\n success: false,\n error: message,\n errorObject: new Error(message),\n };\n }\n return { success: true };\n}\n\n/**\n * Delegate the actual write to the installed `posthog-cli api agents-md\n * install`. The steering snippet lives in the CLI (its single source of truth),\n * so the command should run only after `installOrUpdatePostHogCli` refreshes\n * the CLI to the latest release.\n */\nexport function installSteeringSnippet(\n filePath: string,\n): SteeringInstallResult {\n const args = ['api', 'agents-md', 'install', '--path', filePath];\n debug(`Running posthog-cli ${args.join(' ')}`);\n\n const result = spawnSync('posthog-cli', args, {\n ...spawnOptions,\n env: { ...process.env, POSTHOG_CLI_EXPERIMENTAL_API: '1' },\n });\n\n if (result.error) {\n return {\n success: false,\n filePath,\n error: `Failed to run posthog-cli: ${result.error.message}. Make sure npm's global bin directory is on your PATH.`,\n };\n }\n if (result.status !== 0) {\n const detail = (result.stderr || result.stdout || '').trim();\n return {\n success: false,\n filePath,\n error:\n detail ||\n `posthog-cli exited with status ${result.status ?? 'unknown'}`,\n };\n }\n return { success: true, filePath };\n}\n","/**\n * Machine-global posthog-cli pre-install, shared by the programs whose symbol\n * upload shells out to it (error-tracking and error-tracking-upload-source-maps).\n *\n * The agent cannot install a global package — warlock blocks `npm install -g`\n * — so the wizard does it in-process, at most once per process. Warn, don't\n * fail: the run still instruments capture, and only the release build's upload\n * needs the CLI.\n */\n\nimport type { RunnerContext } from '@programs/runner-context';\nimport { installOrUpdatePostHogCli } from '@steps/install-cli-steering';\nimport { analytics } from '@utils/analytics';\n\nlet attempted = false;\n\nexport function preinstallPostHogCliOnce(\n failureEvent: string,\n properties: Record<string, string>,\n log: RunnerContext['log'],\n): void {\n if (attempted) return;\n attempted = true;\n\n const result = installOrUpdatePostHogCli();\n if (result.success) return;\n\n // No npm, or no permission for a global install, is the user's environment,\n // not a wizard bug: an event, never captureException, which would mint an\n // error-tracking issue per machine.\n analytics.wizardCapture(failureEvent, {\n ...properties,\n error: String(result.error).slice(0, 500),\n });\n log.warn(\n `Could not pre-install posthog-cli (${result.error}). Your release build ` +\n `will fail to upload debug symbols until it's installed: npm install -g @posthog/cli@latest`,\n );\n}\n\n/** Test seam: forget that the install was attempted. */\nexport function resetPostHogCliPreinstallForTests(): void {\n attempted = false;\n}\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport type { RunnerContext } from '@programs/runner-context';\nimport { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';\nimport {\n buildSourceMapsUploadPrompt,\n SOURCE_MAPS_DETECTION_FAILED_PROMPT,\n} from './prompt.js';\nimport {\n SOURCE_MAPS_ABORT_CASES,\n SOURCE_MAPS_CONTEXT_KEYS,\n VARIANTS_REQUIRING_POSTHOG_CLI,\n type SkillVariant,\n} from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/error-tracking-upload-source-maps/index.js';\nimport { preinstallPostHogCliOnce } from '@programs/shared/posthog-cli-preinstall';\n\nconst REPORT_FILE = 'posthog-source-maps-report.md';\nconst DOCS_URL = 'https://posthog.com/docs/error-tracking/upload-source-maps';\n\n/**\n * Pre-install posthog-cli for variants that need a machine-global copy\n * (`VARIANTS_REQUIRING_POSTHOG_CLI`). See `preinstallPostHogCliOnce` for the\n * once-per-process guard and the warn-don't-fail handling.\n */\nfunction ensurePostHogCli(\n variant: SkillVariant,\n log: RunnerContext['log'],\n): void {\n preinstallPostHogCliOnce(\n 'source maps posthog-cli preinstall failed',\n { variant },\n log,\n );\n}\n\nexport const errorTrackingUploadSourceMapsConfig: ProgramConfig = {\n command: 'upload-source-maps',\n description: 'Upload source maps to PostHog Error Tracking',\n id: 'error-tracking-upload-source-maps',\n requiresAi: true,\n steps: ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM,\n reportFile: REPORT_FILE,\n getContentBlocks,\n requires: ['posthog-integration'],\n\n run: (\n _session: WizardSession,\n runner: RunnerContext,\n ): Promise<ProgramRun> => {\n // Read the picked project LIVE at prompt-build time, not here: the picker\n // screen runs AFTER this run config is resolved (post-auth), and the store\n // forks the session reference, so the `session` passed in never sees the\n // choice. The runner reads the live store session.\n const readSelection = () => {\n const variant = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedVariant,\n ) as SkillVariant | undefined;\n const displayName = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedDisplayName,\n ) as string | undefined;\n const projectPath = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedPath,\n ) as string | undefined;\n const skillId = variant\n ? `error-tracking-upload-source-maps-${variant}`\n : undefined;\n return { variant, displayName, projectPath, skillId };\n };\n\n return Promise.resolve({\n integrationLabel: 'error-tracking-upload-source-maps',\n // Skill is installed by the agent (after the API-key choice is made)\n // rather than pre-installed by the runner, so leave skillId unset.\n successMessage: 'Source maps wired up!',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Wiring up source maps...',\n estimatedDurationMinutes: 3,\n abortCases: SOURCE_MAPS_ABORT_CASES,\n // The flow parks on wizard_ask while the user does slow work — create\n // a personal API key in the browser (STEP 1), or run a production\n // build, trigger the test error, and check Error Tracking (STEP 8).\n // The 5-minute default cancels the question mid-task and the agent\n // wraps up to the outro, so give these answers half an hour.\n askTimeoutMs: 30 * 60 * 1000,\n\n customPrompt: (ctx) => {\n const { variant, displayName, projectPath, skillId } = readSelection();\n if (!skillId || !variant) {\n // No project was selected — abort with a structured signal so the\n // runner renders a friendly outro.\n return SOURCE_MAPS_DETECTION_FAILED_PROMPT;\n }\n\n if (VARIANTS_REQUIRING_POSTHOG_CLI.has(variant))\n ensurePostHogCli(variant, runner.log);\n\n const uiHost = ctx.host.appHost.replace(/\\/$/, '');\n\n return buildSourceMapsUploadPrompt({\n displayName,\n variant,\n skillId,\n projectPath,\n projectId: ctx.projectId,\n host: ctx.host.apiHost,\n settingsUrl: `${uiHost}/project/${ctx.projectId}/settings/user-api-keys`,\n uiHost,\n reportFile: REPORT_FILE,\n });\n },\n\n postRun: () => {\n // Stash a hint for the outro about what variant we shipped.\n const { variant } = readSelection();\n if (variant) {\n runner.setFrameworkContext('sourceMapsCompletedVariant', variant);\n }\n return Promise.resolve();\n },\n\n buildOutroData: () => {\n // SourceMapsOutroScreen renders static \"what we did + how it works\"\n // guidance, so no per-run `changes` list is needed here.\n return {\n kind: OutroKind.Success as const,\n message: 'Source maps wired up!',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n };\n },\n });\n },\n};\n\nexport { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';\nexport {\n detectSourceMapsPrerequisites,\n SOURCE_MAPS_ABORT_CASES,\n SOURCE_MAPS_CONTEXT_KEYS,\n VARIANT_DISPLAY_NAME,\n MANUAL_SDK_VARIANTS,\n type SkillVariant,\n type SourceMapsDetectError,\n} from './detect.js';\n","/** Error-tracking learn-deck: the source-maps narrative, worded to also fit platforms that upload nothing. */\n\nimport type { WizardStore } from '@ui/tui/store';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { buildSourceMapsDeck } from '@ui/tui/decks/error-tracking-upload-source-maps/index';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] =>\n buildSourceMapsDeck(store, {\n intro: \"I'm wiring PostHog Error Tracking into your project.\",\n wiring:\n 'If your platform ships minified code or stripped binaries, I also hook source-map or debug-symbol upload into your build, tied to each release you ship.',\n });\n","import type { AbortCase } from '@agent/types';\nimport { Integration } from '@shared/constants';\nimport {\n detectFramework,\n gatherFrameworkContext,\n} from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport type { CiRunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport { createSkillProgram } from '@programs/agent-skill/index';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { detectPostHogIntegration } from '@programs/posthog-integration/detect';\nimport type {\n ProgramConfig,\n ProgramReadyContext,\n ProgramStep,\n} from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { analytics } from '@utils/analytics';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\n\nconst REPLAY_VISION_REPORT_FILE = 'posthog-replay-vision-report.md';\n\n/**\n * The platforms session replay can actually record on. Replay vision watches\n * recordings, so a platform with no recordings has nothing to set up — the\n * run must stop before any work, not after a pointless agent run.\n *\n * Web frameworks record through posthog-js (server-rendered frameworks\n * included — they serve pages), and the mobile SDKs with replay support are\n * React Native, Android, iOS, and Flutter. Excluded: pure backend targets\n * (`javascript_node`, `python`, `ruby`) and KMP, which has no replay support\n * yet.\n */\nexport const REPLAY_VISION_SUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.nextjs,\n Integration.nuxt,\n Integration.vue,\n Integration.reactRouter,\n Integration.tanstackStart,\n Integration.tanstackRouter,\n Integration.angular,\n Integration.astro,\n Integration.sveltekit,\n Integration.javascript_web,\n Integration.django,\n Integration.flask,\n Integration.fastapi,\n Integration.laravel,\n Integration.rails,\n Integration.reactNative,\n Integration.android,\n Integration.swift,\n Integration.flutter,\n]);\n\nasync function abortUnsupportedPlatform(\n integration: Integration,\n): Promise<void> {\n const name = FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration;\n // This is a clean, intentional exit, not a crash. Count it with a normal\n // event keyed on the platform so aborts roll up into one series. Do not hand\n // `wizardAbort` an `error` — that forwards to captureException and mints a\n // new error-tracking issue per install location and per platform.\n analytics.wizardCapture('replay-vision unsupported platform', {\n integration,\n });\n await wizardAbort({\n code: ErrorCodes.DetectUnsupportedPlatform,\n message:\n `Session replay isn't available for ${name} projects, and Replay ` +\n 'vision needs session recordings to watch — so there is nothing to ' +\n 'set up here.\\n\\n' +\n 'If this repo also contains a web or mobile app, run the command from ' +\n 'that project directory instead. See what replay supports at:\\n' +\n ' https://posthog.com/docs/session-replay',\n });\n}\n\n/**\n * `[ABORT]` reasons the replay-vision skill emits when the run can't proceed.\n * Kept in sync with the stop conditions in the skill's `description.md`\n * (context-mill `context/skills/replay-vision`).\n */\nexport const REPLAY_VISION_ABORT_CASES: AbortCase[] = [\n {\n match: /^replay vision not available for this project$/i,\n message: 'Replay vision is not available for this project',\n body:\n 'Every Replay vision scanner endpoint reported that the feature is not ' +\n 'available here yet. Session replay setup done so far is kept. See ' +\n 'https://posthog.com/docs/replay-vision for availability.',\n },\n];\n\n/**\n * Framework detection ahead of the run, exactly like the default integration\n * program. The orchestrator requires it: `session.skillId` must hold the\n * detected framework id before the run arm starts, because the runner\n * resolves the reference integration skill and every task's mini-skill\n * variants (`integration-v2-install`, `integration-v2-init`, …) against it in\n * preflight. Without this step the session would still carry the program's\n * own skill id and preflight would abort.\n */\nconst DETECT_STEP: ProgramStep = {\n id: 'detect',\n label: 'Detecting framework',\n // The platform gate runs on a direct detectFramework call BEFORE the full\n // detect writes to the store: store setters replace the session with a\n // shallow copy, so `ctx.session` read after detectPostHogIntegration would\n // be the stale pre-copy object (see the warning in detect.ts).\n onReady: async (ctx: ProgramReadyContext) => {\n const integration = await detectFramework(ctx.session.installDir);\n if (integration && !REPLAY_VISION_SUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n await detectPostHogIntegration(ctx);\n },\n};\n\nconst base = createSkillProgram({\n // The menu ids this skill `<dir>-<variant>`, and context-mill's\n // `replay-vision/config.yaml` declares a single variant, `setup`. The bare\n // `replay-vision` id does not exist — the orchestrator never installs this\n // (it resolves per-task mini-skills instead), but the linear path does, and\n // aborts `skill-not-found` on a miss.\n skillId: 'replay-vision-setup',\n command: 'replay-vision',\n id: 'replay-vision',\n description: 'Set up PostHog Replay Vision scanners for your product',\n integrationLabel: 'replay-vision',\n customPrompt:\n 'Set up PostHog Replay vision. Run the `replay-vision` skill end-to-end: ' +\n 'make sure session replay is recording (server-side enable plus a ' +\n 'posthog-js init check), then create the vision scanners the skill ' +\n \"defines, scoped to this product's key flows read out of the repo. If \" +\n 'PostHog is not integrated yet, install and initialize the SDK first as ' +\n 'the skill instructs — do not abort. The final report is written to ' +\n `./${REPLAY_VISION_REPORT_FILE}.`,\n successMessage: `Replay vision configured! View the report at ./${REPLAY_VISION_REPORT_FILE}`,\n reportFile: REPLAY_VISION_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/replay-vision',\n spinnerMessage: 'Setting up Replay vision...',\n estimatedDurationMinutes: 6,\n abortCases: REPLAY_VISION_ABORT_CASES,\n});\n\n/**\n * `wizard replay-vision` — flat skill command on the orchestrator sequence.\n *\n * Makes session replay record (server toggle + client init check), then\n * creates vision scanners scoped to the product's key flows, read out of the\n * repo. The orchestrator runs the `replay-vision` agent flow (context-mill\n * `context/agents/replay-vision`): the seed enqueues the install/init tasks\n * (copied from integration-v2, sharing its step-skills) when the project has\n * no PostHog yet, so the command works on uninstrumented repos instead of\n * aborting.\n *\n * Departures from a plain `createSkillProgram`:\n * - `DETECT_STEP` in front, so `session.skillId` carries the framework id the\n * orchestrator's preflight resolves reference + mini-skill variants with.\n * - `agentFlow` pinned (the id would default to the same value — explicit so\n * renaming the program can't silently detach the flow).\n * - `ciPreRun` mirrors the default integration program: scope the install dir\n * to the right project (monorepos), then detect the framework — the\n * headless equivalent of the detect step's onReady hook.\n */\nexport const replayVisionConfig: ProgramConfig = {\n ...base,\n agentFlow: 'replay-vision',\n steps: [DETECT_STEP, ...AGENT_SKILL_STEPS],\n\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n if (!REPLAY_VISION_SUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n\n const frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.frameworkConfig = frameworkConfig;\n session.skillId = integration;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: true,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n },\n};\n","/** Sidebar tips for the error-tracking run: product features the learn deck does not cover. */\n\nimport type { Tip } from '@ui/tui/components/TipsCard';\nimport { REPLAY_VISION_SUPPORTED } from '@programs/replay-vision/index';\n\nexport const ERROR_TRACKING_TIPS: Tip[] = [\n {\n id: 'session-replay',\n title: 'Watch the crash happen',\n description:\n 'An exception from a recorded session links to its replay. See the clicks that led to the error, not only the stack trace.',\n // Replay records only on platforms with a client SDK.\n visible: (store) => {\n const integration = store.session.integration;\n return integration != null && REPLAY_VISION_SUPPORTED.has(integration);\n },\n },\n {\n id: 'alerts',\n title: 'Know when a bug comes back from the dead',\n description:\n 'Set alerts for new issues, issues that return after you resolve them, and sudden spikes. Hear about them before your users do.',\n },\n {\n id: 'external-issues',\n title: 'From crash to ticket in one click',\n description:\n 'Create a GitHub, GitLab, Jira or Linear issue straight from an error.',\n },\n];\n\nexport const getTips = (): Tip[] => ERROR_TRACKING_TIPS;\n","/**\n * Error-tracking adapter over the shared integration scan: classifies each\n * project for the post-login picker and scopes the run to the picked one.\n *\n * Self-driving classifies the same scan (`toIntegrationCandidates`) with a\n * different rule, so each program keeps its own:\n * - A project that already has PostHog is a setup target here. Self-driving\n * offers it as \"continue with existing\" instead.\n * - KMP is not offered here (see `ERROR_TRACKING_UNSUPPORTED`).\n * - The scan asks for a recommended project, and the picker lists it first.\n * - The scan bills to error-tracking.\n */\n\nimport { Integration } from '@shared/constants';\nimport {\n resolveProjectDir,\n type AgenticDetectionReport,\n type DetectEvent,\n} from '@programs/detection/agentic';\nimport { gatherFrameworkContext } from '@programs/detection/index';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '@programs/detection/project-scope';\nimport type { WizardSession } from '@lib/wizard-session';\n\n/** frameworkContext key for the picked project's path, relative to the repo root. */\nexport const ERROR_TRACKING_PROJECT_PATH_KEY = 'errorTrackingProjectPath';\n\n/** KMP skill variants carry no `framework` tag, so orchestrator preflight aborts every KMP run. */\nexport const ERROR_TRACKING_UNSUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.kmp,\n]);\n\n/** One project, classified for the picker. */\nexport type ErrorTrackingProject = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A wizard framework when the project matches one, else null. */\n integration: Integration | null;\n /** The flow can run here. PostHog does not have to be installed yet. */\n instrumentable: boolean;\n};\n\nexport type ErrorTrackingDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: ErrorTrackingProject[];\n};\n\n/** Classify the scan for the picker, recommended project first (exported for tests). */\nexport function toErrorTrackingReport(\n report: AgenticDetectionReport,\n): ErrorTrackingDetectionReport {\n const candidates = toIntegrationCandidates(report).sort(\n (a, b) => Number(b.recommended) - Number(a.recommended),\n );\n return {\n repoType: report.repoType,\n projects: candidates.map((p) => ({\n path: p.path,\n framework: p.framework,\n integration: p.integration,\n instrumentable:\n p.integration != null && !ERROR_TRACKING_UNSUPPORTED.has(p.integration),\n })),\n };\n}\n\n/** Scan the repo for projects, billed to error tracking. */\nexport async function detectErrorTrackingProjects(\n session: WizardSession,\n onEvent?: DetectEvent,\n): Promise<ErrorTrackingDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'error-tracking',\n recommend: true,\n onEvent,\n });\n return toErrorTrackingReport(report);\n}\n\n/** The run's working directory: the picked project, else the repo root. */\nexport function errorTrackingProjectDir(session: WizardSession): string {\n return resolveProjectDir(\n session.installDir,\n session.frameworkContext[ERROR_TRACKING_PROJECT_PATH_KEY],\n );\n}\n\n/** Gather framework context for `session.installDir`, keeping keys already set. */\nexport async function gatherErrorTrackingContext(\n session: WizardSession,\n): Promise<void> {\n const frameworkConfig = session.frameworkConfig;\n if (!frameworkConfig) return;\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n}\n","import { Integration } from '@shared/constants';\nimport { detectFramework } from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { getContentBlocks } from '@ui/tui/decks/error-tracking/index';\nimport { getTips } from '@ui/tui/decks/error-tracking/tips';\nimport {\n ERROR_TRACKING_UNSUPPORTED,\n errorTrackingProjectDir,\n gatherErrorTrackingContext,\n} from '@programs/error-tracking/detect-agentic';\nimport type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { CiRunnerContext, RunnerContext } from '@programs/runner-context';\nimport { preinstallPostHogCliOnce } from '@programs/shared/posthog-cli-preinstall';\nimport { analytics } from '@utils/analytics';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\n\nconst ERROR_TRACKING_REPORT_FILE = 'posthog-error-tracking-report.md';\nconst ERROR_TRACKING_DOCS_URL = 'https://posthog.com/docs/error-tracking';\n\n/**\n * Frameworks whose symbol upload shells out to a machine-global `posthog-cli`\n * with no npx / local-dep fallback. The wizard pre-installs the CLI for them\n * because warlock blocks the agent's `npm install -g`. Mirrors\n * `VARIANTS_REQUIRING_POSTHOG_CLI` in the source-maps program, but keyed by\n * wizard `Integration` because here the framework is known before the flow's\n * seed picks an uploader variant (`swift` maps to the `ios` uploader).\n */\nexport const SYMBOL_UPLOAD_CLI_FRAMEWORKS: ReadonlySet<Integration> = new Set([\n Integration.swift,\n Integration.android,\n Integration.reactNative,\n Integration.flutter,\n Integration.go,\n Integration.rust,\n]);\n\nasync function abortUnsupportedPlatform(\n integration: Integration,\n): Promise<void> {\n const name = FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration;\n // A clean exit, not a crash: an event, never an `error` for captureException.\n analytics.wizardCapture('error tracking unsupported platform', {\n integration,\n });\n await wizardAbort({\n code: ErrorCodes.DetectUnsupportedPlatform,\n message:\n `The wizard cannot set up error tracking for ${name} projects yet.\\n\\n` +\n `Set it up manually:\\n ${ERROR_TRACKING_DOCS_URL}`,\n });\n}\n\n/**\n * Pre-install posthog-cli when the detected framework's symbol upload will\n * shell out to it. See `preinstallPostHogCliOnce` for the once-per-process\n * guard and the warn-don't-fail handling.\n */\nfunction maybePreinstallPostHogCli(\n integration: Integration | null,\n log: RunnerContext['log'],\n): void {\n if (!integration || !SYMBOL_UPLOAD_CLI_FRAMEWORKS.has(integration)) return;\n preinstallPostHogCliOnce(\n 'error tracking posthog-cli preinstall failed',\n { integration },\n log,\n );\n}\n\n/**\n * After login, the scan lists the repo's projects and the user picks one, as in\n * the legacy upload-source-maps program. The pick sets the framework preflight\n * resolves task skills against, and the project path the run is scoped to.\n */\nconst PICK_PROJECT_STEP: ProgramStep = {\n id: 'detect',\n label: 'Detecting projects',\n screenId: 'error-tracking-detect',\n isComplete: (session) => session.integration != null,\n};\n\nconst ERROR_TRACKING_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.flatMap(\n (step): ProgramStep[] => {\n if (step.id === 'intro') {\n return [{ ...step, screenId: 'error-tracking-intro' }];\n }\n if (step.id === 'auth') return [step, PICK_PROJECT_STEP];\n if (step.id === 'run') {\n // targetDir makes run-wizard walk the steps and run in the picked project.\n return [\n {\n ...step,\n targetDir: errorTrackingProjectDir,\n onRunPrep: gatherErrorTrackingContext,\n },\n ];\n }\n return [step];\n },\n);\n\n/**\n * Run instructions for a linear override (`--sequence=linear`), the only\n * sequence that reads `customPrompt`. The orchestrator runs the flow's own\n * prompts, so these spell out the skill-menu lookups its tasks perform.\n */\nconst ERROR_TRACKING_PROMPT = `Set up PostHog error tracking end-to-end:\n\n1. If PostHog is not integrated yet, install and initialize the SDK first —\n do not abort. Pick the matching variant from the skill menu's\n \"integration-v2/install\" and \"integration-v2/init\" categories.\n\n2. Wire up exception capture: install the \"error-tracking\" skill variant that\n matches this project's platform (\\`load_skill_menu\\` with\n \\`category: \"error-tracking\"\\`) and follow it. Set capture up in one place —\n the SDK's own mechanism, never manual capture calls sprinkled across files.\n\n3. When the platform ships minified bundles or stripped binaries (browser JS,\n React Native, iOS, Android, Flutter, Go, Rust), wire up source-map /\n debug-symbol upload too: install the matching\n \"error-tracking-upload-source-maps\" skill variant and follow it, including\n credentials and CI. Skip this step on platforms with readable stack traces\n (plain Python, Ruby, PHP, Elixir, JVM servers).\n\nThe final report is written to ./${ERROR_TRACKING_REPORT_FILE}.`;\n\nconst ERROR_TRACKING_RUN: ProgramRun = {\n integrationLabel: 'error-tracking',\n customPrompt: () => ERROR_TRACKING_PROMPT,\n successMessage: `Error tracking configured! View the report at ./${ERROR_TRACKING_REPORT_FILE}`,\n reportFile: ERROR_TRACKING_REPORT_FILE,\n docsUrl: ERROR_TRACKING_DOCS_URL,\n spinnerMessage: 'Setting up error tracking...',\n estimatedDurationMinutes: 8,\n // The flow can park on wizard_ask while the user does slow work (mint a\n // personal API key in the browser, run a build and trigger the test\n // error). The orchestrator caps per-task asks itself; this covers the\n // linear fallback.\n askTimeoutMs: 30 * 60 * 1000,\n};\n\n/**\n * `wizard error-tracking` — flat command on the orchestrator sequence.\n *\n * Makes uncaught errors reach PostHog with readable stack traces. The\n * orchestrator runs the `error-tracking` agent flow (context-mill\n * `context/agents/error-tracking`): the seed enqueues the install/init tasks\n * (sharing integration-v2's step-skills, like replay-vision) when the project\n * has no PostHog yet, then exception capture, then — when the platform needs\n * it — the source-map subgraph adapted from the standalone\n * `upload-source-maps` flow.\n *\n * Departures from a plain `createSkillProgram`:\n * - No `run.skillId`: the flow's tasks resolve per-framework mini-skills\n * themselves (there is no bare `error-tracking` menu entry), so the intro is\n * a custom screen rather than the generic skill intro.\n * - `PICK_PROJECT_STEP` after auth: the user picks the project, which sets the\n * framework preflight needs and the directory the run is scoped to.\n * - `run` is a function: `runAgent` resolves it after the pick (and after\n * `ciPreRun` headless), so the posthog-cli pre-install, which the agent\n * cannot do (warlock blocks \\`npm install -g\\`), waits for the user.\n * - `agentFlow` pinned (the id would default to the same value — explicit so\n * renaming the program can't silently detach the flow).\n * - `ciPreRun` mirrors replay-vision: scope the install dir to the right\n * project (monorepos), then detect the framework — the headless equivalent\n * of the project picker.\n */\nexport const errorTrackingConfig: ProgramConfig = {\n command: 'error-tracking',\n description: 'Set up PostHog error tracking, source-map upload included',\n id: 'error-tracking',\n agentFlow: 'error-tracking',\n steps: ERROR_TRACKING_STEPS,\n reportFile: ERROR_TRACKING_REPORT_FILE,\n getContentBlocks,\n getTips,\n\n run: (session: WizardSession, runner: RunnerContext): Promise<ProgramRun> => {\n maybePreinstallPostHogCli(session.integration, runner.log);\n return Promise.resolve(ERROR_TRACKING_RUN);\n },\n\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n if (ERROR_TRACKING_UNSUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n session.frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.skillId = integration;\n\n await gatherErrorTrackingContext(session);\n },\n};\n","/**\n * Self-driving prerequisite detection + abort vocabulary.\n *\n * The only thing worth verifying before auth is local and cheap: that\n * `session.installDir` is a real, readable directory. We deliberately do\n * NOT require the base posthog-integration report to be present — it is a\n * report many users never commit, and `requires: ['posthog-integration']`\n * is metadata, not a hard runtime gate.\n *\n * Self-driving is now in OPEN beta — available to every team — so STEP 1\n * no longer probes the Signals API as an access gate; it completes\n * instantly so the run opens with a fast first checkmark. The\n * `self-driving is not available for this project` abort below is kept\n * only as a safety net: if the Signals API genuinely can't be reached\n * during the run (a hard error that is unexpected in open beta), the skill\n * emits it and the wizard renders a friendly \"try again\" screen — now with\n * open-beta wording, not the old closed, per-team \"join the beta\" copy. The\n * PostHog-side flags (`product-autonomy`, `signals-scout`) are unchanged by\n * the wizard-side \"self-driving\" rename.\n */\n\nimport {\n existsSync,\n statSync,\n readFileSync,\n readdirSync,\n type Dirent,\n} from 'fs';\nimport { join } from 'path';\nimport { analytics } from '@utils/analytics';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { detectWarehouseSources } from '@programs/warehouse-sources/detect';\nimport type { DetectedSource } from '@programs/warehouse-sources/types';\n\n/** frameworkContext key holding the deterministic PostHog-presence result. */\nexport const POSTHOG_PRESENT_KEY = 'postHogPresent';\n\n/**\n * frameworkContext key holding the repo-relative path of the project the user\n * picked on the integration-detect screen (\".\" for the repo root). The\n * integrate-run phase scopes its install dir to this so a monorepo integrates\n * into the chosen sub-app, not the root.\n */\nexport const SELF_DRIVING_INTEGRATE_PATH_KEY = 'selfDrivingIntegratePath';\n\n/** Self-driving's own detected-tools key — not the warehouse one, which the integration program reads. */\nexport const SELF_DRIVING_DETECTED_TOOLS_KEY = 'selfDrivingDetectedTools';\n\n/** Read the detected tools out of frameworkContext. */\nexport function getSelfDrivingDetectedTools(\n session: WizardSession,\n): DetectedSource[] {\n return (\n (session.frameworkContext[SELF_DRIVING_DETECTED_TOOLS_KEY] as\n | DetectedSource[]\n | undefined) ?? []\n );\n}\n\n// Matches `posthog` at a dependency boundary (line start, or after \"'/=:.@ or\n// whitespace): catches `com.posthog:posthog-android` and `@posthog/ai`, skips\n// substrings inside other words.\nconst POSTHOG_PACKAGE_RE = /(^|[\"'\\s/=:.@])posthog/im;\n\n// Manifests grepped for a posthog dependency. Distinct from PROJECT_MANIFESTS\n// in @programs/detection/agentic (project-root discovery); keep the two in sync\n// (a test asserts the shared ecosystem names appear in both). Exported for it.\nexport const POSTHOG_MANIFESTS = [\n 'package.json',\n 'requirements.txt',\n 'pyproject.toml',\n 'Pipfile',\n 'setup.py',\n 'Gemfile',\n 'go.mod',\n 'composer.json',\n 'pubspec.yaml',\n // Apple: SPM manifest, CocoaPods (+ lockfile), XcodeGen spec.\n 'Package.swift',\n 'Podfile',\n 'Podfile.lock',\n 'project.yml',\n // Android / JVM (libs.versions.toml holds gradle coordinates).\n 'build.gradle',\n 'build.gradle.kts',\n 'gradle/libs.versions.toml',\n 'pom.xml',\n // Elixir / Rust.\n 'mix.exs',\n 'Cargo.toml',\n];\n\n// Named sub-app dirs always checked (ios/android: RN/Flutter native shells).\n// scanDirs unions these with a bounded depth-2 walk of the install dir.\nconst POSTHOG_DIRS = [\n '.',\n 'app',\n 'frontend',\n 'backend',\n 'ios',\n 'android',\n 'android/app',\n];\n\n// Heavy or vendored trees the shallow walk never descends into — they hold\n// dependencies, not a project's own manifest, and would false-positive on a\n// bundled posthog package.\nconst WALK_SKIP_DIRS = new Set([\n 'node_modules',\n 'Pods',\n 'Carthage',\n 'DerivedData',\n '.build',\n 'build',\n 'dist',\n 'out',\n '.next',\n 'coverage',\n 'vendor',\n '.venv',\n 'site-packages',\n 'target',\n '.git',\n]);\n\n// How deep below the install dir the shallow walk goes. 2 covers the common\n// monorepo shape (apps/<name>, packages/<name>) without a full tree walk.\nconst WALK_MAX_DEPTH = 2;\n\n/**\n * The install dir plus every directory within WALK_MAX_DEPTH levels (skipping\n * heavy/vendored trees and dotdirs), unioned with the explicit POSTHOG_DIRS. A\n * full shallow walk on top of the named dirs, so a monorepo's nested apps\n * (e.g. `apps/mobile`) are seen without hardcoding the path.\n */\nfunction scanDirs(installDir: string): string[] {\n const rels = new Set<string>(POSTHOG_DIRS);\n // Explicit stack — no call-stack recursion.\n const stack: Array<{ rel: string; depth: number }> = [{ rel: '.', depth: 0 }];\n while (stack.length > 0) {\n const { rel, depth } = stack.pop()!;\n rels.add(rel);\n if (depth >= WALK_MAX_DEPTH) continue;\n let entries: Dirent[];\n try {\n entries = readdirSync(join(installDir, rel), { withFileTypes: true });\n } catch {\n continue;\n }\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n if (e.name.startsWith('.') || WALK_SKIP_DIRS.has(e.name)) continue;\n // .xcodeproj/.xcworkspace are inspected via projectFileHasPostHog at the\n // parent dir — descending into the wrapper finds nothing useful.\n if (e.name.endsWith('.xcodeproj') || e.name.endsWith('.xcworkspace')) {\n continue;\n }\n stack.push({\n rel: rel === '.' ? e.name : `${rel}/${e.name}`,\n depth: depth + 1,\n });\n }\n }\n return [...rels];\n}\n\n/**\n * True if a `*.xcodeproj/project.pbxproj` or `*.csproj`/`*.fsproj` in\n * `dirPath` references PostHog.\n */\nfunction projectFileHasPostHog(dirPath: string): boolean {\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch {\n return false;\n }\n for (const entry of entries) {\n const target = entry.endsWith('.xcodeproj')\n ? join(dirPath, entry, 'project.pbxproj')\n : entry.endsWith('.csproj') || entry.endsWith('.fsproj')\n ? join(dirPath, entry)\n : null;\n if (!target || !existsSync(target)) continue;\n try {\n if (POSTHOG_PACKAGE_RE.test(readFileSync(target, 'utf8'))) return true;\n } catch {\n /* unreadable — ignore */\n }\n }\n return false;\n}\n\n/**\n * Deterministic, offline check: does the project already have a PostHog SDK?\n * Scans the common dependency manifests at the install dir for a `posthog`\n * package — the same signal the agentic detector reports as `hasPostHog`, but\n * instant and credential-free. Drives whether self-driving asks to integrate\n * first (\"not found\") or proceeds straight to setup (\"found\").\n */\nexport function detectPostHogPresent(installDir: string): boolean {\n for (const dir of scanDirs(installDir)) {\n const dirPath = join(installDir, dir);\n for (const name of POSTHOG_MANIFESTS) {\n const path = join(dirPath, name);\n if (!existsSync(path)) continue;\n try {\n if (POSTHOG_PACKAGE_RE.test(readFileSync(path, 'utf8'))) return true;\n } catch {\n /* unreadable — ignore */\n }\n }\n // Variably-named project files (.xcodeproj, .csproj/.fsproj).\n if (projectFileHasPostHog(dirPath)) return true;\n }\n return false;\n}\n\n/**\n * Structured detection errors. The intro screen renders each kind into\n * JSX — keeps error data separate from presentation.\n */\nexport type SelfDrivingDetectError = {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n};\n\n/**\n * `[ABORT] <reason>` cases the self-driving skill can emit. The\n * reason strings are part of the skill contract — the context-mill\n * `self-driving-setup` skill emits these exact strings.\n */\n/** Headline shown when the run ends because GitHub was not connected. */\nexport const GITHUB_REQUIRED_MESSAGE = 'GitHub connection required';\n\n/** Body for {@link GITHUB_REQUIRED_MESSAGE}. Shared by the pre-run gate and the abort case. */\nexport const GITHUB_REQUIRED_BODY =\n 'Self-driving needs GitHub access to research issues in your code and ' +\n 'open fixes, so setup cannot finish without it. Nothing was changed. ' +\n 'When you are ready to install the PostHog GitHub App, run the wizard again.';\n\nexport const SELF_DRIVING_ABORT_CASES: AbortCase[] = [\n {\n // Skill emits: [ABORT] self-driving is not available for this project\n match: /^self-driving is not available for this project$/i,\n message: 'PostHog Self-driving is not available for this project',\n body:\n 'Self-driving is in open beta and available to every team, so this ' +\n 'is unexpected — the PostHog Signals API could not be reached for ' +\n 'this project. Nothing was changed. Try again in a moment, and if it ' +\n 'keeps happening reach out to wizard@posthog.com.',\n },\n {\n // Retained as a safety net: the GitHub gate now runs before the agent, but\n // a cached older skill can still emit this mid-run.\n match: /^github connection declined$/i,\n message: GITHUB_REQUIRED_MESSAGE,\n body: GITHUB_REQUIRED_BODY,\n },\n {\n // Skill emits: [ABORT] requires-interactive-mode\n match: /^requires-interactive-mode$/i,\n errorCode: ErrorCodes.CliInteractiveRequired,\n message: 'Interactive terminal required',\n body:\n 'Self-driving setup asks questions along the way (GitHub and ' +\n 'issue trackers), so it needs an interactive terminal. Run ' +\n 'the wizard outside CI / non-interactive mode.',\n },\n {\n // The wizard_ask tool's own error texts (non-interactive host, ask cap\n // reached) instruct the agent to emit this reason — cover it so those\n // paths render a friendly screen instead of the generic abort outro.\n match: /^requirements-incomplete$/i,\n message: 'Setup needs your input',\n body:\n 'The wizard could not collect the answers this setup needs (the ' +\n 'environment was non-interactive, or the question budget ran out). ' +\n 'Nothing was left half-configured. Run the wizard again in an ' +\n 'interactive terminal.',\n },\n];\n\n/**\n * Verify `session.installDir` is a readable directory. Writes a\n * `SelfDrivingDetectError` to frameworkContext on failure — the intro\n * screen renders it and blocks.\n */\nexport function detectSelfDrivingPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: SelfDrivingDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n // Deterministic PostHog-presence check — drives the integration-check\n // screen: found → skip straight to self-driving; not found → ask to set up\n // PostHog first.\n setFrameworkContext(POSTHOG_PRESENT_KEY, detectPostHogPresent(installDir));\n\n detectConnectedTools(installDir, setFrameworkContext);\n}\n\n/** Step 5's ask options intersected with `SOURCE_DETECTORS` — reconcile when the skill's catalog grows. */\nexport const SELF_DRIVING_TOOL_KINDS: ReadonlySet<string> = new Set([\n // Issue trackers / code hosts\n 'Github',\n 'GitLab',\n 'Gitea',\n 'Linear',\n 'Jira',\n 'Shortcut',\n // Error tracking\n 'Sentry',\n 'Rollbar',\n 'Bugsnag',\n 'Honeybadger',\n 'Raygun',\n // Support desks\n 'Zendesk',\n 'Freshdesk',\n 'Front',\n 'Gorgias',\n 'Kustomer',\n 'Plain',\n // Security scanners\n 'Snyk',\n // Product feedback\n 'Canny',\n 'Productboard',\n // Search analytics\n 'GoogleSearchConsole',\n]);\n\n/** Scan for inbox-connectable tools so STEP 5 can surface them first. Best-effort — never blocks detection. */\nfunction detectConnectedTools(\n installDir: string,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n try {\n const tools = detectWarehouseSources(installDir).filter((s) =>\n SELF_DRIVING_TOOL_KINDS.has(s.kind),\n );\n\n // Tagged even at zero, so \"found nothing\" is distinguishable from \"never ran\".\n analytics.setTag('connected_tools_detected_count', tools.length);\n if (tools.length === 0) return;\n\n analytics.setTag(\n 'connected_tools_detected',\n tools.map((s) => s.kind).join(','),\n );\n setFrameworkContext(SELF_DRIVING_DETECTED_TOOLS_KEY, tools);\n } catch (error) {\n // -1, not absent, so a failed scan stays distinguishable from one that never ran.\n analytics.setTag('connected_tools_detected_count', -1);\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectConnectedTools' },\n );\n }\n}\n","/**\n * Agentic (Haiku) detection for self-driving's integration phase.\n *\n * Mirrors source-maps: the integration-detect screen runs `detectSelfDriving-\n * IntegrationProjects` (the shared integration scan from\n * @programs/detection/project-scope), shows a project map, and the user picks one.\n * `toIntegrationCandidates` matches each project to an `Integration`; this file\n * classifies it as instrumentable (a framework the wizard supports that doesn't\n * already have PostHog). The screen writes the choice to the session; `prepSelfDriving-\n * Integration` then gathers the chosen project's framework context before the\n * integration agent runs (the runner scopes the install dir).\n */\n\nimport type {\n AgenticDetectionReport,\n DetectEvent,\n} from '@programs/detection/agentic';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '@programs/detection/project-scope';\nimport { gatherFrameworkContext } from '@programs/detection/index';\nimport type { Integration } from '@shared/constants';\nimport type { WizardSession } from '@lib/wizard-session';\n\nexport type { DetectEvent };\n\n/** One project, classified for a PostHog SDK integration. */\nexport type IntegrationProject = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A supported framework when it matches one, else null. */\n integration: Integration | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** integration != null && !hasPostHog — PostHog can be set up here. */\n instrumentable: boolean;\n /** hasPostHog: skip integration and continue straight to Self-driving. */\n continuable: boolean;\n /** Why the project can't be set up (only when !instrumentable). */\n reason?: string;\n};\n\nexport type IntegrationDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: IntegrationProject[];\n};\n\nfunction classify(\n integration: Integration | null,\n hasPostHog: boolean,\n): { instrumentable: boolean; reason?: string } {\n if (integration == null) {\n return {\n instrumentable: false,\n reason: 'Not a framework the wizard can set up yet',\n };\n }\n if (hasPostHog) {\n return { instrumentable: false, reason: 'Already has the PostHog SDK' };\n }\n return { instrumentable: true };\n}\n\n/** Map a detection report into classified projects (exported for tests). */\nexport function toIntegrationReport(\n report: AgenticDetectionReport,\n): IntegrationDetectionReport {\n return {\n repoType: report.repoType,\n projects: toIntegrationCandidates(report).map((p) => ({\n path: p.path,\n framework: p.framework,\n integration: p.integration,\n hasPostHog: p.hasPostHog,\n continuable: p.hasPostHog,\n ...classify(p.integration, p.hasPostHog),\n })),\n };\n}\n\n/** Run the Haiku detector over the repo and classify projects for integration. */\nexport async function detectSelfDrivingIntegrationProjects(\n session: WizardSession,\n onEvent?: DetectEvent,\n): Promise<IntegrationDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'self-driving',\n onEvent,\n });\n return toIntegrationReport(report);\n}\n\n/**\n * Prepare the integration phase after the user picked a project on the\n * integration-detect screen (which set `session.integration`, `frameworkConfig`,\n * and the chosen path). Scopes `installDir` to the chosen project — so a\n * monorepo integrates into the picked sub-app — and gathers that project's\n * framework context, mirroring posthog-integration's `ciPreRun`. Used as the\n * integrate-run step's `onRunPrep`.\n */\nexport async function prepSelfDrivingIntegration(\n session: WizardSession,\n): Promise<void> {\n // `session` is the phase's derived session — its installDir is already the\n // picked project (the integrate-run step's `targetDir`), so just gather that\n // project's framework context.\n const frameworkConfig = session.frameworkConfig;\n if (!frameworkConfig) return;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n}\n","/**\n * Self-driving program step list.\n *\n * detect → intro → integration-check → health-check → auth → integrate-detect →\n * integrate-run → self-driving-handoff → self-driving-github → run → outro. A deterministic check in\n * `detect` decides whether PostHog is already in the project: found → the\n * integration screens are skipped and the integrate-run phase never shows; not\n * found → integration-check reports it and the only action sets up PostHog.\n * After auth, `integrate-detect` runs the Haiku detector and has the user pick\n * which project to set PostHog up in (a monorepo can have several);\n * `integrate-run` then runs the real integration program's agent (its own task\n * list) in that project. `self-driving-handoff` then bridges to Self-driving\n * (\"PostHog is installed — now set up Self-driving\"), then `self-driving-github`\n * gates on the GitHub App connection the run cannot proceed without. No keep-skills step: the setup skill is transient, so postRun removes it.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { resolveProjectDir } from '@programs/detection/agentic';\nimport { RunPhase, type WizardSession } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { integrationRunStep } from '@programs/posthog-integration/index';\nimport {\n detectSelfDrivingPrerequisites,\n POSTHOG_PRESENT_KEY,\n SELF_DRIVING_INTEGRATE_PATH_KEY,\n} from './detect.js';\nimport { prepSelfDrivingIntegration } from './detect-agentic.js';\n\n/** True once detection found PostHog already present in the project. */\nconst postHogPresent = (session: WizardSession): boolean =>\n session.frameworkContext[POSTHOG_PRESENT_KEY] === true;\n\n/** Absolute dir to integrate into: the picked sub-app (LLM output — the shared resolver clamps escapes), else the repo root. */\nconst integrationDir = (session: WizardSession): string =>\n resolveProjectDir(\n session.installDir,\n session.frameworkContext[SELF_DRIVING_INTEGRATE_PATH_KEY],\n );\n\nexport const SELF_DRIVING_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n // Headless: validates the install dir and runs the deterministic\n // PostHog-presence check (writes frameworkContext.postHogPresent).\n onReady: (ctx) =>\n detectSelfDrivingPrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'self-driving-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n // Shown only when PostHog wasn't detected and the decision is still open:\n // \"Set up PostHog first?\". On \"yes\" the integrate-run phase runs the\n // integration. When PostHog is already present (or `--integrate` pre-set\n // it), this is skipped. The gate lets run-wizard settle the decision —\n // resolved immediately when no question is needed.\n id: 'integration-check',\n label: 'Integration',\n screenId: 'self-driving-integration-check',\n show: (session) => !postHogPresent(session) && session.integrate === null,\n isComplete: (session) =>\n postHogPresent(session) || session.integrate !== null,\n gate: (session) => postHogPresent(session) || session.integrate !== null,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n // After auth, before the integration runs: the detector scans the repo and\n // the user picks which project to set PostHog up in — a single project or\n // the repo root is still a one-item confirm. The pick writes the framework +\n // path to the session. Shown only while integrating and undecided; complete\n // once a project is picked (the orchestrator waits on this live).\n id: 'integrate-detect',\n label: 'Detecting',\n screenId: 'self-driving-integration-detect',\n show: (session) =>\n session.integrate === true && session.integration == null,\n // Complete on a picked project OR \"continue with existing\"\n // (integrate=false); without the latter the orchestrator's waitUntil hangs.\n isComplete: (session) =>\n session.integration != null || session.integrate === false,\n },\n {\n // The integration's own run step, imported and composed here: it runs the\n // integration agent (its prompt, tools, task list) in the picked project's\n // dir. Shown only when integrating; prep gathers that project's framework\n // context. Completion is tracked via `completedRuns`, separate from the\n // Self-driving run's `runPhase`.\n ...integrationRunStep,\n id: 'integrate-run',\n onRunPrep: prepSelfDrivingIntegration,\n targetDir: integrationDir,\n show: (session) => session.integrate === true,\n isComplete: (session) => session.completedRuns.includes('integrate-run'),\n },\n {\n // Handoff after the integration run: \"PostHog is installed — now set up\n // Self-driving\". Only in the integrate path; the already-has-PostHog path\n // skips it. Complete once acknowledged (the orchestrator waits on this).\n id: 'self-driving-handoff',\n label: 'Ready',\n screenId: 'self-driving-handoff',\n show: (session) => session.integrate === true,\n isComplete: (session) => session.selfDrivingHandoffConfirmed,\n },\n {\n // Hard gate before the agent starts: Self-driving cannot research findings\n // or open fixes without repo access. Asking here rather than mid-run means\n // a user who steps away isn't read as declining, and a user who won't\n // connect hasn't paid for an agent start. Complete once GitHub is\n // connected, or once the user says they can't — which hides `run` below.\n id: 'self-driving-github',\n label: 'GitHub',\n screenId: 'self-driving-github',\n isComplete: (session) =>\n session.githubConnected === true || session.githubDeclined,\n gate: (session) =>\n session.githubConnected === true || session.githubDeclined,\n },\n {\n id: 'run',\n label: 'Self-driving',\n screenId: 'run',\n show: (session) => !session.githubDeclined,\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n];\n","import { AgentSignals } from '@agent';\nimport type { PromptContext } from '@agent/types';\nimport type { DetectedSource } from '@programs/warehouse-sources/types';\n\n/**\n * Render the deterministic codebase-tool scan for the prompt. STEP 4 and\n * STEP 5 read this instead of the agent doing its own flaky \"light scan\":\n * enable the matching signal sources, and surface detected tools first in the\n * connected-tools ask rather than dumping the full ~500-source catalog. When\n * nothing is detected we still say so explicitly, so the agent falls back to\n * the skill's default ordering instead of inventing a scan.\n */\nfunction renderDetectedTools(sources: DetectedSource[]): string {\n if (sources.length === 0) {\n return `Tools detected in this codebase: none found by the dependency + env scan. Use the skill's default ordering for STEP 5.`;\n }\n\n const lines = sources.map(\n (s) => `- ${s.label} (source_type: ${s.kind}) — ${s.matchedSignal}`,\n );\n return [\n 'Tools detected in this codebase (deterministic dependency + env-key scan — this is evidence, not a guess):',\n ...lines,\n '',\n 'Use this list in STEP 4 (enable the matching signal sources) and STEP 5 ' +\n '(surface these detected tools first in the connected-tools ask, ahead ' +\n 'of the SaaS basics and the \"others\" option), exactly as the skill ' +\n 'describes. Never enable or pre-select a tool the user has not confirmed.',\n ].join('\\n');\n}\n\n/**\n * Build the self-driving run prompt. The installed\n * `self-driving-setup` skill is the source of truth for the HOW of\n * every step (which MCP tools to call, which sources/scouts apply, how\n * to verify); this prompt carries the order, the wizard-specific\n * mechanics (wizard_ask, abort signals), and the project URLs.\n *\n * `detectedSources` is the deterministic codebase scan (from the detect step);\n * it drives STEP 4/STEP 5 tool prioritisation. Empty is fine — the block then\n * tells the agent to use the skill's default ordering.\n *\n * Integration (when the project has no PostHog yet) runs as a separate phase\n * before this — the real integration program, with its own screens and task\n * list — so this prompt only covers the Self-driving steps.\n */\nexport function buildSelfDrivingPrompt(\n ctx: PromptContext,\n detectedSources: DetectedSource[] = [],\n): string {\n const uiHost = ctx.host.appHost.replace(/\\/$/, '');\n const projectBase = `${uiHost}/project/${ctx.projectId}`;\n const integrationsSettingsUrl = `${projectBase}/settings/environment-integrations`;\n const orgAiSettingsUrl = `${uiHost}/settings/organization#organization-ai-consent`;\n const newWarehouseSourceUrl = `${projectBase}/pipeline/new/source`;\n const inboxUrl = `${projectBase}/inbox`;\n const optIn = (value: boolean | null | undefined): string =>\n value === true ? 'ON' : value === false ? 'OFF' : 'unknown';\n const optIns = ctx.teamProductOptIns;\n\n return `You are setting up PostHog Self-driving for this project: you will enable the right signal sources, tune the scout troop, design custom scouts for what this product uniquely needs, put Replay Vision scanners on its key flows, and hand the user a configured inbox.\n\nProject URLs:\n- Integrations settings: ${integrationsSettingsUrl}\n- Organization AI settings: ${orgAiSettingsUrl}\n- New data warehouse source (Linear / Zendesk / GitHub issues / pganalyze): ${newWarehouseSourceUrl}\n- Self-driving inbox: ${inboxUrl}\n\nProject state read at auth time (PostHog project settings — authoritative\nfor whether a product is enabled, regardless of what this repo\ninstruments; products are often instrumented from other repos or the\nsnippet, so repo evidence may rule a product IN but never OUT):\n- Session replay recording: ${optIn(optIns?.sessionReplay)}\n- Exception autocapture (error tracking): ${optIn(optIns?.exceptionAutocapture)}\n- Surveys: ${optIn(optIns?.surveys)}\n\n${renderDetectedTools(detectedSources)}\n\nThe installed skill is the source of truth for the HOW of every step:\nwhich MCP tools to call, which sources and scouts apply to this product,\nand how to verify each change. The STEPS below give the order and the\nwizard-specific mechanics — read the matching skill reference before\ndoing the work, and do not invent steps the skill doesn't describe.\n\nBefore doing any work, create your FULL task list in a single TaskCreate\ncall so the user can follow your progress in the TUI. Use exactly these\ntasks, in this order:\n 1. Check Self-driving access\n 2. Read project and current Self-driving state\n 3. Enable products (replay, error tracking, support)\n 4. Enable signal sources\n 5. Offer issue-tracker integrations\n 6. Configure the scout troop\n 6b. Design custom scouts\n 6c. Set up Replay Vision scanners\n 7. Write report and hand off\nDrive the list with TaskUpdate — mark a task in_progress when you start\nit and completed when done. If a step turns out to be a no-op (e.g. a\nproduct is already enabled), still mark its task completed.\n\nThe PostHog GitHub App is already connected — the wizard verified it\nbefore this run started, so never ask the user to connect or install it.\n\nWizard mechanics:\n- Ask the user things ONLY with the wizard_ask MCP tool, and batch\n related questions (e.g. one multi-select for all issue trackers, not\n one ask per tool). The per-run ask budget is limited.\n- If wizard_ask is unavailable (CI / non-interactive), emit\n ${AgentSignals.ABORT} requires-interactive-mode and halt.\n- When a step requires the user to do something in the browser, give\n them the exact URL inside the wizard_ask prompt text — do not try to\n open a browser yourself.\n- Emit ${AgentSignals.STATUS} lines as you complete each step so the\n user sees progress.\n\nFollow these steps IN ORDER. Do not skip or reorder.\n\nSTEP 1 — Check Self-driving access. (skill: \"Check access\")\n Self-driving is in open beta and available to every team, so there is\n no access gate to probe. Do NOT call any MCP tool here — mark this task\n in_progress and then completed right away and emit the\n ${AgentSignals.STATUS} line, so the user sees an immediate first step.\n Only if the Signals API later turns out to be genuinely unreachable for\n this project (a hard error on every Signals call, unexpected in open\n beta) should you emit\n ${AgentSignals.ABORT} self-driving is not available for this project\n and halt.\n\nSTEP 2 — Read project and current Signals state. (skill: \"Read context\")\n If ./posthog-setup-report.md exists, read it as a strong hint for what\n THIS repo instruments — but it is often absent (users frequently don't\n commit it), so do NOT depend on it. Combine whatever you find with the\n project-state block above and the skill's server-side usage probes —\n repo evidence rules products in, never out. Do a light scan ONLY for\n what neither covers. List the currently enabled signal sources so every\n later write is idempotent.\n\nSTEP 3 — Enable products. (skill: \"Enable products\")\n Turn ON the PostHog products Signals reads from — Session Replay,\n Error Tracking, and Support — so the sources you enable next, and the\n Replay Vision scanners in STEP 6c, have data to read. These are\n server-side enables with conservative defaults (owned by the server,\n not you). The project-state block above covers\n only Session Replay and Error Tracking, so you can skip those if they\n are already ON; it does not show Support, but every enable is idempotent\n so enabling any of them again is safe regardless. For a web app, also\n make sure this repo's posthog-js\n init doesn't disable them; for a pure backend or mobile app the server\n flip is inert, so just record that for the report and move on. The skill\n names the exact tool and the per-platform detail.\n\nSTEP 4 — Enable signal sources. (skill: \"Enable sources\")\n Enable the sources that match what this product actually uses, per\n the skill. Never enable a source for a tool the user hasn't\n confirmed they use.\n\nSTEP 5 — Offer issue-tracker integrations. (skill: \"Connected tools\")\n One batched multi-select wizard_ask for the external tools the skill\n lists. Order it per the skill: the tools from the \"Tools detected in\n this codebase\" list above come first, then the SaaS basics, then an\n \"others\" option for the long tail — never dump the whole catalog.\n The run auto-connects the ones it can (GitHub Issues, and\n Linear via a one-click OAuth link), verifying each with a single\n silent check — never nudge. For GitHub Issues: when the GitHub\n integration has exactly one repository connected, use that repo by\n default and skip repo research entirely — research which repo\n matches this project only when several are connected. It arms the\n rest as dormant responders to\n finish later: for tools it can't auto-connect (Zendesk, pganalyze) it\n never sends the user to paste credentials and never re-prompts. Enable\n a source only for a tool the user picked.\n\nSTEP 6 — Configure the scout troop. (skill: \"Scouts\")\n Materialize the troop, read the project's enforced scout-run budget\n (100 runs a day by default), then enable a selective set — the\n \"general\" scout plus the three to five specialists for the products\n this project uses most — and disable the rest, per the skill. The\n whole troop, including step 6b's custom scouts, stays at or under\n ten enabled scouts.\n\n Then tell the user what the troop is, before STEP 6b asks them to add\n to it. Scouts are PostHog's built-in ones, and the user has not seen\n the list: without it, STEP 6b asks them to approve additions to a\n baseline they cannot picture. Send one short message naming each\n scout you enabled with a plain one-line description of what it\n watches, and say how many you left disabled and that they can turn\n those on later from the inbox. No wizard_ask here, it is not a\n question. Keep each line to one sentence and do not restate the\n skill's internals.\n\nSTEP 6b — Design custom scouts for this product. (skill: \"Custom scouts\")\n You are the only actor that has read this repo — turn that into\n coverage per the skill: a real gap analysis of the project's\n watchable surfaces against what the built-in troop already covers,\n then custom scouts for the uncovered ones. Start the gap analysis\n from the repo's for-agents context when present (AGENTS.md,\n CLAUDE.md, ARCHITECTURE.md, .cursor/rules, agent-facing docs) — it\n is a maintained map of the product's surfaces and vocabulary, so\n read it before scanning source. Keep scout bodies\n high-level: describe the behavior and signal conditions to watch,\n referencing repo evidence by file/function name — never paste raw\n source, secrets, env values, or customer data into a scout body.\n Never edit built-in scout bodies. Propose all candidates in ONE\n batched wizard_ask\n before creating anything; the user declining everything (or finding\n no gap at all) is a valid outcome, not an abort. Mark the task\n completed either way.\n\nSTEP 6c — Set up Replay Vision scanners. (skill: \"Replay Vision scanners\")\n Create the scanner skeletons the skill defines, filling the per-product\n blanks it leaves you from this repo's code. Scanners need Session Replay\n on (STEP 3). Every failure here — no recordings yet, a backend-only\n project, the scanner API missing — is a follow-up, never an abort; mark\n the task completed regardless.\n\nSTEP 7 — Write the report and hand off. (skill: \"Report\")\n Write the report per the skill, including follow-ups for anything\n deferred. Tell the user findings will start appearing in their inbox\n at ${inboxUrl} within about 30 minutes.`;\n}\n","/**\n * Canonical step keys for the Self-driving run's `wizard: step` analytics.\n *\n * The agent writes its own task labels, so the same step reaches analytics under whatever wording\n * that run's model picked — \"Enable products\", \"Enabling products\" and \"Turn on PostHog products\"\n * are all STEP 3. A funnel keyed on that label silently loses runs the moment the wording drifts,\n * which is invisible: the number just drops.\n *\n * These rules map a label back to the step it belongs to, so a funnel can key on `step_key` and stay\n * correct across rewordings. `step_name` still ships alongside it, unchanged, for anything that wants\n * the label the user actually saw.\n */\n\n/**\n * Canonical keys, in the order the prompt's STEPs run. Frozen: these are event property values.\n * `connect_github` is retained for historical runs — the GitHub App is now connected on a wizard\n * screen before the agent starts, so no current run emits it.\n */\nexport const SELF_DRIVING_STEP_KEYS = [\n 'check_access',\n 'read_context',\n 'connect_github',\n 'enable_products',\n 'enable_signal_sources',\n 'connect_issue_trackers',\n 'configure_scouts',\n 'design_custom_scouts',\n 'setup_replay_vision',\n 'write_report',\n] as const;\n\nexport type SelfDrivingStepKey = (typeof SELF_DRIVING_STEP_KEYS)[number];\n\n/**\n * First match wins, so order is the rule. Entity words come before verbs: \"Checking GitHub\n * connection\" is the GitHub step, not the access-check step, and \"Connecting issue trackers\" must\n * not fall through to GitHub just because a later run words it \"Connecting GitHub issues\".\n */\nconst RULES: readonly { key: SelfDrivingStepKey; match: RegExp }[] = [\n { key: 'connect_issue_trackers', match: /issue[\\s-]?track/i },\n { key: 'setup_replay_vision', match: /replay vision|vision scanner/i },\n { key: 'design_custom_scouts', match: /custom scout|designing scout/i },\n {\n key: 'configure_scouts',\n match: /scout troop|tun(e|ing) .*scout|configur\\w* .*scout/i,\n },\n { key: 'enable_signal_sources', match: /signal source/i },\n { key: 'connect_github', match: /github/i },\n { key: 'enable_products', match: /product/i },\n { key: 'write_report', match: /report/i },\n { key: 'check_access', match: /access/i },\n { key: 'read_context', match: /read|context|project state/i },\n];\n\n/**\n * The canonical key for an agent-authored step label, or `undefined` when nothing matches — an\n * unmatched step is left unkeyed rather than bucketed, so a new step shows up as a gap to name\n * instead of quietly inflating whichever key it landed nearest.\n */\nexport function resolveSelfDrivingStepKey(\n stepName: string | undefined,\n): SelfDrivingStepKey | undefined {\n if (!stepName) return undefined;\n return RULES.find((rule) => rule.match.test(stepName))?.key;\n}\n","/**\n * Self-driving pricing, in one place. It's stated at six points across the run (intro, tips,\n * learn deck, outro), and a price quoted six ways is a price the reader stops trusting.\n *\n * Careful with the word \"free\". The tracking this wizard installs bills as ordinary PostHog\n * usage, and scouts are heading for usage-based pricing too — so anything called free here is\n * a promise with an expiry date. State what the PR costs and leave the rest to usage-based\n * billing, which stays true either side of that change.\n */\n\n/** Flat USD charge per report that ships a pull request. */\nexport const PRICE_PER_PR_USD = 15;\n\n/** One line, for a screen that only has room to say what it costs. */\nexport const PRICING_SHORT = `Agents charge a flat $${PRICE_PER_PR_USD} per pull request they ship.`;\n\n/** The same fact at length, plus the bit people are surprised by later. */\nexport const PRICING_LONG =\n `A report that ships a pull request costs a flat $${PRICE_PER_PR_USD}. ` +\n `Everything else follows your usual PostHog usage-based pricing.`;\n\n/**\n * Nothing caps the spend unless you set it, which is worth saying out loud. Checked against\n * billing: `custom_limits_map` starts empty and a product with no custom limit resolves to\n * `usage_limit: None`, i.e. uncapped. A free plan is capped by its own allocation instead,\n * hence the qualifier.\n */\nexport const NO_DEFAULT_LIMIT =\n 'A paid plan has no monthly cap until you set one. Add a PR limit in your inbox sidebar, ' +\n 'and agents pause when they hit it.';\n","import { NO_DEFAULT_LIMIT, PRICING_LONG } from './pricing.js';\n\n/**\n * Sidebar tips for the self-driving run — short footnotes on the\n * vocabulary ladder (signal source → scout → signal → report → measured\n * loop). The learn deck (`./index.tsx`) carries the teaching; these stay\n * one to two lines each so the pane never overflows.\n *\n * Product knowledge lives here in the program, not in the generic\n * `TipsCard`. Wired onto the program's `getTips`.\n */\n\nimport type { Tip } from '@ui/tui/components/TipsCard';\n\nexport const SELF_DRIVING_TIPS: Tip[] = [\n {\n id: 'signal-source',\n title: 'Signal source',\n description:\n 'A stream PostHog watches: errors, replays, support, GitHub or Linear issues.',\n },\n {\n id: 'scout',\n title: 'Scout',\n description:\n 'An agent on a schedule, watching your sources for anomalies and patterns. Your project gets up to 100 scout runs a day by default.',\n },\n {\n id: 'scanner',\n title: 'Scanner',\n description:\n 'Where a scout reads your data, a scanner watches session recordings — an LLM spotting what broke on screen. It spends Replay Vision quota, so it stays scoped to your key flows.',\n },\n {\n id: 'signal-report',\n title: 'Signal → report',\n description:\n 'One finding with evidence and a suggested action. Signals group into prioritized reports in your inbox.',\n },\n {\n id: 'the-loop',\n title: 'The loop',\n description:\n 'After a fix ships, PostHog measures the result. If the pattern persists, a new signal reopens the work.',\n },\n {\n id: 'pricing',\n title: 'What it costs',\n description: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`,\n },\n {\n id: 'work-anywhere',\n title: 'Work anywhere',\n description:\n 'Your inbox is in the PostHog app, Slack (tag @PostHog), and MCP. You can work from anywhere.',\n },\n];\n\nexport const getTips = (): Tip[] => SELF_DRIVING_TIPS;\n","/**\n * PIPELINE_BLOCK — ASCII diagram of the Self-driving loop: sources feed\n * scouts, scouts raise signals, signals group into reports, reports land\n * in the inbox, shipped fixes get measured, and misses loop back as new\n * signals. Kept under ~38 chars wide so it fits the LearnCard pane at an\n * 80-column terminal.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PIPELINE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 300,\n pause: 8000,\n lines: [\n <Text>\n {' '}\n <Text bold color=\"cyan\">\n signal sources\n </Text>\n </Text>,\n <Text dimColor>{' errors · replays · support'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n scouts\n </Text>\n </Text>,\n <Text dimColor>{' watching on a schedule'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n signals\n </Text>\n </Text>,\n <Text dimColor>{' finding + evidence + action'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n reports\n </Text>\n </Text>,\n <Text dimColor>{' grouped and prioritized'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color=\"green\">\n your inbox\n </Text>\n </Text>,\n <Text dimColor>{' investigate · tag · ship a PR'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n measured\n </Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ↺ '}</Text>\n <Text dimColor>didn't fix it? a new signal</Text>\n </Text>,\n ],\n};\n","import { NO_DEFAULT_LIMIT, PRICING_LONG } from './pricing.js';\n/**\n * Self-driving learn-deck — the narrative script played while the agent\n * sets up Self-driving. Teaches the vocabulary ladder (signal source →\n * scout → signal → report → action → measured loop) before the agent's\n * questions use those nouns, and explains why GitHub is required before\n * the connect ask lands.\n *\n * One deck for every self-driving run. On the integrate path it plays\n * during the install run (the pane belongs to whichever run screen shows\n * first), so it doubles as the wait-time read.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { PIPELINE_BLOCK } from './pipeline-diagram.js';\n\nconst CLEAR: ContentBlock = { type: 'clear', pause: 2000 };\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => {\n return [\n // Scene 1 — orient\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content: \"It's setting up PostHog Self-driving for this project.\",\n pause: 5000,\n },\n\n {\n content: \"We'll occasionally need your input. Stick around.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 2 — controls\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n CLEAR,\n\n // Scene 3 — platform story\n {\n content: 'Your PostHog tools all share one data layer.',\n pause: 4000,\n },\n\n {\n content:\n 'Analytics, replays, errors, flags: they all share the same users, mapped across every tool.',\n pause: 6000,\n },\n\n {\n content: 'Together, they show how people really use your product.',\n pause: 5000,\n },\n\n {\n content:\n \"Piecing that together used to be manual work. That's what Self-driving automates.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 4 — the differentiator, and why GitHub\n {\n content:\n 'A coding agent sees your code, but not how your product is used.',\n pause: 5000,\n },\n\n { content: 'PostHog knows what your users actually do.', pause: 4000 },\n\n { content: 'Self-driving puts an agent in the loop.', pause: 5000 },\n\n {\n content:\n \"That's why setup will ask to connect GitHub: so Self-driving can access your code and your context, together.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 5 — signal sources\n {\n content: 'Each PostHog tool you enable is a signal source.',\n pause: 4000,\n },\n\n {\n content:\n \"Errors, replays, support tickets, GitHub or Linear issues: they're all streams of product usage.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 6 — scouts\n {\n content: 'Scouts are agents that watch those sources on a schedule.',\n pause: 5000,\n },\n\n {\n content:\n 'They look for anomalies and patterns: a spike in errors or a funnel quietly dropping.',\n pause: 6000,\n },\n\n { content: 'Setup tunes your scouts to fit your product.', pause: 4000 },\n\n CLEAR,\n\n // Scene 7 — signals and reports\n {\n content:\n \"When a scout finds something worth your attention, that's a signal.\",\n pause: 5000,\n },\n\n {\n content:\n 'Each signal carries the finding, the evidence, and a suggested action.',\n pause: 5000,\n },\n\n {\n content: 'Related signals group into reports, sorted by priority.',\n pause: 4000,\n },\n\n CLEAR,\n\n { content: \"Here's the whole loop.\", pause: 1000 },\n\n PIPELINE_BLOCK,\n\n CLEAR,\n\n // Scene 8 — payoff: the inbox\n { content: 'Reports land in your Self-driving inbox.', pause: 4000 },\n\n {\n content: 'Investigate with the agent, tag teammates, or kick off a PR.',\n pause: 6000,\n },\n\n {\n // No lead-in: PRICING_LONG already names the PR as what's charged, and the\n // pane only fits ~148 chars per beat.\n content: PRICING_LONG,\n pause: 5000,\n },\n\n {\n content: NO_DEFAULT_LIMIT,\n pause: 5000,\n },\n\n CLEAR,\n\n // Scene 9 — the loop kicker\n {\n content: 'And after the PR ships, PostHog measures the result.',\n pause: 5000,\n },\n\n { content: 'Did the fix actually address the pattern?', pause: 4000 },\n\n {\n content: 'If not, a new signal surfaces, and the loop keeps working.',\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 10 — surfaces + closer\n {\n content: 'Work with Self-driving wherever you already are:',\n pause: 2000,\n },\n\n {\n type: 'lines',\n interval: 800,\n pause: 6000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>Inbox</Text>\n <Text dimColor>{' the PostHog web app'}</Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>Slack</Text>\n <Text dimColor>{' tag @PostHog'}</Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>MCP</Text>\n <Text dimColor>{' your agents and editors'}</Text>\n </Text>,\n ],\n },\n\n {\n content: 'Product usage data becomes real, shippable change.',\n pause: 5000,\n },\n\n { content: 'Your product drives itself.', pause: 10000 },\n ];\n};\n","import { join } from 'path';\nimport { access, rm } from 'node:fs/promises';\nimport type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport { OutroKind, type WizardSession } from '@lib/wizard-session';\nimport { createSkillProgram } from '../agent-skill/index.js';\nimport { SELF_DRIVING_PROGRAM } from './steps.js';\nimport {\n SELF_DRIVING_ABORT_CASES,\n getSelfDrivingDetectedTools,\n} from './detect.js';\nimport { buildSelfDrivingPrompt } from './prompt.js';\nimport { resolveSelfDrivingStepKey } from './step-keys.js';\nimport {\n NO_DEFAULT_LIMIT,\n PRICE_PER_PR_USD,\n PRICING_LONG,\n} from '../../ui/tui/decks/self-driving/pricing.js';\nimport { getTips } from '../../ui/tui/decks/self-driving/tips.js';\nimport { getContentBlocks } from '../../ui/tui/decks/self-driving/index.js';\n\nexport const SELF_DRIVING_SKILL_ID = 'self-driving-setup';\nconst REPORT_FILE = 'posthog-self-driving-report.md';\nconst DOCS_URL = 'https://posthog.com/docs';\nconst SUCCESS_MESSAGE =\n 'Self-driving is on. PostHog is scanning your project; first findings ' +\n 'hit your inbox within ~30 minutes.';\nconst WIZARD_MARKER = '.posthog-wizard';\n\n/**\n * Remove the installed setup skill. It is transient orchestration\n * knowledge (unlike integration skills such as the Next.js one, which\n * are worth keeping for the user's coding agents), so the program\n * cleans it up instead of showing the keep-skills prompt. Marker-\n * guarded: only directories the wizard installed are touched.\n */\nasync function removeInstalledSkill(installDir: string): Promise<void> {\n const skillDir = join(installDir, '.claude', 'skills', SELF_DRIVING_SKILL_ID);\n try {\n await access(join(skillDir, WIZARD_MARKER));\n } catch {\n return;\n }\n await rm(skillDir, { recursive: true, force: true }).catch(() => undefined);\n}\n\n// A session closure (not a static object) so `customPrompt` can read the\n// tools detected in the codebase — written to frameworkContext by the detect\n// step — and hand them to the prompt for STEP 4/STEP 5 prioritisation.\nconst buildRun = (session: WizardSession): Promise<ProgramRun> =>\n Promise.resolve({\n skillId: SELF_DRIVING_SKILL_ID,\n integrationLabel: SELF_DRIVING_SKILL_ID,\n customPrompt: (ctx) =>\n buildSelfDrivingPrompt(ctx, getSelfDrivingDetectedTools(session)),\n successMessage: SUCCESS_MESSAGE,\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Setting up PostHog Self-driving...',\n estimatedDurationMinutes: 10,\n abortCases: SELF_DRIVING_ABORT_CASES,\n // The flow legitimately needs several interactions (GitHub connect +\n // verify, issue-tracker picks, the scout-tailoring proposal), so raise\n // the wizard_ask budget a little above the default 10.\n maxQuestions: 13,\n // This flow hands the user long OAuth/authorize URLs (Linear, GitHub\n // fallback, Zendesk) in wizard_ask prompts. Render them as OSC 8\n // hyperlinks + clipboard copy so the overlay's line wrapping can't break\n // the click target. Scoped to this program only.\n richLinks: true,\n // STEP 3 (GitHub App install) and STEP 5 (Linear OAuth) park on wizard_ask\n // while the user does slow browser work; a first-time GitHub App install\n // routinely exceeds the 5-min default, and a timeout is indistinguishable\n // from a decline (both resolve to __cancelled__). Match upload-source-maps.\n askTimeoutMs: 30 * 60 * 1000,\n\n // Emit a `wizard: step` analytics event on each agent task transition so we\n // can build a step-level drop-off funnel (where a run stops — GitHub connect,\n // scout enable, etc.), including silent steps with no wizard_ask. Opt-in, so\n // only self-driving runs emit these; every other program is unchanged.\n trackStepProgress: true,\n\n // Key those events by step as well as by label, so a funnel over them (GitHub connect\n // conversion, scout enable rate) keeps counting when a run words its tasks differently.\n resolveStepKey: resolveSelfDrivingStepKey,\n\n postRun: async (session) => {\n await removeInstalledSkill(session.installDir);\n },\n\n buildOutroData: (_session, credentials) => {\n const uiHost = credentials.host.appHost.replace(/\\/$/, '');\n const inboxUrl = `${uiHost}/project/${credentials.projectId}/inbox`;\n return {\n kind: OutroKind.Success as const,\n message: SUCCESS_MESSAGE,\n primaryLink: { label: 'Your Self-driving inbox', url: inboxUrl },\n nextSteps: {\n heading: 'In your inbox you can:',\n items: [\n 'Investigate reports with the agent',\n 'Tag teammates to loop them in',\n `Kick off a PR when you like the proposed fix ($${PRICE_PER_PR_USD} flat)`,\n 'Cap the spend with a monthly PR limit in the sidebar',\n 'Or work from Slack (tag @PostHog) and MCP',\n ],\n },\n body: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`,\n reportFile: REPORT_FILE,\n };\n },\n });\n\nexport const selfDrivingConfig: ProgramConfig = {\n ...createSkillProgram({\n skillId: SELF_DRIVING_SKILL_ID,\n command: 'self-driving',\n id: 'self-driving',\n description: 'Set up PostHog Self-driving for this project',\n integrationLabel: SELF_DRIVING_SKILL_ID,\n successMessage: SUCCESS_MESSAGE,\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Setting up PostHog Self-driving...',\n estimatedDurationMinutes: 10,\n requires: ['posthog-integration'],\n abortCases: SELF_DRIVING_ABORT_CASES,\n }),\n steps: SELF_DRIVING_PROGRAM,\n run: buildRun,\n getTips,\n getContentBlocks,\n};\n\nexport { SELF_DRIVING_PROGRAM } from './steps.js';\nexport {\n detectSelfDrivingPrerequisites,\n SELF_DRIVING_ABORT_CASES,\n type SelfDrivingDetectError,\n} from './detect.js';\n","/**\n * MCP add / remove / tutorial programs.\n *\n * None of these run the agent pipeline — they're TUI-only flows invoked\n * by the `mcp add` / `mcp remove` / `mcp tutorial` subcommands in\n * bin.ts. They live in the program registry so the screen sequence is\n * derived alongside every other program (no special-cases in\n * screen-sequences.ts).\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\nimport { McpOutcome } from '@lib/wizard-session';\n\nexport const mcpAddConfig: ProgramConfig = {\n id: 'mcp-add',\n requiresAi: false,\n description: 'Add PostHog MCP server to supported clients',\n // Order: install → Slack → tutorial. Slack runs before the tutorial\n // because it renders gracefully without credentials (no surprise\n // OAuth on the loginless install path). The tutorial is last so its\n // explicit \"Start tutorial\" opt-in is the moment OAuth fires — and\n // skipping the tutorial doesn't bury Slack discovery behind a\n // dismissal screen.\n steps: [\n {\n id: 'mcp-add',\n label: 'Add MCP server',\n screenId: 'mcp-add',\n isComplete: (s) => s.mcpComplete,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n // Gate on a successful install so no-clients / skipped / failed\n // outcomes go straight to program end without a \"what's next\" prompt.\n show: (s) => s.mcpOutcome === McpOutcome.Installed,\n isComplete: (s) => s.slackStepDismissed,\n },\n {\n id: 'mcp-suggested-prompts',\n label: 'Suggested prompts',\n screenId: 'mcp-suggested-prompts',\n // Same install gate — without a working MCP there's nothing to\n // talk to from the tutorial.\n show: (s) => s.mcpOutcome === McpOutcome.Installed,\n isComplete: (s) => s.mcpSuggestedPromptsDismissed,\n // This step *is* the tutorial, so it reports there rather than to\n // `mcp-add`. Literal avoids a runtime cycle with the program registry;\n // the `ProgramId` type still catches a rename.\n reportsAsProgramId: 'mcp-tutorial',\n },\n ],\n};\n\n/**\n * `wizard mcp remove` — single-step uninstall flow.\n *\n * DO NOT append `mcp-suggested-prompts` (or any other tutorial-shaped\n * step) here. A user who just removed MCP is opting OUT of the agent having\n * access to PostHog; immediately pivoting into a tutorial that asks\n * them to log in and try prompts is wrong on intent and confusing on\n * UX. The screen also reads `session.mcpInstalledClients` for its\n * Choose-phase copy (\"MCP is installed for X\") — that array is empty\n * post-remove, so the copy would be a lie.\n *\n * If you want a \"did you mean to keep it?\" confirmation, build that as\n * a screen earlier in this program — don't reuse the tutorial.\n */\nexport const mcpRemoveConfig: ProgramConfig = {\n id: 'mcp-remove',\n requiresAi: false,\n description: 'Remove PostHog MCP server from supported clients',\n steps: [\n {\n id: 'mcp-remove',\n label: 'Remove MCP server',\n screenId: 'mcp-remove',\n isComplete: (s) => s.mcpComplete,\n },\n ],\n};\n\n/**\n * Standalone tutorial flow — boots directly into the Choose phase of\n * McpSuggestedPromptsScreen without going through MCP install first.\n * Useful for users who already installed MCP and want to revisit the\n * tutorial, or anyone who just wants to try the agent against PostHog\n * without touching their IDE config.\n *\n * The screen handles its own OAuth (via services.performLogin) so this\n * program doesn't pre-populate credentials.\n */\nexport const mcpTutorialConfig: ProgramConfig = {\n id: 'mcp-tutorial',\n requiresAi: false,\n description: 'Try the PostHog MCP with your agent — no install needed',\n steps: [\n {\n id: 'mcp-suggested-prompts',\n label: 'MCP tutorial',\n screenId: 'mcp-suggested-prompts',\n isComplete: (s) => s.mcpSuggestedPromptsDismissed,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n isComplete: (s) => s.slackStepDismissed,\n },\n ],\n};\n","import type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { createSkillProgram } from '@programs/agent-skill/index';\n\nconst MCP_ANALYTICS_REPORT_FILE = 'posthog-mcp-analytics-report.md';\n\n/**\n * `[ABORT]` reasons the mcp-analytics skill emits when the project can't be\n * instrumented. Kept in sync with the stop conditions in the skill's\n * `description.md` (context-mill `context/skills/mcp-analytics`).\n */\nexport const MCP_ANALYTICS_ABORT_CASES: AbortCase[] = [\n {\n match: /^unsupported language for mcp analytics$/i,\n errorCode: ErrorCodes.DetectUnsupportedPlatform,\n message: 'Unsupported language for MCP analytics',\n body:\n 'MCP analytics supports TypeScript/JavaScript (`@posthog/mcp`) and Python ' +\n '(`posthog.mcp`, shipped inside the `posthog` package). This project ' +\n \"doesn't look like either, so there's nothing to instrument. \" +\n 'See https://posthog.com/docs/mcp-analytics for the supported setups.',\n },\n {\n match: /^no mcp server found$/i,\n message: 'No MCP server found',\n body:\n 'This command instruments an existing MCP server with PostHog analytics, ' +\n 'but no MCP server was found in this project. If you just want PostHog ' +\n 'product analytics, run `npx @posthog/wizard` instead.',\n },\n {\n match: /^could not locate the server entry point$/i,\n message: 'Could not locate the MCP server entry point',\n body:\n \"This project has MCP signals, but the agent couldn't find where the \" +\n \"server is constructed or requests are dispatched, so there's nowhere \" +\n 'safe to add instrumentation. See https://posthog.com/docs/mcp-analytics ' +\n 'for the supported server styles, or point the wizard at the package ' +\n \"that defines the server if it's in a monorepo subdirectory.\",\n },\n];\n\n/**\n * `wizard mcp-analytics` — flat skill command.\n *\n * Instruments the user's own MCP server with the `@posthog/mcp` SDK so it\n * reports `$mcp_*` analytics about itself. This is the opposite of\n * `wizard mcp add` (which installs the PostHog MCP *server* into a coding\n * agent) — keep the two distinct.\n *\n * Flat while instrumenting is the only action. If an uninstrument / `remove`\n * leaf ever lands, restructure into a family with `familyCommandFactory` and\n * publish each leaf as a `cliEntries` entry with `parentCommand:\n * 'mcp-analytics'` from context-mill — a deliberate breaking change, done then,\n * not pre-emptively.\n */\nexport const mcpAnalyticsConfig = createSkillProgram({\n skillId: 'mcp-analytics',\n command: 'mcp-analytics',\n id: 'mcp-analytics',\n description: 'Add PostHog MCP Analytics to your MCP server',\n integrationLabel: 'mcp-analytics',\n customPrompt:\n \"Instrument this project's MCP server with PostHog MCP analytics. Run the \" +\n '`mcp-analytics` skill end-to-end: detect the server style, install ' +\n '`@posthog/mcp` and `posthog-node`, wrap the server (or use `PostHogMCP` ' +\n 'for a custom dispatcher), wire the project API key and host, and verify. ' +\n 'Make only additive changes — do not alter tool behavior. The final report ' +\n `is written to ./${MCP_ANALYTICS_REPORT_FILE}.`,\n successMessage: `MCP analytics configured! View the report at ./${MCP_ANALYTICS_REPORT_FILE}`,\n reportFile: MCP_ANALYTICS_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/mcp-analytics',\n spinnerMessage: 'Setting up MCP analytics...',\n estimatedDurationMinutes: 5,\n abortCases: MCP_ANALYTICS_ABORT_CASES,\n});\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/index';\nimport { getContentBlocks } from '@ui/tui/decks/agent-skill/index';\nimport { headlessOption, regionOption } from '@lib/headless-mode';\n\nconst AI_OBSERVABILITY_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.map((step) =>\n step.id === 'intro' ? { ...step, screenId: 'ai-observability-intro' } : step,\n);\n\nconst AI_OBSERVABILITY_REPORT_FILE = 'posthog-ai-observability-report.md';\n\n/**\n * `wizard ai-observability` — wrap the project's LLM client calls so they emit\n * `$ai_generation` events into AI Observability.\n *\n * No `run.skillId`: the context-mill `ai-observability` group ships one variant\n * per (LLM provider × language) and the wizard does no provider detection —\n * the agent loads the menu, matches the manifest's vendor SDK, and installs\n * the right variant itself (see `customPrompt`). Stays flat while a single\n * \"add AIO to a project\" flow is the only action.\n */\nexport const aiObservabilityConfig: ProgramConfig = {\n command: 'ai-observability',\n description: 'Add PostHog AI Observability to your LLM calls',\n id: 'ai-observability',\n cliOptions: { ...headlessOption, ...regionOption },\n steps: AI_OBSERVABILITY_STEPS,\n reportFile: AI_OBSERVABILITY_REPORT_FILE,\n getContentBlocks,\n run: {\n integrationLabel: 'ai-observability',\n // No `skillId`: linear.ts skips its pre-install step (see the gate on\n // `linear.ts:47`), so the agent must load the menu and install the right\n // variant itself. The prompt below tells it how.\n customPrompt:\n () => `Instrument this project's LLM calls with PostHog AI Observability.\n\nThis flow has no pre-installed skill — you install the right one yourself:\n\n1. Call \\`load_skill_menu\\` with \\`category: \"ai-observability\"\\`. The menu is\n the source of truth: one variant per (LLM provider × language), plus a\n \\`manual-capture\\` variant for projects with no vendor SDK.\n\n2. Scan the project manifest (\\`package.json\\`, \\`pyproject.toml\\`,\n \\`requirements.txt\\`) for a vendor LLM SDK and pick the variant that matches\n it — the language follows the manifest (\\`package.json\\` → Node, Python\n tooling → Python). Multiple SDKs (e.g. LangChain wrapping OpenAI) → prefer\n the higher abstraction. No vendor SDK → the \\`manual-capture\\` variant.\n Genuinely ambiguous → \\`wizard_ask\\` with a multi-choice picker.\n\n3. Call \\`install_skill\\` with the picked variant id. Then follow that skill's\n \\`SKILL.md\\` and references end-to-end. The skill itself will install\n packages, wire OTel, set env vars, and describe verification.\n\nMake only additive changes — do not touch existing PostHog init, identify\ncalls, event capture, or dashboards. Those belong to other skills. The\nfinal report is written to ./${AI_OBSERVABILITY_REPORT_FILE}.`,\n successMessage: `AI Observability configured! View the report at ./${AI_OBSERVABILITY_REPORT_FILE}`,\n reportFile: AI_OBSERVABILITY_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/ai-observability',\n spinnerMessage: 'Setting up AI Observability...',\n estimatedDurationMinutes: 5,\n },\n};\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/index';\nimport { getContentBlocks } from '@ui/tui/decks/agent-skill/index';\n\nconst METRICS_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.map((step) =>\n step.id === 'intro' ? { ...step, screenId: 'metrics-intro' } : step,\n);\n\nconst METRICS_REPORT_FILE = 'posthog-metrics-report.md';\n\n/**\n * `wizard metrics` — instrument the project with PostHog application metrics\n * (`posthog.metrics` counters, gauges, and histograms).\n *\n * No `run.skillId`: the context-mill `metrics` group ships one variant per\n * platform (python, nodejs, javascript, kubernetes, other/OTLP) and the agent\n * pulls the matching one itself — the flow's tasks and the linear\n * `customPrompt` both pick from the menu and install it. Stays flat while a\n * single \"add metrics to a project\" flow is the only action.\n */\nexport const metricsConfig: ProgramConfig = {\n command: 'metrics',\n description: 'Add PostHog application metrics to your project',\n id: 'metrics',\n // Orchestrator flow (context-mill `context/agents/metrics`): the seed queues\n // verify-sdk → instrument-metrics → report; the tasks install the matching\n // platform variant themselves. Explicit so renaming the program can't\n // silently detach the flow.\n agentFlow: 'metrics',\n steps: METRICS_STEPS,\n reportFile: METRICS_REPORT_FILE,\n getContentBlocks,\n run: {\n integrationLabel: 'metrics',\n // No `skillId`: the agent must load the menu and install the right\n // variant itself. The prompt below tells it how.\n customPrompt:\n () => `Instrument this project with PostHog application metrics.\n\nThis flow has no pre-installed skill — you install the right one yourself:\n\n1. Call \\`load_skill_menu\\` with \\`category: \"metrics\"\\`. The menu is the\n source of truth: one variant per platform.\n\n2. Pick the variant that matches the project:\n - Python tooling (\\`pyproject.toml\\`, \\`requirements.txt\\`, \\`Pipfile\\`) →\n \\`metrics-python\\` (needs \\`posthog\\` >= 7.23.0)\n - \\`package.json\\` with server-side Node code → \\`metrics-nodejs\\`\n (needs \\`posthog-node\\` >= 5.43.0)\n - \\`package.json\\` that is browser-only → \\`metrics-javascript\\`\n (needs \\`posthog-js\\` >= 1.399.0)\n - Kubernetes manifests / Helm charts and the user wants cluster-level\n scraping → \\`metrics-kubernetes\\`\n - Any other language → \\`metrics-other\\` (plain OTLP exporter)\n A full-stack app (e.g. Next.js) usually wants the server variant — metrics\n measure service work, not user actions. Genuinely ambiguous →\n \\`wizard_ask\\` with a multi-choice picker.\n\n3. Call \\`install_skill\\` with the picked variant id. Then follow that skill's\n \\`SKILL.md\\` and references end-to-end — it covers where to place metrics\n (middleware, background jobs, external calls, business commit sites) and\n the low-cardinality attribute rules.\n\nMake only additive changes — reuse an existing PostHog client by adding the\n\\`metrics\\` config to it rather than constructing a second client, and do not\ntouch existing identify calls, event capture, or dashboards. The final report\nis written to ./${METRICS_REPORT_FILE}.`,\n successMessage: `Application metrics configured! View the report at ./${METRICS_REPORT_FILE}`,\n reportFile: METRICS_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/metrics',\n spinnerMessage: 'Setting up application metrics...',\n estimatedDurationMinutes: 5,\n },\n};\n","/**\n * Slack connect program — TUI-only flow invoked by `wizard slack`. One\n * step: the same Connect Slack screen the MCP flows end on. The screen\n * renders the no-creds nudge (marketing copy + \"Open Slack setup\" link);\n * we deliberately don't force OAuth here because connecting Slack itself\n * happens in the browser, so a wizard login adds nothing for the user.\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\n\nexport const slackConnectConfig: ProgramConfig = {\n id: 'slack',\n description: 'Connect PostHog to your Slack',\n steps: [\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n isComplete: (s) => s.slackStepDismissed,\n },\n ],\n};\n","/**\n * Central registry of all wizard programs.\n *\n * Adding a new program:\n * 1. Create src/programs/<name>/ with index.ts exporting a ProgramConfig\n * 2. Import and add it to PROGRAM_REGISTRY below\n * 3. (If custom intro screen) add to src/ui/tui/screen-registry.tsx\n *\n * screen-sequences.ts, store.ts, and bin.ts all derive their wiring from\n * this array — no need to touch those files when adding a program.\n */\n\nimport type { ProgramConfig } from './program-step.js';\nimport { POSTHOG_DOCS_URL } from '@shared/constants.js';\nimport { posthogIntegrationConfig } from './posthog-integration/index.js';\nimport { revenueAnalyticsConfig } from './revenue-analytics/index.js';\nimport { warehouseSourceConfig } from './warehouse-source/index.js';\nimport { auditConfig } from './audit/index.js';\nimport { eventsAuditConfig } from './events-audit/index.js';\nimport { posthogDoctorConfig } from './posthog-doctor/index.js';\nimport { webAnalyticsDoctorConfig } from './web-analytics-doctor/index.js';\nimport { migrationConfig } from './migration/index.js';\nimport { errorTrackingUploadSourceMapsConfig } from './error-tracking-upload-source-maps/index.js';\nimport { errorTrackingConfig } from './error-tracking/index.js';\nimport { selfDrivingConfig } from './self-driving/index.js';\nimport { AGENT_SKILL_STEPS } from './agent-skill/index.js';\nimport { getContentBlocks as agentSkillContentBlocks } from '../ui/tui/decks/agent-skill/index.js';\nimport {\n mcpAddConfig,\n mcpRemoveConfig,\n mcpTutorialConfig,\n} from './mcp/index.js';\nimport { mcpAnalyticsConfig } from './mcp-analytics/index.js';\nimport { replayVisionConfig } from './replay-vision/index.js';\nimport { aiObservabilityConfig } from './ai-observability/index.js';\nimport { metricsConfig } from './metrics/index.js';\nimport { slackConnectConfig } from './slack/index.js';\n\n// Generic skill program — runs an arbitrary context-mill skill chosen at\n// dispatch time (session.skillId) rather than a registered named program.\n// Backs `wizard skill <name>` and the narrow `audit` leaves (events,\n// feature-flags, identify, session-replay, autocapture); each injects its\n// skillId onto the config, which lands on session.skillId before the run.\n//\n// The `run` recipe is a function rather than a static block because the\n// skillId isn't known until dispatch. Without a `run` recipe the runner's\n// `skipAgent` guard (run-wizard.ts) fires and the skill never executes — so we\n// derive generic run metadata from the resolved skill id at run time.\nexport const agentSkillConfig: ProgramConfig = {\n id: 'agent-skill',\n description: 'Run an arbitrary context-mill skill',\n steps: AGENT_SKILL_STEPS,\n getContentBlocks: agentSkillContentBlocks,\n allowedTools: ['Agent'],\n run: (session) => {\n const skillId = session.skillId ?? 'agent-skill';\n return Promise.resolve({\n skillId,\n integrationLabel: skillId,\n spinnerMessage: `Running ${skillId}...`,\n successMessage: `${skillId} complete!`,\n estimatedDurationMinutes: 5,\n reportFile: `posthog-${skillId}-report.md`,\n docsUrl: POSTHOG_DOCS_URL,\n });\n },\n};\n\nexport const PROGRAM_REGISTRY = [\n posthogIntegrationConfig,\n revenueAnalyticsConfig,\n warehouseSourceConfig,\n errorTrackingUploadSourceMapsConfig,\n errorTrackingConfig,\n auditConfig,\n eventsAuditConfig,\n posthogDoctorConfig,\n webAnalyticsDoctorConfig,\n migrationConfig,\n selfDrivingConfig,\n agentSkillConfig,\n mcpAddConfig,\n mcpRemoveConfig,\n mcpTutorialConfig,\n mcpAnalyticsConfig,\n replayVisionConfig,\n aiObservabilityConfig,\n metricsConfig,\n slackConnectConfig,\n] as const satisfies readonly ProgramConfig[];\n\n/**\n * Typed program names. Values come from each config's `id`, so there's\n * no parallel string list to keep in sync — adding `Program.Foo` here is\n * just exposing `fooConfig.id` under a friendly name for call sites.\n */\nexport const Program = {\n PostHogIntegration: posthogIntegrationConfig.id,\n RevenueAnalyticsSetup: revenueAnalyticsConfig.id,\n WarehouseSource: warehouseSourceConfig.id,\n ErrorTrackingUploadSourceMaps: errorTrackingUploadSourceMapsConfig.id,\n ErrorTracking: errorTrackingConfig.id,\n Migration: migrationConfig.id,\n Audit: auditConfig.id,\n EventsAudit: eventsAuditConfig.id,\n PosthogDoctor: posthogDoctorConfig.id,\n WebAnalyticsDoctor: webAnalyticsDoctorConfig.id,\n SelfDriving: selfDrivingConfig.id,\n AgentSkill: agentSkillConfig.id,\n McpAdd: mcpAddConfig.id,\n McpRemove: mcpRemoveConfig.id,\n McpTutorial: mcpTutorialConfig.id,\n McpAnalytics: mcpAnalyticsConfig.id,\n ReplayVision: replayVisionConfig.id,\n AiObservability: aiObservabilityConfig.id,\n Metrics: metricsConfig.id,\n SlackConnect: slackConnectConfig.id,\n} as const;\n\n/** Compile-time union of every registered program id. */\nexport type ProgramId = (typeof PROGRAM_REGISTRY)[number]['id'];\n\n/**\n * Look up a program config by its id. `ProgramId` is a union of every\n * registered id, so the lookup is statically guaranteed to find a match\n * — the `!` is a load-bearing assertion of that invariant, not a hope.\n */\nexport function getProgramConfig(id: ProgramId): ProgramConfig {\n return PROGRAM_REGISTRY.find((c) => c.id === id)!;\n}\n\n/** A program config that is exposed as a CLI subcommand. */\nexport type SubcommandProgram = ProgramConfig & { command: string };\n\n/** All program configs that are exposed as CLI subcommands. */\nexport function getSubcommandPrograms(): SubcommandProgram[] {\n return PROGRAM_REGISTRY.filter(\n (c): c is SubcommandProgram => c.command != null,\n );\n}\n\n/** What a user types to reach the program. Nested ones go through its parent. */\nexport function getCommandPath(config: SubcommandProgram): string {\n return config.parentCommand\n ? `${config.parentCommand} ${config.command}`\n : config.command;\n}\n\n/** What the intro offers, in order. Curated: no config field ranks these. */\nconst INTRO_PROGRAMS = [\n 'self-driving',\n 'error-tracking-upload-source-maps',\n 'warehouse-source',\n 'audit',\n 'posthog-doctor',\n 'mcp-analytics',\n 'replay-vision',\n 'ai-observability',\n 'metrics',\n 'revenue-analytics-setup',\n];\n\n/** The programs the intro can hand off to, in the order it lists them. */\nexport function getLaunchablePrograms(): SubcommandProgram[] {\n const byId = new Map(getSubcommandPrograms().map((c) => [c.id, c]));\n return INTRO_PROGRAMS.map((id) => byId.get(id)).filter(\n (config): config is SubcommandProgram => config != null,\n );\n}\n","import { OutroKind, type WizardSession } from '@lib/wizard-session';\n\nexport const MINT_FAILURE_MESSAGE = \"The Wizard's a little busy\";\n\nexport const MINT_FAILURE_BODY =\n 'The wizard can still share spells. Grab a skill and let your agent take over.';\n\nexport const MINT_FAILURE_CONTACT =\n 'Email wizard@posthog.com and tell us what happened. Please attach this log:';\n\n/** The agent run ended in an error, whatever the reason. Login failures also\n * set an error outro but never had credentials, so they stay on the outro. */\nexport function isRunFailure(\n session: Pick<WizardSession, 'outroData' | 'credentials'>,\n): boolean {\n return (\n session.outroData?.kind === OutroKind.Error && session.credentials !== null\n );\n}\n"],"mappings":"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SAAS;GACT,OAAO;GACP,MAAA;GACA,mBAAmB;GACpB;EAED;GAAE,SAAS;GAA2B,OAAO;GAAM;EAEnD;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,OAAO;GACP,SAAS;GACT,SAAS,oBAAC,mBAAD,EAA0B,OAAS,CAAA;GAC7C;EAED;GACE,OAAO;GACP,SACE,qBAAC,MAAD,EAAA,UAAA;IAAM;IACE;IACN,oBAAC,MAAD;KAAM,OAAO,OAAO;KAAQ,MAAA;eAAK;KAE1B,CAAA;IAAC;IAAI;IAEP,EAAA,CAAA;GAEV;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SACE;GACF,OAAO;GACR;EAED;GAAE,SAAS;GAA8C,OAAO;GAAM;EAEtE;GAAE,SAAS;GAA2C,OAAO;GAAM;EAEnE;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GAAE,SAAS;GAAgD,OAAO;GAAM;EAExE;EAGA;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAEA;GAAE,SAAS;GAA0B,OAAO;GAAM;EAElD;EAEA;EAGA;GAAE,SAAS;GAA4C,OAAO;GAAM;EAEpE;GACE,SAAS;GACT,OAAO;GACR;EAED;GAGE,SAAS;GACT,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GAAE,SAAS;GAA6C,OAAO;GAAM;EAErE;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,MAAM;GACN,UAAU;GACV,OAAO;G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"}
1
+ {"version":3,"file":"mint-failure-u8xQKNOn.js","names":["buildPrompt","needsSetup","getContentBlocks","getContentBlocks","DOCS_URL","SETUP_REPORT_FILE","REPORT_FILE","DOCS_URL","getContentBlocks","getContentBlocks","fs","path","REPORT_FILE","DOCS_URL","getContentBlocks","abortUnsupportedPlatform","getTips","agentSkillContentBlocks"],"sources":["../src/programs/detection/framework.ts","../src/programs/detection/context.ts","../src/programs/detection/agentic.ts","../src/programs/detection/project-scope.ts","../src/programs/shared/health-check-step.ts","../src/programs/shared/package-scanning.ts","../src/programs/posthog-integration/detect.ts","../src/programs/posthog-integration/steps.ts","../src/ui/tui/styles.ts","../src/ui/tui/primitives/text-helpers.ts","../src/ui/tui/primitives/content-types.ts","../src/ui/tui/primitives/layout-helpers.ts","../src/ui/tui/primitives/TextBlock.tsx","../src/ui/tui/components/StatusPeekTrigger.tsx","../src/ui/tui/decks/posthog-integration/data-flow.tsx","../src/ui/tui/decks/posthog-integration/product-suite.tsx","../src/ui/tui/decks/posthog-integration/line-chart.tsx","../src/ui/tui/decks/posthog-integration/funnel.tsx","../src/ui/tui/decks/posthog-integration/index.tsx","../src/programs/posthog-integration/handoff.ts","../src/programs/posthog-integration/index.ts","../src/programs/revenue-analytics/detect.ts","../src/programs/revenue-analytics/steps.ts","../src/ui/tui/decks/agent-skill/index.tsx","../src/programs/revenue-analytics/index.ts","../src/programs/warehouse-source/steps.ts","../src/programs/warehouse-source/index.ts","../src/programs/agent-skill/steps.ts","../src/programs/agent-skill/index.ts","../src/programs/audit/detect.ts","../src/programs/audit/seed.ts","../src/programs/audit/index.ts","../src/programs/events-audit/steps.ts","../src/programs/events-audit/seed.ts","../src/programs/events-audit/index.ts","../src/programs/posthog-doctor/steps.ts","../src/programs/posthog-doctor/types.ts","../src/programs/posthog-doctor/fetch.ts","../src/programs/posthog-doctor/kind-metadata.ts","../src/programs/posthog-doctor/index.ts","../src/programs/web-analytics-doctor/detect.ts","../src/programs/web-analytics-doctor/steps.ts","../src/programs/web-analytics-doctor/index.ts","../src/programs/migration/steps.ts","../src/ui/tui/decks/migration/vendor-stack.tsx","../src/ui/tui/decks/migration/free-tier.tsx","../src/ui/tui/decks/migration/pricing-structure.tsx","../src/ui/tui/decks/migration/index.tsx","../src/programs/migration/index.ts","../src/programs/error-tracking-upload-source-maps/detect.ts","../src/programs/error-tracking-upload-source-maps/steps.ts","../src/programs/error-tracking-upload-source-maps/prompt.ts","../src/ui/tui/decks/error-tracking-upload-source-maps/index.tsx","../src/steps/install-cli-steering/index.ts","../src/programs/shared/posthog-cli-preinstall.ts","../src/programs/error-tracking-upload-source-maps/index.ts","../src/ui/tui/decks/error-tracking/index.tsx","../src/programs/replay-vision/index.ts","../src/ui/tui/decks/error-tracking/tips.ts","../src/programs/error-tracking/detect-agentic.ts","../src/programs/error-tracking/index.ts","../src/programs/self-driving/detect.ts","../src/programs/self-driving/detect-agentic.ts","../src/programs/self-driving/steps.ts","../src/programs/self-driving/prompt.ts","../src/programs/self-driving/step-keys.ts","../src/ui/tui/decks/self-driving/pricing.ts","../src/ui/tui/decks/self-driving/tips.ts","../src/ui/tui/decks/self-driving/pipeline-diagram.tsx","../src/ui/tui/decks/self-driving/index.tsx","../src/programs/self-driving/index.ts","../src/programs/mcp/index.ts","../src/programs/mcp-analytics/index.ts","../src/programs/ai-observability/index.ts","../src/programs/metrics/index.ts","../src/programs/slack/index.ts","../src/programs/program-registry.ts","../src/ui/mint-failure.ts"],"sourcesContent":["/**\n * Framework detection — identify which PostHog-supported framework\n * is present in the project directory.\n *\n * Pure function: takes an install dir, returns the detected integration\n * (or undefined). No store mutations, no UI calls.\n */\n\nimport { Integration, DETECTION_TIMEOUT_MS } from '@shared/constants';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\n\n/**\n * Loop through all registered frameworks and return the first one\n * whose `detect()` predicate matches the given directory.\n * Returns undefined if no framework is detected or detection times out.\n */\nexport async function detectFramework(\n installDir: string,\n): Promise<Integration | undefined> {\n for (const integration of Object.values(Integration)) {\n const config = FRAMEWORK_REGISTRY[integration];\n try {\n const detected = await Promise.race([\n config.detection.detect({ installDir }),\n new Promise<false>((resolve) =>\n setTimeout(() => resolve(false), DETECTION_TIMEOUT_MS),\n ),\n ]);\n if (detected) {\n return integration;\n }\n } catch {\n // Skip frameworks whose detection throws\n }\n }\n}\n","/**\n * Framework context gathering — run gatherContext and version checks\n * for a detected framework.\n *\n * Pure functions: take a framework config and options, return results.\n * No store mutations, no UI calls.\n */\n\nimport * as semver from 'semver';\nimport { DETECTION_TIMEOUT_MS } from '@shared/constants';\nimport type { FrameworkConfig } from '@programs/framework-config';\nimport type { WizardRunOptions } from '@utils/types';\n\n/**\n * Run a framework's `gatherContext()` to collect variant-specific\n * metadata (e.g., router type for Next.js, Expo vs bare for React Native).\n *\n * Returns the gathered context, or an empty object on failure/timeout.\n */\nexport async function gatherFrameworkContext(\n config: FrameworkConfig,\n options: WizardRunOptions,\n): Promise<Record<string, unknown>> {\n if (!config.metadata.gatherContext) return {};\n\n try {\n return await Promise.race([\n config.metadata.gatherContext(options),\n new Promise<Record<string, never>>((resolve) =>\n setTimeout(() => resolve({}), DETECTION_TIMEOUT_MS),\n ),\n ]);\n } catch {\n return {};\n }\n}\n\nexport interface VersionCheckResult {\n /** Whether the installed version is supported */\n supported:\n | true\n | {\n current: string;\n minimum: string;\n docsUrl: string;\n };\n}\n\n/**\n * Check whether the installed framework version meets the minimum requirement.\n *\n * Returns `{ supported: true }` if the version is fine (or no check is needed).\n * Returns the version details if unsupported.\n */\nexport async function checkFrameworkVersion(\n config: FrameworkConfig,\n options: WizardRunOptions,\n): Promise<VersionCheckResult> {\n if (\n !config.detection.minimumVersion ||\n !config.detection.getInstalledVersion\n ) {\n return { supported: true };\n }\n\n const version = await config.detection.getInstalledVersion(options);\n if (!version) return { supported: true };\n\n const coerced = semver.coerce(version);\n if (coerced && semver.lt(coerced, config.detection.minimumVersion)) {\n return {\n supported: {\n current: version,\n minimum: config.detection.minimumVersion,\n docsUrl:\n config.metadata.unsupportedVersionDocsUrl ?? config.metadata.docsUrl,\n },\n };\n }\n\n return { supported: true };\n}\n","/**\n * Agentic project detection.\n *\n * A reusable detection tool that drives an agent over the repo: it finds\n * project roots, resolves monorepo/workspace markers, reads package manifests,\n * classifies each project against a caller-supplied set of targets, and reports\n * which projects already have a PostHog SDK installed.\n *\n * Product-knowledge-free by design — the caller passes the targets to classify\n * into (e.g. source-map skill variants) and maps the result back. Sits next to\n * the other detection tools (framework, features, package-manager) so it's\n * discoverable. Runs AFTER auth: it goes through the same `runAgent` every\n * program uses, which needs credentials.\n */\n\nimport {\n buildRunTags,\n runAgent,\n RunOutcome,\n resolveScanBindings,\n} from '@agent';\nimport type {\n AgentProgress,\n AgentRunDefinition,\n SwitchboardCtx,\n RunConfig,\n RunInput,\n} from '@agent/types';\nimport { isAbsolute, resolve, sep } from 'path';\nimport { z } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { ErrorCodes } from '@shared/errors';\nimport { detectNodePackageManagers } from './package-manager.js';\nimport {\n AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS,\n AGENTIC_DETECTION_RETRY_TIMEOUT_MS,\n CallType,\n getSkillsBaseUrl,\n POSTHOG_DOCS_URL,\n} from '@shared/constants';\nimport { analytics } from '@utils/analytics';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { getUI } from '@ui';\nimport { createUiReducer } from '@ui/agent-progress';\n\n/** A category the agent classifies each project into (id the agent returns). */\nexport type DetectTarget = { id: string; name: string };\n\n/** One project the agent found in the repo. */\nexport type AgenticProject = {\n /** Path relative to the working directory (\".\" for the repo root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A target id when the project matches one of the supplied targets, else null. */\n targetId: string | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** True on the one project picked as the main user-facing app; only present when the scan set `recommend`. */\n recommended?: boolean;\n};\n\nexport type AgenticDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: AgenticProject[];\n};\n\nexport class AgenticDetectionTimeoutError extends Error {\n constructor(attempt: number, timeoutMs: number) {\n super(\n `Project scan attempt ${attempt} timed out after ${timeoutMs / 1000}s`,\n );\n this.name = 'AgenticDetectionTimeoutError';\n }\n}\n\n/** Streaming progress callback — one short activity line per agent step. */\nexport type DetectEvent = (line: string) => void;\n\n/**\n * Every project-manifest / workspace-marker filename the wizard's frameworks\n * are identified by. One brace-expansion Glob over all of these locates every\n * project root in the repo in a single call, regardless of language.\n *\n * Counterpart: POSTHOG_MANIFESTS in @programs/self-driving/detect (SDK\n * grep); keep the two in sync.\n */\nexport const PROJECT_MANIFESTS: readonly string[] = [\n // JS/TS + workspace markers\n 'package.json',\n 'pnpm-workspace.yaml',\n 'turbo.json',\n 'nx.json',\n 'lerna.json',\n // Python\n 'requirements.txt',\n 'pyproject.toml',\n 'setup.py',\n 'Pipfile',\n 'manage.py',\n // Ruby / PHP\n 'Gemfile',\n 'composer.json',\n // Java\n 'pom.xml',\n // Rust\n 'Cargo.toml',\n // Elixir\n 'mix.exs',\n // Go\n 'go.mod',\n // .NET: no framework targets yet, but found so\n // an existing PostHog SDK is reported (feeds self-driving's \"continue\" path).\n '*.csproj',\n // Mobile / native\n 'Package.swift',\n 'Podfile',\n 'project.yml',\n 'project.pbxproj',\n 'pubspec.yaml',\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n // Gradle version catalog (holds dependency coordinates).\n 'gradle/libs.versions.toml',\n];\n\n/** The single brace-expansion glob covering every supported manifest. */\nexport function manifestGlob(): string {\n return `**/{${PROJECT_MANIFESTS.join(',')}}`;\n}\n\nexport type AgenticDetectOptions = {\n /**\n * Categories to classify each project into. Order matters: list targets by\n * priority, most specific first. When a project could match more than one,\n * the agent keeps the earliest — so the caller encodes precedence purely\n * through ordering (e.g. a bundler target before a generic framework target).\n */\n targets: readonly DetectTarget[];\n /** The program this scan bills to. Required, not optional: the scan drives a\n * real agent through the gateway, and a caller that forgets leaves that\n * spend unattributed. */\n programId: string;\n /** One short clause describing what the scan is for (frames the prompt). */\n purpose?: string;\n /** Ask the agent to label exactly one project `recommended` (the main client app). Off by default. */\n recommend?: boolean;\n /**\n * Target ids whose technologies co-occur in one manifest (e.g. the JS\n * family). Among these, the enumeration's priority winner overrides the\n * model's pick; elsewhere a valid pick always wins — see coerceAgenticReport.\n */\n rerankIds?: readonly string[];\n /** Streaming activity callback for the UI. */\n onEvent?: DetectEvent;\n};\n\nfunction buildPrompt(\n cwd: string,\n targets: readonly DetectTarget[],\n purpose: string,\n recommend: boolean,\n): string {\n const targetList = targets.map((t) => `- ${t.id} → ${t.name}`).join('\\n');\n // matchingTargets precedes targetId: the model enumerates before it picks.\n const projectShape = `{\"path\":string,\"framework\":string,\"matchingTargets\":string[],\"targetId\":string|null,\"hasPostHog\":boolean,\"evidence\":string${\n recommend ? ',\"recommended\":boolean' : ''\n }}`;\n return [\n `You are scanning a code repository to ${purpose}. Be fast and mechanical — do not over-explore. Keep any reasoning to one short sentence, then run the tool calls and output the JSON.`,\n '',\n `Working directory: ${cwd}`,\n '',\n 'A \"project\" is a directory containing one or more of the manifest files below. Find projects by their manifests, NOT by walking directories — never report a directory that has no manifest.',\n '',\n 'Do exactly this:',\n `1. Run Glob ONCE with this pattern to find every project manifest in the repo in a single call: \"${manifestGlob()}\". Discard any result whose path contains node_modules/, dist/, build/, .next/, out/, coverage/, vendor/, .venv/, site-packages/, target/, Pods/, Carthage/, or DerivedData/. Group the remaining results by directory — each directory is one project. Three exceptions to \"directory = project\": a project.pbxproj lives inside a \"<Name>.xcodeproj/\" wrapper, so the project root is the PARENT of that .xcodeproj directory; a project.yml at a directory root is an XcodeGen-generated Xcode app rooted at that directory; a gradle/libs.versions.toml is a version catalog belonging to the gradle project rooted at the PARENT of that gradle/ directory (read it alongside the build.gradle when deciding hasPostHog), never its own project.`,\n '2. Decide repoType: \"monorepo\" if the root package.json has a \"workspaces\" field OR a pnpm-workspace.yaml / turbo.json / nx.json / lerna.json was found at the root, else \"single\".',\n `3. For EACH project directory, ONE AT A TIME: Read its manifest(s) ONCE, decide the fields below, then IMMEDIATELY — before reading any other project — write that project's verdict as one JSON line of shape ${projectShape}. Never write a verdict from memory of an earlier Read; the manifest you just read is the only source. Decide from its dependency lists:`,\n ' - the human-readable framework name (e.g. \"Next.js\", \"Django\", \"Rails\"),',\n ' - matchingTargets: EVERY target id from the list below whose technology appears in THIS manifest, in the priority order of the list. An id belongs here only if you can point at the dependency or setting in the manifest you just read — never carry one over from another project,',\n ' - targetId: the FIRST entry of matchingTargets (the list is ordered by priority, most specific first). null when matchingTargets is empty,',\n ` - hasPostHog: true if any dependency is a PostHog SDK. This includes: a name containing \"posthog\" in any ecosystem (e.g. posthog-js, posthog-node, @posthog/*, posthog for pip/gem/hex, a com.posthog:* gradle/maven coordinate, a PostHog NuGet PackageReference); an SPM package named \"PostHog\" or a repositoryURL of github.com/PostHog/posthog-ios (in Package.swift or a .pbxproj); or a \"pod 'PostHog'\" line in a Podfile. Else false.`,\n ' - evidence: the manifest fact that decided targetId, with the file it came from (e.g. \"rollup in devDependencies of backend/package.json\"). When targetId is null, name the closest fact you saw. One short clause, quoted from the manifest you just read.',\n ' Do NOT read any file other than these manifests.',\n ...(recommend\n ? [\n '4. Pick exactly ONE project as recommended — the main user-facing client application. Prefer a frontend web app or a mobile app over backend servers/APIs, libraries, CLIs, tooling, and example/demo/docs apps. If several client apps exist, pick the primary production app; if no client app exists, pick the primary application project.',\n ]\n : []),\n '',\n 'Target ids (id → name), in priority order — most specific first; if several match a project, keep the one listed earliest:',\n targetList,\n '',\n 'Output requirements:',\n '- After the last verdict line, respond with ONLY a single JSON object assembling the verdict lines you wrote, copied VERBATIM — same path, framework, targetId and evidence per project. No prose, no markdown code fences.',\n `- Shape: {\"repoType\":\"monorepo\"|\"single\",\"projects\":[${projectShape}]}`,\n '- \"path\" is the project directory relative to the working directory; use \".\" for the repo root.',\n '- \"targetId\" MUST be exactly one of the target ids above when it matches; if several match, use the one listed earliest; otherwise null.',\n '- Include projects whose stack matches no target too (targetId: null).',\n ...(recommend\n ? [\n '- Exactly one project has \"recommended\": true; every other project has \"recommended\": false.',\n ]\n : []),\n '- If there are no manifests at all, return {\"repoType\":\"single\",\"projects\":[]}.',\n ].join('\\n');\n}\n\n/**\n * Build the detection report from the agent's output, or null when it holds\n * neither a project verdict nor a `projects` array. Exported for testing.\n *\n * The verdict lines are far more reliable than the model's final assembly\n * (prose, pretty-printing, per-object fences), so every parseable line\n * contributes: objects with a `path` merge by path (last wins), and a\n * one-line assembly's `projects` merge the same way. `repoType` is\n * approximated from the count — it only feeds telemetry and display.\n */\nexport function deriveReportJson(text: string): unknown | null {\n const byPath = new Map<string, Record<string, unknown>>();\n let sawReport = false;\n for (const line of text.split('\\n')) {\n const start = line.indexOf('{');\n const end = line.lastIndexOf('}');\n if (start === -1 || end <= start) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line.slice(start, end + 1));\n } catch {\n continue; // not JSON — prose, shape echoes, pretty-printed fragments\n }\n const obj = (parsed ?? {}) as Record<string, unknown>;\n sawReport ||= Array.isArray(obj.projects);\n const candidates = Array.isArray(obj.projects) ? obj.projects : [obj];\n for (const candidate of candidates) {\n const p = (candidate ?? {}) as Record<string, unknown>;\n if (typeof p.path === 'string') byPath.set(p.path, p);\n }\n }\n if (byPath.size === 0 && !sawReport) return null;\n const projects = [...byPath.values()];\n return { repoType: projects.length > 1 ? 'monorepo' : 'single', projects };\n}\n\n/**\n * Clamp an agent-reported project path to a safe repo-relative dir. The path\n * is LLM output: absolute paths or `..` segments could steer later phases\n * (e.g. integrate-run's targetDir) outside the repo, so they clamp to '.'.\n */\nfunction coercePath(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '.';\n if (isAbsolute(raw) || raw.split(/[\\\\/]/).includes('..')) return '.';\n return raw;\n}\n\n/** Resolve an agent-reported path to an absolute dir clamped to the root — the caller-side counterpart of coercePath. */\nexport function resolveProjectDir(installDir: string, rel: unknown): string {\n if (typeof rel !== 'string' || rel === '.') return installDir;\n const root = resolve(installDir);\n const dir = resolve(root, rel);\n return dir === root || dir.startsWith(root + sep) ? dir : root;\n}\n\n/**\n * Validate the agent's raw JSON into a report, clamping each targetId to the\n * supplied set and each path to a repo-relative dir. Exported for testing.\n * `recommended` is stripped unless requested; at most one (the first) survives.\n */\nexport function coerceAgenticReport(\n parsed: unknown,\n validTargetIds: readonly string[],\n options?: { recommend?: boolean; rerankIds?: readonly string[] },\n): AgenticDetectionReport {\n const recommend = options?.recommend === true;\n const rerankIds = options?.rerankIds ?? [];\n const obj = (parsed ?? {}) as Record<string, unknown>;\n const repoType = obj.repoType === 'monorepo' ? 'monorepo' : 'single';\n const rawProjects = Array.isArray(obj.projects) ? obj.projects : [];\n let recommendedSeen = false;\n const projects: AgenticProject[] = rawProjects.map((raw) => {\n const p = (raw ?? {}) as Record<string, unknown>;\n // Trust the model's pick; an invalid one falls back to the enumeration's\n // highest-priority member (validTargetIds IS the priority order). The\n // winner overrides a valid pick only when both sit in rerankIds — stacks\n // that co-occur in one manifest, where a misordered enumeration is the\n // common miss. Elsewhere enumerations are padded across exclusive stacks\n // (a Flutter app listing react-native) and must not beat a correct pick.\n const pick =\n typeof p.targetId === 'string' && validTargetIds.includes(p.targetId)\n ? p.targetId\n : null;\n const enumerated = Array.isArray(p.matchingTargets)\n ? validTargetIds.find((id) =>\n (p.matchingTargets as unknown[]).includes(id),\n ) ?? null\n : null;\n const targetId =\n pick === null\n ? enumerated\n : enumerated !== null &&\n rerankIds.includes(enumerated) &&\n rerankIds.includes(pick)\n ? enumerated\n : pick;\n const recommended = recommend && !recommendedSeen && p.recommended === true;\n recommendedSeen ||= recommended;\n return {\n path: coercePath(p.path),\n framework: typeof p.framework === 'string' ? p.framework : 'Unknown',\n targetId,\n hasPostHog: p.hasPostHog === true,\n ...(recommend ? { recommended } : {}),\n };\n });\n return { repoType, projects };\n}\n\nfunction detectionReportSchema(recommend: boolean) {\n return z\n .object({\n repoType: z.enum(['monorepo', 'single']),\n projects: z.array(\n z\n .object({\n path: z.string(),\n framework: z.string(),\n matchingTargets: z.array(z.string()),\n targetId: z.string().nullable(),\n hasPostHog: z.boolean(),\n evidence: z.string(),\n ...(recommend ? { recommended: z.boolean() } : {}),\n })\n .strict(),\n ),\n })\n .strict();\n}\n\n/** Read-only scan with a typed result and transcript recovery. */\nfunction detectionRunDefinition(prompt: string): AgentRunDefinition {\n return {\n integrationLabel: 'agentic-detect',\n readOnly: true,\n prompt: () => prompt,\n collectTranscript: true,\n requestRemark: false,\n detectPackageManager: detectNodePackageManagers,\n spinnerMessage: 'Scanning the repo...',\n successMessage: 'Detection complete',\n errorMessage: 'Detection failed',\n estimatedDurationMinutes: 1,\n reportFile: '',\n docsUrl: POSTHOG_DOCS_URL,\n };\n}\n\n/** What the UI saw before `runAgent`: no run lifecycle, spinner, outro, or setup logs below warn. */\nfunction reachesUi(event: AgentProgress): boolean {\n switch (event.kind) {\n case 'lifecycle':\n case 'completion':\n case 'spinner':\n return false;\n case 'log':\n return event.level === 'warn' || event.level === 'error';\n default:\n return true;\n }\n}\n\n/** Scan through `runAgent` with the bound triage model, then the SDK fallback. */\nexport async function detectProjectsWithAgent(\n session: WizardSession,\n options: AgenticDetectOptions,\n): Promise<AgenticDetectionReport> {\n if (!session.credentials) {\n throw new Error('Detection requires authenticated credentials.');\n }\n const {\n targets,\n programId,\n purpose = 'set up a PostHog integration',\n recommend = false,\n rerankIds,\n onEvent,\n } = options;\n\n // Built here: the scan runs before the program's own run tags exist.\n const wizardMetadata = {\n ...buildRunTags({\n programId,\n integration: 'agentic-detect',\n runId: analytics.runId,\n build: analytics.build,\n }),\n call_type: CallType.detection,\n };\n const wizardFlags = await analytics.getAllFlagsForWizard();\n const wizardFlagPayloads = analytics.getWizardFlagPayloads();\n const switchboard: SwitchboardCtx = {\n program: programId,\n composed: true,\n flags: wizardFlags,\n flagPayloads: wizardFlagPayloads,\n cliHarness: session.harness,\n };\n const bindings = resolveScanBindings(switchboard);\n const reportSchema = detectionReportSchema(recommend);\n const schema = zodToJsonSchema(reportSchema);\n const config: RunConfig = {\n programId,\n run: detectionRunDefinition(\n buildPrompt(session.installDir, targets, purpose, recommend),\n ),\n composed: true,\n binding: bindings[0],\n switchboard,\n skillsBaseUrl: getSkillsBaseUrl(),\n wizardFlags,\n wizardFlagPayloads,\n wizardMetadata,\n // The scan's scans count toward the program run's report.\n scanReport: 'defer',\n };\n const input: RunInput = {\n installDir: session.installDir,\n credentials: session.credentials,\n project: null,\n apiUser: null,\n // No benchmark pipeline and no AIO capture: the scan never had either.\n flags: {\n ci: session.ci,\n signup: session.signup,\n debug: session.debug,\n e2eAsk: false,\n localMcp: session.localMcp,\n captureAio: false,\n benchmark: false,\n yaraReport: session.yaraReport,\n },\n host: { projectId: session.projectId, apiKey: session.apiKey },\n };\n const reduceUi = createUiReducer(getUI());\n const forward = (event: AgentProgress): void => {\n if (event.kind === 'activity') onEvent?.(event.line);\n if (reachesUi(event)) reduceUi(event);\n };\n\n for (let attempt = 0; attempt < 2; attempt++) {\n const timeoutMs =\n attempt === 0\n ? AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS\n : AGENTIC_DETECTION_RETRY_TIMEOUT_MS;\n const result = await runAgent(\n {\n ...config,\n binding: bindings[attempt],\n run: { ...config.run, structured: { schema, timeoutMs } },\n },\n input,\n { onProgress: forward },\n );\n\n if (\n result.outcome !== RunOutcome.Success &&\n result.failure.code === ErrorCodes.AgenticDetectionTimeout\n ) {\n if (attempt === 0) {\n onEvent?.('Project scan timed out; retrying...');\n continue;\n }\n throw new AgenticDetectionTimeoutError(attempt + 1, timeoutMs);\n }\n if (result.outcome !== RunOutcome.Success) {\n if (\n result.failure.code === ErrorCodes.AgentInvalidStructuredOutput &&\n attempt === 0\n ) {\n onEvent?.('Project scan returned invalid output; retrying...');\n continue;\n }\n throw result.failure.error ?? new Error(result.failure.message);\n }\n if (result.structuredOutput !== undefined) {\n const structured = reportSchema.safeParse(result.structuredOutput);\n if (structured.success) {\n return coerceAgenticReport(\n structured.data,\n targets.map((t) => t.id),\n { recommend, rerankIds },\n );\n }\n // A typed report that misses the schema is invalid output, not a transcript.\n if (attempt === 0) {\n onEvent?.('Project scan returned invalid output; retrying...');\n }\n continue;\n }\n\n // Transcript first, final message last — its verdicts win path conflicts.\n const output = result.snapshot.transcriptTail ?? '';\n const derived = deriveReportJson(output);\n if (derived !== null) {\n return coerceAgenticReport(\n derived,\n targets.map((t) => t.id),\n { recommend, rerankIds },\n );\n }\n if (attempt === 0) onEvent?.('Retrying project scan...');\n }\n throw new Error('Agent did not return a JSON object after retry');\n}\n","/** Non-interactive project scoping — scan the repo agentically and pick which project the run integrates. */\n\nimport {\n detectProjectsWithAgent,\n AgenticDetectionTimeoutError,\n resolveProjectDir,\n type AgenticDetectionReport,\n type AgenticProject,\n type DetectEvent,\n type DetectTarget,\n} from './agentic.js';\nimport { authenticate } from '@programs/authenticate';\nimport type { CiRunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport {\n Integration,\n WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY,\n} from '@shared/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { analytics } from '@utils/analytics';\nimport { logToFile } from '@utils/debug';\n\n/** Integration framework targets for the agentic detector (id → display name). */\nconst INTEGRATION_TARGETS: DetectTarget[] = Object.entries(\n FRAMEWORK_REGISTRY,\n).map(([id, config]) => ({ id, name: config.metadata.name }));\n\nconst INTEGRATION_IDS = new Set<string>(Object.values(Integration));\n\n/** A scanned project matched to a wizard framework. Each program classifies it with its own rule. */\nexport type IntegrationCandidate = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** The wizard framework the project matches, else null. */\n integration: Integration | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** The scan's pick for the main app. Always false unless the scan set `recommend`. */\n recommended: boolean;\n};\n\n/** Match each project of an integration scan to a wizard framework. */\nexport function toIntegrationCandidates(\n report: AgenticDetectionReport,\n): IntegrationCandidate[] {\n return report.projects.map((p) => ({\n path: p.path,\n framework: p.framework,\n integration:\n p.targetId && INTEGRATION_IDS.has(p.targetId)\n ? (p.targetId as Integration)\n : null,\n hasPostHog: p.hasPostHog,\n recommended: p.recommended === true,\n }));\n}\n\n/** Run the agentic detector for the wizard's integration frameworks — the single home of targets + purpose. */\nexport async function detectIntegrationProjects(\n session: WizardSession,\n options: {\n /** Program the scan bills to. Required so no caller can go unattributed. */\n programId: string;\n recommend?: boolean;\n onEvent?: DetectEvent;\n },\n): Promise<AgenticDetectionReport> {\n // Spread first so the targets and purpose this function owns always win.\n return detectProjectsWithAgent(session, {\n ...options,\n targets: INTEGRATION_TARGETS,\n purpose: 'set up a PostHog SDK integration',\n });\n}\n\n/** Pick the recommended-if-supported project (even with PostHog — the main app wins), else the first supported PostHog-free one. */\nexport function chooseIntegrationProject(\n projects: AgenticProject[],\n): AgenticProject | undefined {\n return (\n projects.find((p) => p.recommended === true && p.targetId != null) ??\n projects.find((p) => p.targetId != null && !p.hasPostHog)\n );\n}\n\n/** Every run fires exactly one `wizard: agentic detection` event with one of these outcomes — no untracked exits. */\nexport type AgenticDetectionOutcome =\n | 'flag-off'\n | 'error'\n | 'timeout'\n | 'no-project'\n | 'recommended'\n | 'first-instrumentable';\n\nfunction captureOutcome(\n outcome: AgenticDetectionOutcome,\n properties: Record<string, unknown> = {},\n): void {\n analytics.wizardCapture('agentic detection', { outcome, ...properties });\n}\n\n/** Flag-gated non-interactive monorepo phase: scan, auto-choose the recommended project, re-point session.installDir; every failure leaves the session untouched. */\nexport async function scopeInstallDirToProject(\n session: WizardSession,\n runner: CiRunnerContext,\n): Promise<void> {\n // Idempotent early auth: the detector needs credentials and the flag must evaluate as the logged-in user.\n await authenticate(session, 'posthog-integration');\n const flags = await analytics.getAllFlagsForWizard();\n if (flags[WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY] !== 'true') {\n // A failed flag fetch surfaces as an empty map, so flag-off also covers \"flags unavailable\".\n captureOutcome('flag-off');\n return;\n }\n\n runner.log.info('Scanning the repo for projects...');\n const startedAt = Date.now();\n let report: AgenticDetectionReport;\n try {\n report = await detectIntegrationProjects(session, {\n // Literal, like the `authenticate` call above: importing the program\n // registry here would cycle.\n programId: 'posthog-integration',\n recommend: true,\n onEvent: (line) => logToFile('[agentic detect]', line),\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n if (error instanceof AgenticDetectionTimeoutError) {\n captureOutcome('timeout', { duration_ms: Date.now() - startedAt });\n runner.log.warn(\n `${error.message}; continuing with the install dir as-is.`,\n );\n return;\n }\n analytics.captureException(error, { step: 'agentic_detection' });\n captureOutcome('error', {\n duration_ms: Date.now() - startedAt,\n error_message: error.message,\n });\n runner.log.warn(\n `Project scan failed (${error.message}); continuing with the install dir as-is.`,\n );\n return;\n }\n\n const { projects } = report;\n const recommended = projects.find((p) => p.recommended === true);\n // The event carries at most this many projects; project_count is the true total.\n const MAX_PROJECTS_CAPTURED = 25;\n const scanProperties = {\n duration_ms: Date.now() - startedAt,\n repo_type: report.repoType,\n project_count: projects.length,\n supported_count: projects.filter((p) => p.targetId != null).length,\n has_recommendation: recommended !== undefined,\n recommended_path: recommended?.path ?? null,\n projects: projects.slice(0, MAX_PROJECTS_CAPTURED),\n };\n\n const project = chooseIntegrationProject(projects);\n if (!project) {\n captureOutcome('no-project', scanProperties);\n runner.log.info(\n 'The scan found no supported project; continuing with the install dir as-is.',\n );\n return;\n }\n\n session.installDir = resolveProjectDir(session.installDir, project.path);\n captureOutcome(project.recommended ? 'recommended' : 'first-instrumentable', {\n ...scanProperties,\n chosen_framework: project.targetId,\n chosen_path: project.path,\n });\n runner.log.info(`Continuing with ${project.path} (${project.framework}).`);\n}\n","/**\n * Shared health-check step used by every program that runs an agent.\n *\n * Renders the HealthCheckScreen between intro and auth, kicks off the\n * readiness probe in onInit, and gates the screen on either a clean\n * readiness result or an explicit user dismissal of the outage.\n *\n * Programs without this step that hit a blocking outage gridlock the\n * router: agent-runner calls wizardAbort, which awaits outroDismissed,\n * but the router can't advance past the still-incomplete auth step to\n * render the OutroScreen.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport {\n evaluateWizardReadiness,\n WizardReadiness,\n SIGNUP_WIZARD_READINESS_CONFIG,\n getBlockingServiceKeys,\n} from '@shared/health-checks/readiness';\nimport { logToFile } from '@utils/debug';\n\nexport function healthCheckReady(session: WizardSession): boolean {\n if (!session.readinessResult) return false;\n\n if (session.signup) {\n const hardBlocking = getBlockingServiceKeys(\n session.readinessResult.health,\n SIGNUP_WIZARD_READINESS_CONFIG,\n );\n const defaultBlocking = getBlockingServiceKeys(\n session.readinessResult.health,\n );\n if (hardBlocking.length === 0 && defaultBlocking.length === 0) return true;\n return session.outageDismissed;\n }\n\n if (session.readinessResult.decision === WizardReadiness.No) {\n return session.outageDismissed;\n }\n return true;\n}\n\nexport const HEALTH_CHECK_STEP: ProgramStep = {\n id: 'health-check',\n label: 'Health check',\n screenId: 'health-check',\n gate: healthCheckReady,\n onInit: (ctx) => {\n evaluateWizardReadiness()\n .then((readiness) => {\n logToFile(\n `[health-checks] TUI pre-flight complete: decision=${readiness.decision}`,\n );\n ctx.setReadinessResult(readiness);\n })\n .catch((err) => {\n logToFile('[health-checks] TUI pre-flight failed:', err);\n ctx.setReadinessResult({\n decision: WizardReadiness.Yes,\n health: {} as never,\n reasons: [],\n });\n });\n },\n};\n","import { relative } from 'path';\nimport { safeReadFile, walkProjectFiles } from '@utils/bounded-fs';\n\nexport const POSTHOG_SDKS = new Set([\n 'posthog-js',\n 'posthog-node',\n 'posthog-react-native',\n 'posthog-android',\n 'posthog-ios',\n]);\n\nexport const STRIPE_SDKS = new Set([\n 'stripe',\n '@stripe/stripe-js',\n '@stripe/react-stripe-js',\n]);\n\nexport interface PackageMatch {\n /** Path to the package.json relative to installDir */\n path: string;\n posthogSdks: string[];\n stripeSdks: string[];\n}\n\n// At most this many matches retained, regardless of monorepo package count.\nconst MAX_PACKAGE_MATCHES = 500;\n\n/**\n * Find all package.json files under installDir via the shared bounded walk\n * (depth cap, ignored dirs, symlink-loop protection, per-dir entry cap).\n * Returns matches with detected SDKs.\n */\nexport function findPackageJsons(\n installDir: string,\n maxDepth = 3,\n): PackageMatch[] {\n const matches: PackageMatch[] = [];\n\n walkProjectFiles(\n installDir,\n (name, fullPath) => {\n if (name !== 'package.json' || matches.length >= MAX_PACKAGE_MATCHES) {\n return;\n }\n const content = safeReadFile(fullPath);\n if (content === null) return;\n try {\n const pkg = JSON.parse(content) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const depNames = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ];\n const posthogSdks = depNames.filter((d) => POSTHOG_SDKS.has(d));\n const stripeSdks = depNames.filter((d) => STRIPE_SDKS.has(d));\n matches.push({\n path: relative(installDir, fullPath) || 'package.json',\n posthogSdks,\n stripeSdks,\n });\n } catch {\n // Skip malformed package.json\n }\n },\n maxDepth,\n );\n\n return matches;\n}\n","/**\n * Core integration detection step.\n *\n * Runs framework detection, context gathering, version checking,\n * and feature discovery. Writes results to the store via the\n * ProgramReadyContext so the IntroScreen can display them.\n *\n * This is the same work that bin.ts $0 handler does inline —\n * extracted here so the `integrate` subcommand can reuse it.\n */\n\nimport type { ProgramReadyContext } from '@programs/program-step';\nimport {\n mayReportScanResults,\n ScanConsent,\n type WizardSession,\n} from '@lib/wizard-session';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport {\n detectFramework,\n discoverFeatures,\n gatherFrameworkContext,\n checkFrameworkVersion,\n} from '@programs/detection/index';\nimport { analytics } from '@utils/analytics';\nimport { detectWarehouseSources } from '@programs/warehouse-sources/detect';\nimport {\n DETECTED_WAREHOUSE_SOURCES_KEY,\n getDetectedWarehouseSources,\n} from '@programs/warehouse-source/detect';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\nimport { stampAiSdkDetected } from '@programs/posthog-integration/ai-sdk-stamp';\n\n// Session-free, so runProgram can stamp without loading the session.\nexport {\n stampAiSdkDetected,\n type AiSdkStampEvidence,\n} from '@programs/posthog-integration/ai-sdk-stamp';\n\nexport async function detectPostHogIntegration(\n ctx: ProgramReadyContext,\n): Promise<void> {\n const session = ctx.session;\n const installDir = session.installDir;\n\n const detectedIntegration = await detectFramework(installDir);\n\n if (detectedIntegration) {\n const config = FRAMEWORK_REGISTRY[detectedIntegration];\n\n const sessionOptions = {\n installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n };\n\n // Gather framework-specific context (e.g., router type)\n const context = await gatherFrameworkContext(config, sessionOptions);\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n ctx.setFrameworkContext(key, value);\n }\n }\n\n ctx.setFrameworkConfig(detectedIntegration, config);\n // Must go through the store setter: setFrameworkConfig (above) calls\n // nanostore setKey, which replaces the session with a shallow copy —\n // a direct `session.skillId = ...` here would write to the stale\n // pre-copy object and the live session would never see it.\n ctx.setSkillId(detectedIntegration);\n\n if (!session.detectedFrameworkLabel) {\n ctx.setDetectedFramework(config.metadata.name);\n }\n\n // Version check\n const versionResult = await checkFrameworkVersion(config, sessionOptions);\n if (versionResult.supported !== true) {\n ctx.setUnsupportedVersion(versionResult.supported);\n }\n }\n\n // Feature discovery\n for (const feature of discoverFeatures(installDir)) {\n ctx.addDiscoveredFeature(feature);\n }\n\n detectWarehouseSourcesForSuggestion(ctx, installDir);\n detectExistingPostHog(ctx, installDir);\n\n ctx.setDetectionComplete();\n}\n\n/**\n * How the scan went, for the reporter to read later. Three states, and the\n * absent one carries the most weight:\n *\n * absent this program never scanned. Only `posthog-integration` runs the\n * scan below, but every program's intro screen resolves consent\n * through the same store method, so the reporter is reached on runs\n * where no scan ever happened.\n * 'failed' the scan threw. A zero-count row would read as \"scanned, found\n * nothing\", which is a different fact.\n * 'ok' the scan ran. Zero sources is a real result and gets reported.\n */\nconst WAREHOUSE_SCAN_STATE_KEY = 'warehouseScanState';\ntype WarehouseScanState = 'ok' | 'failed';\n\n/**\n * Scan for data warehouse source signals (Postgres, Stripe, Hubspot, …) and,\n * when found, stash them for a `wizard warehouse` suggestion on the outro.\n * Runs unconditionally during `detect`, before the intro screen has asked\n * about sharing. Reporting what this finds is a separate, later concern.\n * See `reportWarehouseSourcesDetected` below.\n *\n * Deliberately a suggestion, not an inline agent run: a second credential-\n * collecting agent run before the outro could `process.exit()` on any of its\n * failure paths and cost the user the success outro on a run where PostHog\n * installed fine.\n *\n * Calls `detectWarehouseSources` (a pure scan) rather than the warehouse\n * program's `detectWarehousePrerequisites`, which writes a `detectError` when\n * it finds nothing — that's right for `wizard warehouse` but here would show a\n * spurious error and trip the CI prerequisites abort. No sources is a no-op.\n *\n * Best-effort: never let a scan failure break the wizard flow.\n */\nfunction detectWarehouseSourcesForSuggestion(\n ctx: ProgramReadyContext,\n installDir: string,\n): void {\n try {\n const sources = detectWarehouseSources(installDir);\n // Before the empty-list return: a scan that found nothing still ran, and\n // those rows are the denominator.\n ctx.setFrameworkContext(WAREHOUSE_SCAN_STATE_KEY, 'ok');\n if (sources.length === 0) return;\n ctx.setFrameworkContext(DETECTED_WAREHOUSE_SOURCES_KEY, sources);\n } catch (error) {\n ctx.setFrameworkContext(WAREHOUSE_SCAN_STATE_KEY, 'failed');\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectWarehouseSourcesForSuggestion' },\n );\n }\n}\n\n/**\n * Fires the org stamp once per session, right after `authenticate()` succeeds\n * — never from the consent path, since consent on the intro screen resolves\n * before login and `session.apiUser` is unset there. Called right after\n * `authenticate()` from run-wizard.ts's auth step and from bootstrap.ts,\n * whichever completes it first for a given program; a no-op on every call\n * after that. In the `--ci` path, project-scope.ts authenticates first as a\n * prerequisite (no evidence gathered yet, so nothing would stamp there\n * anyway) and this only ever runs from the later, idempotent bootstrap.ts\n * call — still correctly finding no evidence, since CI skips the detect step.\n */\nexport function maybeStampAiSdkDetected(session: WizardSession): void {\n // Direct mutation, not a store setter: unlike `warehouseSourcesReported`\n // (latched only from TUI-only consent screens), this runs from\n // `authenticate()`, which also fires in `--ci` mode, where the session is a\n // plain object with no nanostore — see run-non-interactive.ts. A setter\n // routed through WizardStore would silently never latch there.\n //\n // Depends on consent resolving before auth, same as every program's step\n // list orders 'intro' before 'auth' today; a program that reversed that\n // would latch this before consent exists and never stamp even once granted.\n if (session.aiSdkStampReported) return;\n session.aiSdkStampReported = true;\n stampAiSdkDetected({\n apiUser: session.apiUser,\n discoveredFeatures: session.discoveredFeatures,\n warehouseSources: getDetectedWarehouseSources(session),\n mayReportScanResults: mayReportScanResults(session),\n });\n}\n\n/**\n * The single place scan results become telemetry. Called from\n * `WizardStore.completeSetup()`, the point consent becomes final — the privacy\n * panel's choice is reversible until then, so nothing may report earlier.\n * The org stamp is a separate concern: see `maybeStampAiSdkDetected`, which\n * runs post-auth rather than at consent resolution.\n *\n * Returns true when consent has resolved and the caller should latch\n * `warehouseSourcesReported`, which is not the same as \"this sent something\":\n * a decline, a failed scan, and a program that never scanned all resolve\n * without sending.\n */\nexport function reportWarehouseSourcesDetected(\n session: WizardSession,\n): boolean {\n if (session.warehouseSourcesReported) return false;\n // 'undecided' means come back later, not no.\n if (session.scanConsent === ScanConsent.Undecided) return false;\n\n if (mayReportScanResults(session)) {\n const sources = getDetectedWarehouseSources(session);\n\n // Only an 'ok' scan has something to say. Absent means this program never\n // scanned, and reporting a zero count there would invent a scan that never\n // ran; 'failed' already fired captureException.\n const scanState = session.frameworkContext[WAREHOUSE_SCAN_STATE_KEY] as\n | WarehouseScanState\n | undefined;\n if (scanState !== 'ok') return true;\n\n // Captured on every run, including the empty case. The denominator is\n // the whole point: without the zero rows, \"20% of runs have a Postgres\"\n // is unanswerable.\n analytics.wizardCapture('warehouse sources detected', {\n warehouse_source_count: sources.length,\n warehouse_source_kinds: sources.map((s) => s.kind),\n warehouse_source_modes: sources.map((s) => s.mode),\n });\n\n if (sources.length > 0) {\n // Tag subsequent events too, so any downstream funnel can slice on what the\n // project had available without re-joining to the event above.\n analytics.setTag(\n 'warehouse_source_kinds',\n sources.map((s) => s.kind).join(','),\n );\n analytics.setTag('warehouse_source_count', sources.length);\n }\n }\n // Nothing on 'declined': saved insights read every row of this event as a\n // scan that ran. Decline rate comes from 'intro menu selected' instead.\n\n return true;\n}\n\n/** Dependency-level signal, not a verified install. A failed scan reports false. */\nexport function detectExistingPostHog(\n ctx: Pick<ProgramReadyContext, 'setPosthogSdkDetected'>,\n installDir: string,\n): void {\n try {\n const pkgJsons = findPackageJsons(installDir);\n ctx.setPosthogSdkDetected(pkgJsons.some((p) => p.posthogSdks.length > 0));\n } catch (error) {\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectExistingPosthog' },\n );\n ctx.setPosthogSdkDetected(false);\n }\n}\n","/**\n * PostHog integration program — the default wizard flow.\n *\n * Steps define their own gate predicates and onInit callbacks.\n * The store derives gate promises and fires init work from these\n * definitions — no hardcoded per-flow logic in the store.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { detectPostHogIntegration } from './detect.js';\n\nfunction needsSetup(session: WizardSession): boolean {\n const config = session.frameworkConfig;\n if (!config?.metadata.setup?.questions) return false;\n\n return config.metadata.setup.questions.some(\n (q: { key: string }) => !(q.key in session.frameworkContext),\n );\n}\n\nexport const POSTHOG_INTEGRATION_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting framework',\n // Headless step: no screen. onReady fires after bin.ts assigns the\n // session — runs framework detection, context gathering, version\n // check, and feature discovery. Results are written to the store\n // for the IntroScreen to render.\n onReady: (ctx) => detectPostHogIntegration(ctx),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'setup',\n label: 'Setup',\n screenId: 'setup',\n show: needsSetup,\n isComplete: (session) => !needsSetup(session),\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Integration',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'mcp',\n label: 'MCP servers',\n screenId: 'mcp',\n isComplete: (session) => session.mcpComplete,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n // Always shown — the user declines via Skip/esc, never bypassed.\n isComplete: (session) => session.slackStepDismissed,\n },\n {\n id: 'keep-skills',\n label: 'Keep Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Shared style constants for TUI layout primitives.\n */\n\nexport enum HAlign {\n Left = 'flex-start',\n Center = 'center',\n Right = 'flex-end',\n}\n\nexport enum VAlign {\n Top = 'flex-start',\n Center = 'center',\n Bottom = 'flex-end',\n}\n\nexport const Colors = {\n primary: 'cyan',\n accent: '#DC9300',\n titleColor: '#3D2800',\n success: 'green',\n error: 'red',\n muted: 'gray',\n} as const;\n\nexport const Icons = {\n diamond: '\\u25C6',\n diamondOpen: '\\u25C7',\n check: '\\u2714',\n warning: '\\u26A0',\n squareFilled: '\\u25FC',\n squareOpen: '\\u25FB',\n triangleRight: '\\u25B6',\n triangleSmallRight: '\\u25B8',\n bullet: '\\u2022',\n} as const;\n","/**\n * Text-splitting helpers for sentence boundary detection.\n * Used by TextBlock for animation pauses at punctuation.\n */\n\n/** Split text into sentences (keeps the delimiter attached) */\nexport function splitSentences(text: string): string[] {\n const parts: string[] = [];\n const re = /[^.!?]*[.!?]+\\s*/g;\n let match: RegExpExecArray | null;\n let lastIndex = 0;\n while ((match = re.exec(text)) !== null) {\n parts.push(match[0]);\n lastIndex = re.lastIndex;\n }\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n return parts;\n}\n\n/** Build a set of character indices where sentences end (for typewriter pause) */\nexport function sentenceEndChars(text: string): Set<number> {\n const ends = new Set<number>();\n const sentences = splitSentences(text);\n let pos = 0;\n for (const s of sentences) {\n pos += s.length;\n ends.add(pos - 1);\n }\n return ends;\n}\n\n/** Build a set of word indices where sentences end (for word-by-word pause) */\nexport function sentenceEndWords(text: string): Set<number> {\n const ends = new Set<number>();\n const sentences = splitSentences(text);\n let wordCount = 0;\n for (const s of sentences) {\n const words = s.trim().split(/\\s+/).filter(Boolean);\n wordCount += words.length;\n ends.add(wordCount - 1);\n }\n return ends;\n}\n","/**\n * Content block types and type guards.\n *\n * Extracted from ContentSequencer so that pure-logic modules (like\n * layout-helpers) can import them without pulling in Ink/React.\n */\n\nimport type { ReactNode } from 'react';\nimport type { TextRevealMode } from './TextBlock.js';\n\n/** Object form — string or ReactNode content with per-block overrides. */\nexport interface ContentObjectBlock {\n content: string | ReactNode;\n mode?: TextRevealMode;\n animationInterval?: number;\n sentenceInterval?: number;\n pause?: number;\n persist?: boolean;\n /**\n * When the sequencer advances past this block, should it render dim?\n * Defaults to `true` — the standard \"completed step\" treatment. Set to\n * `false` to keep the block at full opacity even after it's complete\n * (useful for headings or greetings that should stay readable while\n * later blocks animate in).\n *\n * Only meaningful for blocks whose `content` is a string (TextBlock\n * renders the dim treatment). NodeBlocks render whatever JSX you pass\n * verbatim regardless of completion state.\n */\n dimWhenComplete?: boolean;\n}\n\n/** Lines block — reveals ReactNode lines one at a time. */\nexport interface ContentLinesBlock {\n type: 'lines';\n lines: ReactNode[];\n interval?: number;\n pause?: number;\n}\n\n/** Clear block — page break that hides all prior blocks. */\nexport interface ContentClearBlock {\n type: 'clear';\n pause?: number;\n}\n\n/** A content block in the sequence. Bare strings are sugar for { content: '...' }. */\nexport type ContentBlock =\n | string\n | ContentObjectBlock\n | ContentLinesBlock\n | ContentClearBlock;\n\n/** Type guard for lines blocks. */\nexport function isLinesBlock(block: ContentBlock): block is ContentLinesBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'lines';\n}\n\n/** Type guard for clear blocks. */\nexport function isClearBlock(block: ContentBlock): block is ContentClearBlock {\n return typeof block !== 'string' && 'type' in block && block.type === 'clear';\n}\n\n/** Type guard for object blocks (text or node content). */\nexport function isObjectBlock(\n block: ContentBlock,\n): block is ContentObjectBlock {\n return typeof block !== 'string' && !('type' in block);\n}\n","/**\n * Layout helpers — pure functions for height estimation and viewport eviction.\n *\n * These are the core of the responsive content system. They estimate how many\n * terminal rows a content block will occupy and determine which blocks fit\n * within a given height budget.\n */\n\nimport type { ContentBlock } from './content-types.js';\nimport { isLinesBlock, isClearBlock, isObjectBlock } from './content-types.js';\n\n/**\n * Estimate the number of terminal rows a content block will occupy,\n * including 1 row of marginBottom.\n */\nexport function estimateBlockHeight(\n block: ContentBlock,\n availableWidth: number,\n): number {\n if (typeof block === 'string') {\n return wordWrap(block, availableWidth).length + 1; // +1 for marginBottom\n }\n\n if (isClearBlock(block)) return 0;\n\n if (isLinesBlock(block)) {\n return block.lines.length + 1;\n }\n\n if (isObjectBlock(block)) {\n if (typeof block.content === 'string') {\n return wordWrap(block.content, availableWidth).length + 1;\n }\n return 4; // conservative fixed estimate for ReactNode\n }\n\n return 1;\n}\n\n/**\n * Given all blocks, the active index, available width, and a row budget,\n * return [startIdx, endIdx] — the range of blocks to render.\n *\n * Always includes activeIdx. Walks backward to include as many completed\n * blocks as fit within maxHeight.\n */\nexport function computeVisibleRange(\n blocks: ContentBlock[],\n activeIdx: number,\n availableWidth: number,\n maxHeight: number,\n): [number, number] {\n // Reserve a 2-row buffer so resize-induced estimate drift doesn't\n // cause overflow=\"hidden\" to clip the margin between blocks.\n const budget = Math.max(4, maxHeight - 2);\n\n let totalHeight = estimateBlockHeight(blocks[activeIdx], availableWidth);\n let start = activeIdx;\n\n for (let i = activeIdx - 1; i >= 0; i--) {\n // Clear blocks act as a hard boundary — don't show anything before them\n if (isClearBlock(blocks[i])) break;\n const h = estimateBlockHeight(blocks[i], availableWidth);\n if (totalHeight + h > budget) break;\n totalHeight += h;\n start = i;\n }\n\n return [start, activeIdx];\n}\n\n/**\n * Word-wrap text at clean word boundaries. Always returns pre-wrapped text\n * joined with \\n — this avoids Ink's native wrap which can leave leading\n * spaces on continuation lines.\n *\n * Uses a 1-char safety margin so slight width estimate mismatches don't\n * cause Ink to re-wrap our already-wrapped lines.\n */\nexport function wordWrap(text: string, availableWidth: number): string[] {\n const safeWidth = Math.max(10, availableWidth - 1);\n const words = text.split(/\\s+/);\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n if (\n currentLine.length + word.length + 1 > safeWidth &&\n currentLine.length > 0\n ) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine += (currentLine.length > 0 ? ' ' : '') + word;\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n\n return lines;\n}\n\n/**\n * Word-wrap text and return only the last `maxRows` lines.\n * Used for intra-block truncation when a single text block exceeds the viewport.\n * Also used for normal rendering to avoid Ink's leading-space wrap artifacts.\n */\nexport function wrapAndTruncate(\n text: string,\n availableWidth: number,\n maxRows: number,\n): string {\n const lines = wordWrap(text, availableWidth);\n\n if (lines.length <= maxRows) {\n return lines.join('\\n');\n }\n\n return lines.slice(-maxRows).join('\\n');\n}\n","/**\n * TextBlock — Animates a single string paragraph.\n *\n * Self-contained: owns its own animIdx and timer.\n * Calls onComplete() when the animation finishes.\n *\n * Five animation modes:\n * 1. Typewriter — character-by-character reveal\n * 2. Word by word — each word appears in order\n * 3. Sentence by sentence — sentences appear one at a time\n * 4. Paragraph fade — paragraph appears at full opacity immediately\n * 5. Sentence fade — paragraph dim, sentences light up in order\n */\n\nimport { Text } from 'ink';\nimport { useState, useEffect, useRef, useMemo, type ReactNode } from 'react';\nimport { Colors } from '@ui/tui/styles';\nimport {\n splitSentences,\n sentenceEndChars,\n sentenceEndWords,\n} from './text-helpers.js';\nimport { wrapAndTruncate } from './layout-helpers.js';\n\nexport enum TextRevealMode {\n Typewriter = 0,\n WordByWord = 1,\n SentenceBySentence = 2,\n ParagraphFade = 3,\n SentenceFade = 4,\n}\n\nexport const TEXT_REVEAL_MODE_LABELS = [\n 'Typewriter',\n 'Word by word',\n 'Sentence by sentence',\n 'Paragraph fade',\n 'Sentence fade',\n];\n\nexport const TEXT_REVEAL_MODE_COUNT = 5;\n\n/** Default interval per mode (ms) */\nexport const TEXT_REVEAL_MODE_DEFAULTS: Record<TextRevealMode, number> = {\n [TextRevealMode.WordByWord]: 240,\n [TextRevealMode.Typewriter]: 32,\n [TextRevealMode.SentenceBySentence]: 1800,\n [TextRevealMode.ParagraphFade]: 4800,\n [TextRevealMode.SentenceFade]: 2400,\n};\n\ninterface TextBlockProps {\n text: string;\n active: boolean;\n completed: boolean;\n onComplete: () => void;\n mode: TextRevealMode;\n bullet?: ReactNode;\n animationInterval?: number;\n sentenceInterval?: number;\n /** Max rows this block may occupy. When exceeded, top lines are truncated. */\n maxHeight?: number;\n /** Available text width in columns (for truncation wrapping). */\n availableWidth?: number;\n /**\n * When `completed === true`, render the text dim. Defaults to `true` —\n * the standard \"completed step\" treatment. Pass `false` to keep the\n * text at full opacity even after the sequencer has moved on.\n */\n dimWhenComplete?: boolean;\n}\n\nexport const TextBlock = ({\n text,\n active,\n completed,\n onComplete,\n mode,\n bullet,\n animationInterval,\n sentenceInterval = 1600,\n maxHeight,\n availableWidth,\n dimWhenComplete = true,\n}: TextBlockProps) => {\n const speed = animationInterval ?? TEXT_REVEAL_MODE_DEFAULTS[mode];\n\n const [animIdx, setAnimIdx] = useState(\n mode === TextRevealMode.SentenceFade ? 1 : 0,\n );\n\n // Reset synchronously during render to avoid a one-frame flash\n const resetRef = useRef(0);\n const prevMode = useRef(mode);\n if (prevMode.current !== mode) {\n prevMode.current = mode;\n resetRef.current += 1;\n setAnimIdx(mode === TextRevealMode.SentenceFade ? 1 : 0);\n }\n\n const words = text.split(/\\s+/);\n const sentences = splitSentences(text);\n\n const sentenceCharEnds = useMemo(() => sentenceEndChars(text), [text]);\n const sentenceWordEnds = useMemo(() => sentenceEndWords(text), [text]);\n\n const isDone =\n mode === TextRevealMode.Typewriter\n ? animIdx >= text.length\n : mode === TextRevealMode.ParagraphFade\n ? true\n : mode === TextRevealMode.WordByWord\n ? animIdx >= words.length\n : mode === TextRevealMode.SentenceFade ||\n mode === TextRevealMode.SentenceBySentence\n ? animIdx >= sentences.length\n : true;\n\n // Fire onComplete when done\n useEffect(() => {\n if (isDone && active) onComplete();\n }, [isDone, active, onComplete]);\n\n // Animate: single effect for all tick-based modes\n useEffect(() => {\n if (!active || mode === TextRevealMode.ParagraphFade || isDone) return;\n const token = resetRef.current;\n\n const isFirstTick = animIdx === 0;\n\n let delay = isFirstTick ? 0 : speed;\n if (\n !isFirstTick &&\n mode === TextRevealMode.Typewriter &&\n animIdx > 0 &&\n sentenceCharEnds.has(animIdx - 1)\n ) {\n delay = sentenceInterval;\n } else if (\n !isFirstTick &&\n mode === TextRevealMode.WordByWord &&\n animIdx > 0 &&\n sentenceWordEnds.has(animIdx - 1)\n ) {\n delay = sentenceInterval;\n }\n\n const timer = setTimeout(() => {\n if (token !== resetRef.current) return;\n setAnimIdx((c) => c + 1);\n }, delay);\n return () => clearTimeout(timer);\n }, [\n active,\n mode,\n animIdx,\n isDone,\n speed,\n sentenceInterval,\n sentenceCharEnds,\n sentenceWordEnds,\n ]);\n\n // Pre-wrap text ourselves to avoid Ink's native wrap leaving leading spaces\n // on continuation lines. When maxHeight is set, also truncates to last N rows.\n const wrap = (visibleText: string): string => {\n if (availableWidth == null) return visibleText;\n if (maxHeight == null) {\n return wrapAndTruncate(visibleText, availableWidth, Infinity);\n }\n return wrapAndTruncate(visibleText, availableWidth, maxHeight);\n };\n\n // Completed: dimmed by default, but can be overridden per-block.\n if (completed) {\n return (\n <Text dimColor={dimWhenComplete}>\n {bullet}\n {wrap(text)}\n </Text>\n );\n }\n\n // Active: mode-specific rendering\n if (mode === TextRevealMode.Typewriter) {\n const revealed = text.slice(0, animIdx);\n const atSentenceEnd = /[.!?]\\s*$/.test(revealed);\n const display = atSentenceEnd ? revealed.trimEnd() : revealed;\n return (\n <Text>\n {bullet}\n {wrap(display)}\n <Text color={Colors.muted}>{'\\u258C'}</Text>\n </Text>\n );\n }\n\n if (mode === TextRevealMode.WordByWord) {\n const visible = words.slice(0, animIdx).join(' ');\n return (\n <Text>\n {bullet}\n {wrap(visible)}\n </Text>\n );\n }\n\n if (mode === TextRevealMode.ParagraphFade) {\n return (\n <Text>\n {bullet}\n {wrap(text)}\n </Text>\n );\n }\n\n if (mode === TextRevealMode.SentenceBySentence) {\n const visible = sentences.slice(0, animIdx).join('');\n return (\n <Text>\n {bullet}\n {wrap(visible)}\n </Text>\n );\n }\n\n // SentenceFade\n return (\n <Text>\n {bullet}\n {sentences.map((s, si) => (\n <Text key={si} dimColor={si >= animIdx}>\n {s}\n </Text>\n ))}\n </Text>\n );\n};\n","/**\n * StatusPeekTrigger — Fires the status-bar expansion once, renders a hint.\n *\n * Module-level `peekedOnce` guards against re-mounts (resize, tab switch)\n * so the peek only happens a single time per process.\n */\n\nimport { Text } from 'ink';\nimport { useEffect } from 'react';\nimport type { WizardStore } from '@ui/tui/store';\n\nlet peekedOnce = false;\n\ninterface StatusPeekTriggerProps {\n store?: WizardStore;\n /** How long the status bar stays expanded, in ms. */\n duration?: number;\n}\n\nexport const StatusPeekTrigger = ({\n store,\n duration = 10000,\n}: StatusPeekTriggerProps) => {\n useEffect(() => {\n if (peekedOnce) return;\n peekedOnce = true;\n store?.setStatusExpanded(true);\n // No cleanup — the store call is safe after unmount and the component\n // may be evicted before the timer fires (non-persist NodeBlock).\n setTimeout(() => {\n store?.setStatusExpanded(false);\n }, duration);\n }, [store, duration]);\n\n return <Text>You can view the Wizard&apos;s status below.</Text>;\n};\n","/**\n * POSTHOG_DATA_FLOW — ASCII diagram of the PostHog event ingestion flow.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const POSTHOG_DATA_FLOW: ContentBlock = {\n type: 'lines',\n interval: 500,\n pause: 8000,\n // Box is 30 chars wide between │ borders.\n // Labels: 1-char indent. Arrows: \" ↓ \" (5). Sub-items: \" │ \" (7).\n lines: [\n <Text color=\"gray\">{' ┌──────────────────────────────┐'}</Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color=\"cyan\">\n Your App\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text>posthog.capture()</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>custom events</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>custom properties</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ │ '}</Text>\n <Text dimColor>person profiles</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text dimColor>groups</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color={Colors.accent}>\n PostHog SDK\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text>HTTP</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color={Colors.accent}>\n PostHog Cloud\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ ↓ '}</Text>\n <Text>query + visualize</Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │ '}</Text>\n <Text bold color=\"green\">\n Dashboards & Insights\n </Text>\n <Text color=\"gray\">{' │'}</Text>\n </Text>,\n <Text color=\"gray\">{' └──────────────────────────────┘'}</Text>,\n ],\n};\n","/**\n * PRODUCT_SUITE_BLOCK — Two-column listing of PostHog products.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PRODUCT_SUITE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 1000,\n pause: 15000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Error Tracking'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Web Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Session Replay'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Feature Flags '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Data Pipelines'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Experiments '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Data Warehouse'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'AI Observability '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Surveys'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Workflows '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Logs'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Tours '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Support'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Revenue Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Endpoints'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Customer Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'PostHog Desktop'}\n </Text>,\n ],\n};\n","/**\n * LINE_CHART_BLOCK — ASCII trends chart illustrating an insight.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const LINE_CHART_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 300,\n pause: 6000,\n lines: [\n <Text bold>{' Trends · user signups (monthly)'}</Text>,\n <Text> </Text>,\n // 10k\n <Text>\n <Text color=\"gray\">{' 10k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──'}</Text>\n <Text dimColor>{' 9,575'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭╯'}</Text>\n </Text>,\n // 7.5k\n <Text>\n <Text color=\"gray\">{' 7.5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭─╯'}</Text>\n </Text>,\n // 5k\n <Text>\n <Text color=\"gray\">{' 5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭─╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──╯'}</Text>\n </Text>,\n // 2.5k\n <Text>\n <Text color=\"gray\">{' 2.5k ┤'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭───╯'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' │'}</Text>\n {' '}\n <Text color=\"cyan\">{'╭──────╯'}</Text>\n </Text>,\n // 0\n <Text>\n <Text color=\"gray\">{' 0 ┤'}</Text>\n <Text color=\"cyan\">{'──────╯'}</Text>\n </Text>,\n // X-axis\n <Text color=\"gray\">{' └┬─────┬─────┬─────┬─────┬──'}</Text>,\n <Text dimColor>{' May Aug Nov Feb May'}</Text>,\n ],\n};\n","/**\n * FUNNEL_BLOCK — ASCII funnel illustrating step conversion.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const FUNNEL_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 200,\n pause: 8000,\n lines: [\n <Text bold>{' Funnel · ride conversion'}</Text>,\n <Text> </Text>,\n // Step 1\n <Text>\n {' '}\n <Text bold>1</Text>\n {' app_launched'}\n {' '}\n <Text bold color=\"green\">\n 100.00%\n </Text>\n </Text>,\n <Text color=\"cyan\">{' ██████████████████████████████'}</Text>,\n <Text dimColor>{' → 1,200 users'}</Text>,\n <Text> </Text>,\n // Step 2\n <Text>\n {' '}\n <Text bold>2</Text>\n {' ride_requested'}\n {' '}\n <Text dimColor>{'avg 2m 30s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 72.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'██████████████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 864 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 336 (28%)'}</Text>\n </Text>,\n <Text> </Text>,\n // Step 3\n <Text>\n {' '}\n <Text bold>3</Text>\n {' ride_accepted'}\n {' '}\n <Text dimColor>{'avg 5m 12s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 51.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'██████████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 612 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 252 (29%)'}</Text>\n </Text>,\n <Text> </Text>,\n // Step 4\n <Text>\n {' '}\n <Text bold>4</Text>\n {' ride_started'}\n {' '}\n <Text dimColor>{'avg 1m 45s'}</Text>\n {' '}\n <Text bold color=\"green\">\n 38.00%\n </Text>\n </Text>,\n <Text>\n {' '}\n <Text color=\"cyan\">{'█████████████'}</Text>\n <Text dimColor>{'░░░░░░░░░░░░░░░░░░'}</Text>\n </Text>,\n <Text>\n {' '}\n <Text dimColor>→ 456 users</Text>\n {' '}\n <Text color=\"red\">↘</Text>\n <Text dimColor>{' 156 (25%)'}</Text>\n </Text>,\n ],\n};\n","/**\n * Integration learn-deck — the narrative script played while the agent\n * runs the PostHog integration flow. Weaves typewriter lines, pauses,\n * `clear` markers, and the diagram blocks into one sequence.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { POSTHOG_DATA_FLOW } from './data-flow.js';\nimport { PRODUCT_SUITE_BLOCK } from './product-suite.js';\nimport { LINE_CHART_BLOCK } from './line-chart.js';\nimport { FUNNEL_BLOCK } from './funnel.js';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => [\n {\n // Name comes from the login profile; falls back when absent (CI keys).\n content: store?.session.apiUser?.first_name\n ? `Welcome, ${store.session.apiUser.first_name}.`\n : 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content: 'It handles the entire PostHog setup process on your behalf.',\n pause: 5000,\n },\n\n {\n content:\n \"As we speak, it's building a plan to set up PostHog in your project.\",\n pause: 6000,\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n content: 'It takes about eight minutes.',\n pause: 2000,\n },\n\n {\n content: 'So grab some coffee ☕️.',\n pause: 2000,\n },\n\n {\n content: 'Or stick around and learn about PostHog.',\n pause: 5000,\n },\n\n { type: 'clear', pause: 3000 },\n\n {\n content: 'Events are the foundation of the PostHog platform.',\n pause: 4000,\n },\n\n {\n content:\n 'Every time an action is performed in your codebase — like button clicks, function calls, or thrown errors — we can capture an event.',\n pause: 6000,\n },\n\n {\n content: 'Events are sent to PostHog and joined with other product data.',\n pause: 6000,\n },\n\n { type: 'clear', pause: 1000 },\n\n { content: \"Here's the flow.\", pause: 1000 },\n\n POSTHOG_DATA_FLOW,\n\n { type: 'clear', pause: 2000 },\n\n {\n content:\n 'With enough event data, you can answer powerful questions about your product.',\n pause: 4000,\n },\n\n { content: 'And create insights.', pause: 4000 },\n\n { type: 'clear', pause: 500 },\n\n { content: 'Like trends to measure growth.', pause: 2500 },\n\n LINE_CHART_BLOCK,\n\n { type: 'clear', pause: 500 },\n\n { content: 'Or funnels to reveal bottlenecks.', pause: 2500 },\n\n FUNNEL_BLOCK,\n\n { type: 'clear', pause: 1000 },\n\n {\n content: 'Use those signals to decide what to build next.',\n pause: 4000,\n },\n\n { content: 'PostHog has all the dev tools you need.', pause: 3000 },\n\n PRODUCT_SUITE_BLOCK,\n\n { type: 'clear', pause: 2000 },\n\n {\n content: 'The wizard is wiring up your PostHog stack with...',\n pause: 2500,\n },\n\n {\n type: 'lines',\n interval: 1000,\n pause: 12000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Product Analytics '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Web Analytics'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Error Tracking '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Session Replay'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'AI Observability '}\n <Text color=\"cyan\">{'◆ '}</Text>\n {'Logs'}\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n {'Data Warehouse sources'}\n </Text>,\n ],\n },\n];\n","/**\n * The one-line prompt the operator pastes into their own coding agent after a\n * run: open the setup-report notebook, work its \"Verify before merging\"\n * checklist. Surfaced via `OutroData.handoffPrompt` (printed to scrollback on\n * exit); only exists when the run captured a notebook URL, since no report\n * file is written. The wording demands investigate-then-approval, and stops\n * there — the operator's own agent governs how changes land. Background:\n * https://github.com/PostHog/wizard/issues/447\n */\nexport function buildCodingAgentPrompt(notebookUrl: string): string {\n return `Open ${notebookUrl} (the PostHog setup report) and work through its \"Verify before merging\" checklist: investigate each item, then list the changes you'd make and get my approval before applying any of them.`;\n}\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { runProgramAgent } from '@programs/run-agent-legacy';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AgentSignals, shouldDisableAsk, WIZARD_TOOL_NAMES } from '@agent';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { mayReportScanResults, OutroKind, RunPhase } from '@lib/wizard-session';\nimport {\n DEFAULT_PACKAGE_INSTALLATION,\n SPINNER_MESSAGE,\n} from '@programs/framework-config';\nimport { tryGetPackageJson, isUsingTypeScript } from '@utils/setup-utils';\nimport { analytics } from '@utils/analytics';\nimport {\n detectFramework,\n gatherFrameworkContext,\n} from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport type { CiRunnerContext, RunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\nimport {\n WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY,\n WIZARD_INTERACTION_EVENT_NAME,\n} from '@shared/constants';\nimport { requestDeepLink } from '@utils/provisioning';\nimport { openTrackedLink, withUtm } from '@utils/links';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { getDetectedWarehouseSources } from '@programs/warehouse-source/detect';\nimport { POSTHOG_INTEGRATION_PROGRAM } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/posthog-integration/index.js';\nimport { buildCodingAgentPrompt } from './handoff.js';\nimport { EVENT_PLAN_FILE } from './constants.js';\n\nconst DASHBOARD_DEEP_LINK_KEY = 'dashboardDeepLink';\n\nconst WAREHOUSE_SOURCES_DOCS_URL =\n 'https://posthog.com/docs/data-warehouse/sources';\n\n/** Task type of the seeded step below, matched against the drain's result. */\nconst WAREHOUSE_SEED_TASK_TYPE = 'warehouse';\n\nfunction resolveContinueUrl(\n sess: WizardSession,\n host: HostResolution,\n deepLink: unknown,\n): string | undefined {\n if (!sess.signup) return undefined;\n if (typeof deepLink === 'string' && deepLink) return deepLink;\n return withUtm(`${host.appHost}/products?source=wizard`, 'outro-continue');\n}\n\n/** Sources listed with their own link before the outro falls back to a summary line. */\nconst WAREHOUSE_LINK_LIMIT = 3;\n\n/**\n * How many detected sources the seeded step is allowed to collect credentials\n * for.\n *\n * The step's cost to the user is one credential prompt per source, and the\n * wizard knows the count at seed time: detection keys off dependency and\n * `.env` key names, so an ordinary app can match a dozen sources it has no\n * intention of importing. Offering all of them asks a person to agree, up\n * front, to an interrogation whose length they cannot see — and the runs\n * offered more than a handful answer none of the prompts at all, while the\n * short ones are where every connected source comes from.\n *\n * So the step takes the first few and the outro carries the rest as links,\n * which is the same trade {@link WAREHOUSE_LINK_LIMIT} already makes for a\n * list too long to read. Ordering is the registry's, which groups databases\n * ahead of the API-key SaaS that inflates the tail.\n */\nconst WAREHOUSE_SEED_LIMIT = 3;\n\n/**\n * The app's new-source page, pre-selected to one source kind.\n *\n * `kind` is matched case-insensitively against the connector list, and a kind\n * the app cannot resolve lands on the source catalog rather than erroring. So a\n * source we detect but the app has not shipped a connector for still takes the\n * user somewhere useful.\n */\nfunction warehouseSourceUrl(\n host: HostResolution,\n projectId: number | string,\n kind: string,\n): string {\n const path = `${\n host.appHost\n }/project/${projectId}/data-warehouse/new-source?kind=${encodeURIComponent(\n kind,\n )}`;\n return withUtm(path, 'outro-warehouse');\n}\n\n/**\n * Outro suggestion for data sources found in the project but not connected.\n *\n * A pointer at the app, not an inline flow. Connecting a source needs\n * interactive credential collection, and chaining that as a second agent run\n * before the outro would let any of its terminal failure paths `process.exit()`\n * — costing the user the success outro and the post-outro MCP / Slack steps on\n * a run where PostHog installed fine.\n *\n * Each source gets its own pre-filled link, because the alternative we shipped\n * first — naming the `wizard warehouse` command — asks the user to start a\n * second CLI run before they can connect anything. A link opens the form for\n * that one source. The command line stays for anyone who wants every source in\n * one pass, and it is the only route offered once the list is too long to read.\n *\n * Returns undefined when nothing was detected, so the outro is unchanged for\n * projects with no connectable source — and when the run's own warehouse step\n * connected everything it was given, where every bullet here would ask the\n * user to redo work the wizard just did and send them at a new-source form\n * that would collide with the source already created. A completed step is\n * only ever given the first {@link WAREHOUSE_SEED_LIMIT} sources, so anything\n * past that is still unconnected and still belongs here.\n */\nfunction buildWarehouseNextSteps(\n sess: WizardSession,\n host: HostResolution,\n projectId: number | string,\n completedSeededTypes: readonly string[],\n): { heading: string; items: string[] } | undefined {\n const detected = getDetectedWarehouseSources(sess);\n const sources = completedSeededTypes.includes(WAREHOUSE_SEED_TASK_TYPE)\n ? detected.slice(WAREHOUSE_SEED_LIMIT)\n : detected;\n if (sources.length === 0) return undefined;\n\n const listed = sources.slice(0, WAREHOUSE_LINK_LIMIT);\n const items = listed.map(\n (s) => `Connect ${s.label}: ${warehouseSourceUrl(host, projectId, s.kind)}`,\n );\n\n const remaining = sources.length - listed.length;\n if (remaining > 0) {\n items.push(`And ${remaining} more we found in this project.`);\n }\n items.push('Connect them all at once with: npx @posthog/wizard warehouse');\n\n return { heading: 'Query your other data in PostHog:', items };\n}\n\n/**\n * Prompt fragment asking the agent to note the detected data sources in the\n * setup report's checklist.\n *\n * Empty string when nothing was detected, so the prompt is byte-identical to\n * today for projects with no connectable source. Best-effort by nature — the\n * agent may word it differently or skip it. That is acceptable here precisely\n * because it is a note in a report: the outro `nextSteps` bullet carries the\n * same information deterministically, so nothing is lost if the agent drops it.\n */\nfunction warehouseReportInstruction(sess: WizardSession): string {\n const sources = getDetectedWarehouseSources(sess);\n if (sources.length === 0) return '';\n\n const labels = sources.map((s) => s.label).join(', ');\n return `Finally: this project also contains data sources PostHog can import (${labels}). In the setup report's \"Verify before merging\" checklist, add one item noting these were found and that \\`npx @posthog/wizard warehouse\\` will connect them to PostHog's data warehouse. Do not attempt to set them up yourself in this run.`;\n}\n\n/**\n * The orchestrator task that connects the data sources detection found in the\n * project — the one step of the flow that stops to ask the user for\n * credentials. Queued here rather than by the planner: whether it belongs in\n * the run is a fact about the project the wizard already scanned, so a model\n * can neither invent it nor forget it.\n *\n * It runs at the end of the queue, not the start. Where exactly is the agent\n * prompt's business (`dependsOn` in the warehouse agent's frontmatter, resolved\n * by `seeded-deps.ts` once the planner has run) — this file only decides whether\n * the task belongs in the run at all.\n *\n * Empty when nothing was detected, and in CI, signup, and any other run where\n * `wizard_ask` is disabled — a credential prompt nobody can answer would burn\n * the task's whole timeout and then fail the run. Capped at\n * {@link WAREHOUSE_SEED_LIMIT} sources, with the rest handed over as outro\n * links by {@link buildWarehouseNextSteps}.\n */\nconst warehouseSeedTasks: NonNullable<ProgramConfig['seedTasks']> = (sess) => {\n if (shouldDisableAsk(sess)) return [];\n const sources = getDetectedWarehouseSources(sess);\n if (sources.length === 0) return [];\n\n const offered = sources.slice(0, WAREHOUSE_SEED_LIMIT);\n const deferred = sources.length - offered.length;\n\n // The task is queued either way. A decline withholds reporting, not the\n // feature. See the matching gate in reportWarehouseSourcesDetected.\n if (mayReportScanResults(sess)) {\n analytics.wizardCapture('orchestrator warehouse task queued', {\n warehouse_source_count: sources.length,\n warehouse_source_kinds: sources.map((s) => s.kind),\n // What the step was actually given, which is what its outcome is a rate\n // of. The two counts above stay the detection totals they have always\n // been, so the detection trend reads across this change.\n warehouse_offered_count: offered.length,\n });\n }\n return [\n {\n type: WAREHOUSE_SEED_TASK_TYPE,\n inputs: {\n sources: offered.map((s) => ({\n kind: s.kind,\n label: s.label,\n mode: s.mode,\n matchedSignal: s.matchedSignal,\n })),\n },\n notice: {\n title: 'Connect your data sources',\n // Two moments, and the copy has to name both. The answer is given here,\n // at the start of the run. The credential questions arrive at the end of\n // it, minutes later. So this must not read as \"expect a prompt any\n // moment now\", and equally must not read as \"walk away for the run\".\n body: [\n 'We detected some warehouse sources we can connect to enrich your PostHog data. Answer now, and we connect them at the end of the run, after your code changes. We will ask you for the credentials at that point, and beep when we do.',\n ...(deferred > 0\n ? [\n `We found ${deferred} more we can connect. Those are listed with a link each at the end, so this step stays short.`,\n ]\n : []),\n \"You can select [Skip] if you'd like to do this later in PostHog.\",\n ],\n items: offered.map((s) => s.label),\n docsLabel: 'Learn more about warehouse sources',\n docsUrl: WAREHOUSE_SOURCES_DOCS_URL,\n prompt: 'Connect these during setup?',\n confirmLabel: 'Continue [Enter]',\n cancelLabel: 'Skip [Esc]',\n },\n },\n ];\n};\n\nexport const SETUP_REPORT_FILE = 'posthog-setup-report.md';\nexport { EVENT_PLAN_FILE } from './constants.js';\n\nexport const posthogIntegrationConfig: ProgramConfig = {\n description: 'Set up PostHog SDK integration',\n id: 'posthog-integration',\n agentFlow: 'integration-v2',\n eventPlanFile: EVENT_PLAN_FILE,\n steps: POSTHOG_INTEGRATION_PROGRAM,\n getContentBlocks,\n // Basic integration runs without structured user input; drop wizard_ask\n // so the model can't pop modal prompts mid-run. The runner forwards this\n // list to the general-purpose subagent as well, so dispatched subagents\n // can't reach around the parent and ask either.\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n\n seedTasks: warehouseSeedTasks,\n\n // Kill switch over the shipped default: only an explicit 'false' excludes,\n // so a failed flag fetch keeps AI Observability and Logs in the run.\n excludedTaskTypes: (flags) =>\n flags[WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY] === 'false'\n ? ['ai-observability', 'logs']\n : [],\n\n // CI-mode prerequisite work: the headless equivalent of the detect step's\n // onReady hook. Auto-detect the framework, then gather context.\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n\n const frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.frameworkConfig = frameworkConfig;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: true,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n },\n\n run: async (\n session: WizardSession,\n runner: RunnerContext,\n ): Promise<ProgramRun> => {\n const config = session.frameworkConfig!;\n\n const typeScriptDetected = isUsingTypeScript({\n installDir: session.installDir,\n });\n session.typescript = typeScriptDetected;\n analytics.setTag('typescript', typeScriptDetected);\n\n // Read package.json and resolve framework version\n const usesPackageJson = config.detection.usesPackageJson !== false;\n let frameworkVersion: string | undefined;\n\n if (usesPackageJson) {\n const packageJson = await tryGetPackageJson({\n installDir: session.installDir,\n });\n if (packageJson) {\n const { hasDeclaredDependency } = await import('@utils/package-json');\n if (!hasDeclaredDependency(config.detection.packageName, packageJson)) {\n runner.log.warn(\n `${config.detection.packageDisplayName} does not seem to be installed. Continuing anyway — the agent will handle it.`,\n );\n }\n frameworkVersion = config.detection.getVersion(packageJson);\n } else {\n runner.log.warn(\n 'Could not find package.json. Continuing anyway — the agent will handle it.',\n );\n }\n } else {\n frameworkVersion = config.detection.getVersion(null);\n }\n\n // Analytics tags\n if (frameworkVersion && config.detection.getVersionBucket) {\n const versionBucket = config.detection.getVersionBucket(frameworkVersion);\n analytics.setTag(`${config.metadata.integration}-version`, versionBucket);\n }\n // The same kill switch the orchestrator applies via excludedTaskTypes,\n // gated here for linear/composed runs, which assemble their own prompt —\n // without this, disabling the flag would not reach this path and the\n // prompt would still instruct installing the AIO and Logs skills. Only an\n // explicit 'false' excludes, so a failed flag fetch keeps the default.\n const wizardFlags = await analytics.getAllFlagsForWizard();\n const skillCategoryInstruction =\n wizardFlags[WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY] !== 'false'\n ? `Choose a skill from the \\`integration\\` category that matches this project's framework. Start with this framework skill; load the AI Observability and Logs skills when its workflow calls for them, and follow each installed skill's own steps to completion — framework first, then AI Observability, then Logs — before verification and the setup report. Both are included by default where applicable; the skills define applicability and how to report skipped work. These three categories — \\`integration\\`, \\`ai-observability\\`, \\`logs\\` — are the only ones this run uses. Do NOT load or install skills from any other category (llm-analytics, error-tracking, feature-flags, audit, etc.) — those are handled separately. In particular, \\`ai-observability\\` is the category for AI Observability; do not substitute \\`llm-analytics\\`.`\n : `Choose a skill from the \\`integration\\` category that matches this project's framework. The \\`integration\\` category is the ONLY one this run uses. Do NOT load or install skills from any other category (ai-observability, logs, llm-analytics, error-tracking, feature-flags, audit, etc.) — those are handled separately. If the installed skill's workflow contains an \"AI Observability and Logs\" section, skip that entire section: this run excludes both products.`;\n\n const frameworkContext = session.frameworkContext;\n const contextTags = config.analytics.getTags(frameworkContext);\n Object.entries(contextTags).forEach(([key, value]) => {\n analytics.setTag(key, value);\n });\n\n return {\n integrationLabel: config.metadata.integration,\n additionalMcpServers: config.metadata.additionalMcpServers,\n detectPackageManager: config.detection.detectPackageManager,\n spinnerMessage: SPINNER_MESSAGE,\n successMessage: config.ui.successMessage,\n estimatedDurationMinutes: config.ui.estimatedDurationMinutes,\n reportFile: SETUP_REPORT_FILE,\n docsUrl: config.metadata.docsUrl,\n errorMessage: 'Integration failed',\n // The seeded warehouse task's fallback, when a user cannot hand over a\n // credential, is to give them the pre-filled new-source URL. That only\n // works if the overlay renders it as a link they can open or copy.\n richLinks: true,\n\n customPrompt: (ctx) => {\n const additionalLines = config.prompts.getAdditionalContextLines\n ? config.prompts.getAdditionalContextLines(frameworkContext)\n : [];\n const additionalContext =\n additionalLines.length > 0\n ? '\\n' + additionalLines.map((line) => `- ${line}`).join('\\n')\n : '';\n\n return `You have access to the PostHog MCP server which provides skills to integrate PostHog into this ${\n config.metadata.name\n } project.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- Framework: ${config.metadata.name} ${frameworkVersion || 'latest'}\n- TypeScript: ${typeScriptDetected ? 'Yes' : 'No'}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n- Project type: ${config.prompts.projectTypeDetection}\n- Package installation: ${\n config.prompts.packageInstallation ?? DEFAULT_PACKAGE_INSTALLATION\n }${additionalContext}\n\nInstructions (follow these steps IN ORDER - do not skip or reorder):\n\nSTEP 1: Call load_skill_menu (from the wizard-tools MCP server) with category: \"integration\" to see available framework skills.\n If the tool fails, emit: ${\n AgentSignals.ERROR_MCP_MISSING\n } Could not load skill menu and halt.\n\n ${skillCategoryInstruction}\n If no suitable integration skill is found, emit: ${\n AgentSignals.ERROR_RESOURCE_MISSING\n } Could not find a suitable skill for this project.\n\nSTEP 2: Call install_skill (from the wizard-tools MCP server) with the chosen skill ID (e.g., \"integration-nextjs-app-router\").\n Do NOT run any shell commands to install skills.\n If install_skill fails, emit on its own line: ${\n AgentSignals.SKILL_INSTALL_FAILED\n } <skill id — one-line reason>. Then CONTINUE and SKIP to STEP 5 the integration without the skill, following these steps and your knowledge of ${\n config.metadata.name\n } and PostHog's official docs, and note in the setup report that the skill could not be installed.\n\nSTEP 3: Load the installed skill's SKILL.md file to understand what references are available.\n\nSTEP 4: Follow the skill's program files in sequence. Look for numbered program files in the references (e.g., files with patterns like \"1-\", \"2-\", \"3-\"). Start with the first one and proceed through each step until completion. Each program file will tell you what to do and which file comes next. Never directly write PostHog tokens directly to code files; always use environment variables.\n\nSTEP 5: Set up environment variables for PostHog using the wizard-tools MCP server (this runs locally — secret values never leave the machine):\n - Use check_env_keys to see which keys the project already sets, and where. Omit filePath and it scans every .env file in the project, so you don't have to guess between .env, .env.local and a nested one. It answers { status, foundIn } per key: \"present\" means a real env file sets the key, while a key found only in a committed template (.env.example and friends) reads as \"missing\" — a template documents a key rather than setting it, and is never a file to write credentials into.\n - Use set_env_values to create or update the PostHog public token and host, using the appropriate environment variable naming convention for ${\n config.metadata.name\n }, which you'll find in example code. The tool will also ensure .gitignore coverage. Don't assume the presence of keys means the value is up to date. Write the correct value each time.\n - Reference these environment variables in the code files you create instead of hardcoding the public token and host.\n\nImportant: Use the detect_package_manager tool (from the wizard-tools MCP server) to determine which package manager the project uses, then run its install command to add the SDK. Do not manually search for lockfiles or config files. If a file already EXISTS, read it immediately before you edit or overwrite it — writing from a stale read causes a tool failure. Creating a brand-new file needs no prior read: never read a path that does not exist yet; just write it.\n\n${warehouseReportInstruction(session)}\n`;\n },\n\n postRun: async (sess, credentials) => {\n const envVars = config.environment.getEnvVars(\n credentials.projectApiKey,\n credentials.host.apiHost,\n );\n if (config.environment.uploadToHosting) {\n const { uploadEnvironmentVariablesStep } = await import(\n '@steps/index'\n );\n const uploadedEnvVars = await uploadEnvironmentVariablesStep(\n envVars,\n {\n integration: config.metadata.integration,\n session: sess,\n },\n );\n if (uploadedEnvVars.length > 0) {\n analytics.capture(WIZARD_INTERACTION_EVENT_NAME, {\n action: 'wizard_env_vars_uploaded',\n integration: config.metadata.integration,\n variable_count: uploadedEnvVars.length,\n variable_keys: uploadedEnvVars,\n });\n }\n }\n\n if (sess.signup) {\n const deepLink = await requestDeepLink(\n credentials.accessToken,\n credentials.host,\n );\n if (deepLink) {\n const taggedDeepLink = withUtm(deepLink, 'dashboard-deeplink');\n sess.frameworkContext[DASHBOARD_DEEP_LINK_KEY] = taggedDeepLink;\n openTrackedLink(taggedDeepLink, 'dashboard-deeplink', {\n auto: true,\n });\n }\n }\n },\n\n buildOutroNextSteps: (sess, credentials, completedSeededTypes) =>\n buildWarehouseNextSteps(\n sess,\n credentials.host,\n credentials.projectId,\n completedSeededTypes,\n ),\n\n buildOutroData: (sess, credentials) => {\n const envVars = config.environment.getEnvVars(\n credentials.projectApiKey,\n credentials.host.apiHost,\n );\n const deepLink = sess.frameworkContext[DASHBOARD_DEEP_LINK_KEY];\n const continueUrl = resolveContinueUrl(\n sess,\n credentials.host,\n deepLink,\n );\n\n const changes = [\n ...config.ui.getOutroChanges(frameworkContext),\n Object.keys(envVars).length > 0\n ? 'Added environment variables to .env file'\n : '',\n ].filter(Boolean);\n\n return {\n kind: OutroKind.Success as const,\n message: 'Successfully installed PostHog!',\n changes,\n docsUrl: config.metadata.docsUrl,\n continueUrl,\n // The linear sequence seeds no tasks, so nothing here was connected\n // during the run. `buildOutroNextSteps` carries the orchestrated case.\n nextSteps: buildWarehouseNextSteps(\n sess,\n credentials.host,\n credentials.projectId,\n [],\n ),\n // Set once the agent mirrors the report into a notebook and emits [NOTEBOOK_URL].\n notebookUrl: sess.notebookUrl ?? undefined,\n // No report file — the prompt points at the notebook, when the run captured one.\n handoffPrompt: sess.notebookUrl\n ? buildCodingAgentPrompt(sess.notebookUrl)\n : undefined,\n };\n },\n };\n },\n};\n\nexport { POSTHOG_INTEGRATION_PROGRAM } from './steps.js';\n\n/**\n * Self-contained run step that runs the integration agent. Other programs\n * import this and splice it into their own step list to compose the\n * integration's work as one of their run steps — self-driving sets up PostHog\n * this way before its own run. The host program supplies `show`/`onRunPrep`/\n * `targetDir`; this carries the run.\n */\nexport const integrationRunStep: ProgramStep = {\n id: 'run',\n label: 'Integration',\n screenId: 'run',\n // composed: runs inside the host program (self-driving), so skip the\n // integration's terminal outro + analytics shutdown of the shared client.\n run: (session) =>\n runProgramAgent(posthogIntegrationConfig, session, { composed: true }),\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n};\n","/**\n * Revenue analytics prerequisite detection.\n *\n * Scans the project for PostHog + Stripe SDKs and writes results\n * into frameworkContext for the intro screen to render.\n */\n\nimport { existsSync, statSync } from 'fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\n\nexport {\n findPackageJsons,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type PackageMatch,\n} from '@programs/shared/package-scanning';\n\n/**\n * Structured detection errors. The screen renders each kind into JSX\n * with proper formatting — keeps error data separate from presentation.\n */\nexport type RevenueDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-package-json' }\n | { kind: 'no-sdks'; scannedCount: number }\n | { kind: 'missing-posthog'; foundStripe: string[] }\n | { kind: 'missing-stripe'; foundPosthog: string[] };\n\n/** `[ABORT] <reason>` cases the revenue analytics skill can emit. */\nexport const REVENUE_ABORT_CASES: AbortCase[] = [\n {\n // Skill emits: [ABORT] Could not find a PostHog distinct_id\n match: /^could not find a posthog distinct_id$/i,\n message: 'Could not find a PostHog distinct_id',\n body:\n 'The agent could not find PostHog distinct_id usage in your codebase. ' +\n 'Your users must be identified in PostHog before they can be tagged in Stripe. ' +\n 'Please identify your users and try again.',\n docsUrl: 'https://posthog.com/docs/product-analytics/identify',\n },\n {\n // Skill emits: [ABORT] Could not find a Stripe integration\n match: /^could not find a stripe integration$/i,\n message: 'Could not find a Stripe integration',\n body:\n 'The Wizard could not find an existing Stripe customer, charge, ' +\n 'subscription, or other Stripe operations. Please run the Revenue ' +\n 'Analytics Wizard on a project with an existing Stripe integration.',\n docsUrl: 'https://posthog.com/docs/revenue-analytics',\n },\n];\n\n/**\n * Scan `session.installDir` for PostHog + Stripe SDKs. Writes detection\n * results into frameworkContext via the callback — either the detected\n * SDK lists (for the intro screen) or a `RevenueDetectError` on failure.\n *\n * The skill install happens later in the bootstrap runner, not here.\n */\nexport function detectRevenuePrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: RevenueDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n // Verify the install directory exists and is readable\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n // Find all package.json files (root + monorepo subpackages)\n const matches = findPackageJsons(installDir);\n\n if (matches.length === 0) {\n fail({ kind: 'no-package-json' });\n return;\n }\n\n // Aggregate detected SDKs across all package.json files\n const allPosthogSdks = new Set<string>();\n const allStripeSdks = new Set<string>();\n for (const match of matches) {\n for (const sdk of match.posthogSdks) allPosthogSdks.add(sdk);\n for (const sdk of match.stripeSdks) allStripeSdks.add(sdk);\n }\n\n const detectedPosthogSdks = [...allPosthogSdks];\n const detectedStripeSdks = [...allStripeSdks];\n\n if (detectedPosthogSdks.length === 0 && detectedStripeSdks.length === 0) {\n fail({ kind: 'no-sdks', scannedCount: matches.length });\n return;\n }\n\n if (detectedPosthogSdks.length === 0) {\n fail({ kind: 'missing-posthog', foundStripe: detectedStripeSdks });\n return;\n }\n\n if (detectedStripeSdks.length === 0) {\n fail({ kind: 'missing-stripe', foundPosthog: detectedPosthogSdks });\n return;\n }\n\n setFrameworkContext('detectedPosthogSdks', detectedPosthogSdks);\n setFrameworkContext('detectedStripeSdks', detectedStripeSdks);\n setFrameworkContext(\n 'detectedPackagePaths',\n matches\n .filter((m) => m.posthogSdks.length > 0 || m.stripeSdks.length > 0)\n .map((m) => m.path),\n );\n}\n","/**\n * Revenue analytics program step list.\n *\n * The detect step checks for PostHog + Stripe SDKs. The skill install\n * and agent run live in the program runner (see agent-runner.ts).\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { detectRevenuePrerequisites } from './detect.js';\n\nexport const REVENUE_ANALYTICS_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n // Headless step: no screen, no gate. onReady fires after bin.ts\n // assigns the session — the hook scans for PostHog + Stripe SDKs\n // and writes the results (or a detectError) to frameworkContext\n // for the intro screen to render.\n onReady: (ctx) =>\n detectRevenuePrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'revenue-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Revenue analytics',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Agent-skill learn-deck — the short three-line sequence shown while a\n * skill-based program (audit, revenue-analytics, agent-skill, etc.)\n * runs. Skill programs don't need the full PostHog onboarding narrative.\n */\n\nimport { Text } from 'ink';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => {\n const skillId = store?.session.skillId ?? 'unknown';\n return [\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n { content: 'The Wizard is an agent.', pause: 4000 },\n {\n pause: 60000,\n content: (\n <Text>\n Running the <Text color=\"cyan\">{skillId}</Text> skill...\n </Text>\n ),\n },\n ];\n};\n","import type { ProgramConfig } from '@programs/program-step';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { REVENUE_ANALYTICS_PROGRAM } from './steps.js';\nimport { REVENUE_ABORT_CASES } from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/revenue-analytics/index.js';\n\nexport const revenueAnalyticsConfig: ProgramConfig = {\n command: 'revenue-analytics',\n description: 'Set up PostHog for Revenue Analytics',\n id: 'revenue-analytics-setup',\n skillId: 'revenue-analytics-setup',\n steps: REVENUE_ANALYTICS_PROGRAM,\n getContentBlocks,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n run: {\n skillId: 'revenue-analytics-setup',\n integrationLabel: 'revenue-analytics-setup',\n customPrompt: () => 'Set up revenue analytics for this project.',\n successMessage: 'Revenue analytics configured!',\n reportFile: 'posthog-revenue-report.md',\n docsUrl: 'https://posthog.com/docs/revenue-analytics',\n spinnerMessage: 'Setting up revenue analytics...',\n estimatedDurationMinutes: 5,\n abortCases: REVENUE_ABORT_CASES,\n },\n requires: ['posthog-integration'],\n};\n\nexport { REVENUE_ANALYTICS_PROGRAM } from './steps.js';\nexport {\n detectRevenuePrerequisites,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type RevenueDetectError,\n} from './detect.js';\n","/**\n * Warehouse-source program step list.\n *\n * The detect step scans for warehouse-source signals. The skill install and\n * agent run live in the program runner (see agent-runner.ts). The skill drives\n * both in-CLI source creation and deep-link emission per detected source.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { detectWarehousePrerequisites } from './detect.js';\n\nexport const WAREHOUSE_SOURCE_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting data sources',\n // Headless step: no screen. onReady scans installDir and writes the\n // detected sources (or a detectError) to frameworkContext for the\n // intro screen to render.\n onReady: (ctx) =>\n detectWarehousePrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'warehouse-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Data warehouse',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { LONGER_ASK_TIMEOUT_MS } from '@agent';\nimport { WAREHOUSE_SOURCE_PROGRAM } from './steps.js';\nimport {\n WAREHOUSE_ABORT_CASES,\n getDetectedWarehouseSources,\n} from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/warehouse-source/index.js';\n\n/**\n * Inject the detected sources (and their creation mode) into the prompt so the\n * skill knows what to set up. The *how* — in-CLI creation vs deep-link, field\n * collection, validation — lives in the skill, not here.\n */\nfunction buildPrompt(session: WizardSession): string {\n const sources = getDetectedWarehouseSources(session);\n if (sources.length === 0) {\n return 'Set up a data warehouse source for this project.';\n }\n\n const lines = sources.map(\n (s) =>\n `- ${s.label} (kind: ${s.kind}, mode: ${s.mode}) — ${s.matchedSignal}`,\n );\n\n return [\n 'The wizard detected the following data warehouse sources in this project:',\n ...lines,\n '',\n 'Each signal names the file it came from. Trust that path — the wizard ' +\n 'read it. To confirm a key is set, call `check_env_keys` with the key ' +\n 'names and no `filePath`; it scans every `.env` file in the project.',\n '',\n 'Set these up in PostHog following the skill instructions: create `in-cli` ' +\n 'sources directly via the PostHog MCP after collecting credentials; for ' +\n '`deep-link` sources, provide the user the pre-filled new-source URL.',\n 'Use the `kind` string exactly as printed above for `source_type` — ' +\n 'PostHog rejects the display label.',\n ].join('\\n');\n}\n\nexport const warehouseSourceConfig: ProgramConfig = {\n command: 'warehouse',\n description: 'Detect and connect Data Warehouse sources',\n id: 'warehouse-source',\n skillId: 'data-warehouse-source-setup',\n steps: WAREHOUSE_SOURCE_PROGRAM,\n getContentBlocks,\n reportFile: 'posthog-warehouse-report.md',\n allowedTools: ['Agent'],\n run: (session: WizardSession): Promise<ProgramRun> =>\n Promise.resolve({\n skillId: 'data-warehouse-source-setup',\n integrationLabel: 'data-warehouse-source-setup',\n customPrompt: () => buildPrompt(session),\n successMessage: 'Data warehouse source connected!',\n reportFile: 'posthog-warehouse-report.md',\n docsUrl: 'https://posthog.com/docs/data-warehouse',\n spinnerMessage: 'Connecting your data source...',\n estimatedDurationMinutes: 5,\n // Same questions the orchestrator's seeded warehouse task asks, so the\n // same allowance. On the 5-minute default a user who went to fetch a\n // database password came back to a cancelled prompt and the browser\n // fallback — in the command the outro sends declines to.\n askTimeoutMs: LONGER_ASK_TIMEOUT_MS,\n abortCases: WAREHOUSE_ABORT_CASES,\n }),\n requires: ['posthog-integration'],\n};\n\nexport { WAREHOUSE_SOURCE_PROGRAM } from './steps.js';\nexport {\n detectWarehousePrerequisites,\n getDetectedWarehouseSources,\n DETECTED_WAREHOUSE_SOURCES_KEY,\n WAREHOUSE_ABORT_CASES,\n type WarehouseDetectError,\n} from './detect.js';\n","/**\n * Generic agent skill step list.\n *\n * Minimal flow: intro → health-check → auth → run → outro → skills.\n * No detection, no setup, no MCP.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const AGENT_SKILL_STEPS: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'agent-skill-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Running',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Generic agent skill program factory.\n *\n * Creates a ProgramConfig for any context-mill skill. Provide a\n * skill ID and basic UI config — the factory handles the rest.\n *\n * Usage:\n * createSkillProgram({\n * skillId: 'error-tracking-setup',\n * command: 'errors',\n * id: 'error-tracking',\n * description: 'Set up PostHog error tracking',\n * integrationLabel: 'error-tracking',\n * successMessage: 'Error tracking configured!',\n * reportFile: 'posthog-error-tracking-report.md',\n * docsUrl: 'https://posthog.com/docs/error-tracking',\n * spinnerMessage: 'Setting up error tracking...',\n * estimatedDurationMinutes: 5,\n * })\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\nimport type { AbortCase } from '@agent/types';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AGENT_SKILL_STEPS } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/agent-skill/index.js';\n\nexport interface SkillProgramOptions {\n /** Context-mill skill ID to install */\n skillId: string;\n /** CLI subcommand name */\n command: string;\n /** Unique flow key — must match a Program enum entry */\n id: string;\n /** CLI description shown in --help */\n description: string;\n /** Analytics integration label */\n integrationLabel: string;\n /** Custom prompt instruction. Appended after default project prompt. */\n customPrompt?: string;\n successMessage: string;\n reportFile: string;\n docsUrl: string;\n spinnerMessage: string;\n estimatedDurationMinutes: number;\n /** Other program ids that must be satisfied first */\n requires?: string[];\n /** Override the default outro. Receives the same args as ProgramRun.buildOutroData. */\n buildOutroData?: ProgramRun['buildOutroData'];\n /** Known `[ABORT] <reason>` cases the skill can emit. */\n abortCases?: AbortCase[];\n}\n\nexport function createSkillProgram(opts: SkillProgramOptions): ProgramConfig {\n return {\n command: opts.command,\n description: opts.description,\n id: opts.id,\n skillId: opts.skillId,\n steps: AGENT_SKILL_STEPS,\n reportFile: opts.reportFile,\n getContentBlocks,\n run: {\n skillId: opts.skillId,\n integrationLabel: opts.integrationLabel,\n customPrompt: opts.customPrompt ? () => opts.customPrompt! : undefined,\n successMessage: opts.successMessage,\n reportFile: opts.reportFile,\n docsUrl: opts.docsUrl,\n spinnerMessage: opts.spinnerMessage,\n estimatedDurationMinutes: opts.estimatedDurationMinutes,\n buildOutroData: opts.buildOutroData,\n abortCases: opts.abortCases,\n },\n requires: opts.requires,\n };\n}\n\nexport { AGENT_SKILL_STEPS } from './steps.js';\n","import type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\n\n/** `[ABORT] <reason>` cases the audit skill can emit. Reason strings are\n * defined in the skill's `Abort statuses` section. */\nexport const AUDIT_ABORT_CASES: AbortCase[] = [\n {\n match: /^no posthog sdk found$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'No PostHog SDK found',\n body:\n 'The audit needs an existing PostHog integration to review. No PostHog ' +\n 'SDK appears in this project’s dependency manifests. Run the basic ' +\n 'integration program to install PostHog first, then re-run the audit.',\n docsUrl: 'https://posthog.com/docs/getting-started/install',\n },\n];\n","import path from 'path';\nimport { writeJsonAtomic } from '@utils/atomic-ledger';\nimport { logToFile } from '@utils/debug';\nimport { AUDIT_CHECKS_FILE, type AuditCheck } from './types.js';\n\n/**\n * The 10 source-tree checks the audit runs, plus one row for the PostHog-side\n * sweep and two workflow rows at the end (so the skill's\n * `audit_resolve_checks` calls for `write-report` and `upload-notebook`\n * succeed — the skill writes the report to disk, then mirrors it into a\n * PostHog notebook as its final step).\n *\n * Every id the skill resolves needs a row here: a batch resolve rejects\n * atomically, so one missing id discards the whole call.\n *\n * `posthog-side-findings` is a sweep row, not a rule: the skill reads what\n * PostHog itself already computed for this project (error-tracking\n * recommendations, health issues) and appends one row per open finding via\n * `audit_add_checks`. That's why the seed can't enumerate them — the list is\n * whatever the project's data says today, and it grows as PostHog ships new\n * checks, with no wizard release needed.\n */\nexport const AUDIT_SEED_CHECKS: AuditCheck[] = [\n {\n id: 'sdk-installed',\n area: 'Installation',\n label: 'PostHog SDK installed',\n status: 'pending',\n },\n {\n id: 'sdk-up-to-date',\n area: 'Installation',\n label: 'SDK version up to date',\n status: 'pending',\n },\n {\n id: 'init-correct',\n area: 'Installation',\n label: 'Initialization is correct',\n status: 'pending',\n },\n {\n id: 'init-not-duplicated',\n area: 'Installation',\n label: 'One initialization per runtime',\n status: 'pending',\n },\n {\n id: 'identify-stable-distinct-id',\n area: 'Identification',\n label: 'Stable distinct_id (not session UUID)',\n status: 'pending',\n },\n {\n id: 'identify-not-late',\n area: 'Identification',\n label: 'identify() called before captures / flag evals',\n status: 'pending',\n },\n {\n id: 'cross-runtime-distinct-id',\n area: 'Identification',\n label: 'Same distinct_id across client and server',\n status: 'pending',\n },\n {\n id: 'identify-reset-on-logout',\n area: 'Identification',\n label: 'reset() called on logout / account switch',\n status: 'pending',\n },\n {\n id: 'capture-event-names-static',\n area: 'Event Capture',\n label: 'Event names are static and consistent',\n status: 'pending',\n },\n {\n id: 'capture-uses-proxy',\n area: 'Event Capture',\n label: 'Captures route through a reverse proxy',\n status: 'pending',\n },\n {\n id: 'capture-growth-events',\n area: 'Event Capture',\n label: 'Key activation events captured',\n status: 'pending',\n },\n {\n id: 'live-data-findings',\n // Area is capped at COL_AREA_WIDTH (18) in the checks viewer and never\n // flexes, so it has to fit. Labels get the flexed column.\n area: 'Live Data',\n label: 'Open findings in PostHog',\n status: 'pending',\n },\n {\n id: 'write-report',\n area: 'Write report',\n label: 'Create posthog-audit-report.md',\n status: 'pending',\n },\n {\n id: 'upload-notebook',\n area: 'Upload notebook',\n label: 'Write the report into a PostHog notebook',\n status: 'pending',\n },\n];\n\n/**\n * Atomically write a seeded ledger to the project's audit checks file.\n *\n * Each audit-flavored program (doctor, events-audit) owns its own seed\n * shape — pass the seed in so this writer stays program-agnostic.\n */\nexport function seedAuditLedger(\n installDir: string,\n checks: AuditCheck[] = AUDIT_SEED_CHECKS,\n): void {\n const target = path.join(installDir, AUDIT_CHECKS_FILE);\n writeJsonAtomic(target, checks);\n logToFile(`seedAuditLedger: wrote ${checks.length} entries to ${target}`);\n}\n","import {\n AGENT_SKILL_STEPS,\n createSkillProgram,\n} from '@programs/agent-skill/index';\nimport type { ProgramStep, ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { RunnerContext } from '@programs/runner-context';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { headlessOption, regionOption } from '@lib/headless-mode';\nimport { AUDIT_ABORT_CASES } from './detect.js';\nimport {\n AUDIT_CHECKS_FILE,\n AUDIT_CHECKS_KEY,\n AUDIT_REPORT_FILE,\n} from './types.js';\nimport { AUDIT_SEED_CHECKS, seedAuditLedger } from './seed.js';\n\n/** Audit-specific screens for the shared agent-skill pipeline. */\nconst AUDIT_SCREEN_BY_STEP: Record<string, string> = {\n intro: 'audit-intro',\n run: 'audit-run',\n outro: 'audit-outro',\n};\n\nconst seedBeforeAuditRun = (session: WizardSession): void => {\n seedAuditLedger(session.installDir);\n session.frameworkContext[AUDIT_CHECKS_KEY] = AUDIT_SEED_CHECKS;\n};\n\nconst withAuditScreens = (steps: ProgramStep[]): ProgramStep[] =>\n steps.map((step) => {\n const override = AUDIT_SCREEN_BY_STEP[step.id];\n return override ? { ...step, screenId: override } : step;\n });\n\nconst auditSteps: ProgramStep[] = withAuditScreens(AGENT_SKILL_STEPS);\n\nconst baseConfig = createSkillProgram({\n skillId: 'audit',\n command: 'audit',\n id: 'audit',\n description: 'Audit and improve your PostHog setup',\n integrationLabel: 'audit',\n customPrompt:\n 'Run a comprehensive audit of the existing PostHog integration. Follow the skill program steps in order. Do not modify any project files — only create the final audit report.',\n successMessage:\n 'Audit complete! You can view the audit report at ./posthog-audit-report.md',\n reportFile: AUDIT_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/product-analytics/best-practices',\n spinnerMessage: 'Auditing PostHog integration...',\n estimatedDurationMinutes: 5,\n requires: ['posthog-integration'],\n abortCases: AUDIT_ABORT_CASES,\n});\n\nconst auditRun = async (\n session: WizardSession,\n runner: RunnerContext,\n): Promise<ProgramRun> => {\n seedBeforeAuditRun(session);\n\n if (!baseConfig.run) {\n throw new Error('Audit program has no run configuration.');\n }\n\n const baseRun =\n typeof baseConfig.run === 'function'\n ? await baseConfig.run(session, runner)\n : baseConfig.run;\n\n return {\n ...baseRun,\n // Override the default outro so the dashboard + notebook URLs the\n // agent emits via `[DASHBOARD_URL]` / `[NOTEBOOK_URL]` are surfaced\n // on the post-run screen.\n buildOutroData: (sess, credentials) => {\n const cloudUrl = credentials.host.appHost;\n const continueUrl = sess.signup\n ? `${cloudUrl}/products?source=wizard`\n : undefined;\n\n // Note: `sess` here is the agent-runner's snapshot of session at\n // runAgent() invocation time. Any URL emissions during the run land\n // on the live store, NOT on this snapshot. The UI layer\n // (InkUI.setOutroData) merges live URLs in on top of this return\n // value, so it's safe to leave dashboardUrl/notebookUrl as undefined\n // here when the snapshot doesn't have them.\n return {\n kind: OutroKind.Success as const,\n message: baseRun.successMessage,\n reportFile: baseRun.reportFile,\n docsUrl: baseRun.docsUrl,\n continueUrl,\n dashboardUrl: session.dashboardUrl ?? undefined,\n notebookUrl: session.notebookUrl ?? undefined,\n };\n },\n };\n};\n\nexport const auditConfig: ProgramConfig = {\n ...baseConfig,\n steps: auditSteps,\n run: auditRun,\n auditLedgerFile: AUDIT_CHECKS_FILE,\n // Ledger tools are opt-in per program; pi matches on the short name.\n allowedTools: [\n 'Agent',\n WIZARD_TOOL_NAMES.auditSeedChecks,\n WIZARD_TOOL_NAMES.auditAddChecks,\n WIZARD_TOOL_NAMES.auditResolveChecks,\n ],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n // The experimental headless flag — declared on `audit` (and basic\n // integration) rather than globally. mergeCommandOptions lands it on the\n // `wizard audit` command; dispatchProgram routes it to runWizardHeadless.\n cliOptions: { ...headlessOption, ...regionOption },\n};\n","/**\n * Events-audit program.\n *\n * Mirrors the posthog-integration step list, except:\n * - The initial framework detection step is omitted — the events-audit\n * skill handles detection at agent run time.\n * - The intro step uses the audit intro screen (no framework selection\n * logic) instead of the integration intro.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nfunction needsSetup(session: WizardSession): boolean {\n const config = session.frameworkConfig;\n if (!config?.metadata.setup?.questions) return false;\n\n return config.metadata.setup.questions.some(\n (q: { key: string }) => !(q.key in session.frameworkContext),\n );\n}\n\nexport const EVENTS_AUDIT_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'audit-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'setup',\n label: 'Setup',\n screenId: 'setup',\n show: needsSetup,\n isComplete: (session) => !needsSetup(session),\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Events audit',\n screenId: 'audit-run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'mcp',\n label: 'MCP servers',\n screenId: 'mcp',\n isComplete: (session) => session.mcpComplete,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'audit-outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'keep-skills',\n label: 'Keep Skills',\n screenId: 'keep-skills',\n },\n];\n","import type { AuditCheck } from '@programs/audit/types';\n\n/**\n * The 7 phases the events-audit skill marches through. One check per area\n * so PendingChecksList renders a clean linear pipeline (area = bold header,\n * single row = the active spinner).\n *\n * Phase ids match what the skill's step files resolve via\n * `mcp__wizard-tools__audit_resolve_checks` as each phase completes. The\n * skill's step 1 also seeds these same ids — keep both in sync so the\n * wizard pre-seed and the skill's MCP seed agree.\n */\nexport const EVENTS_AUDIT_SEED_CHECKS: AuditCheck[] = [\n {\n id: 'detect-sdk',\n area: 'Detect SDK',\n label: 'Identify PostHog SDK(s) in dependencies',\n status: 'pending',\n },\n {\n id: 'scan-sites',\n area: 'Scan capture sites',\n label: 'Grep capture/identify/group call sites',\n status: 'pending',\n },\n {\n id: 'enrich-sites',\n area: 'Enrich',\n label: 'Subagent fan-out to read capture files',\n status: 'pending',\n },\n {\n id: 'query-volume',\n area: 'Query PostHog',\n label: '30-day volume + last_seen via MCP',\n status: 'pending',\n },\n {\n id: 'write-report',\n area: 'Write report',\n label: 'Create posthog-events-audit-report.md',\n status: 'pending',\n },\n {\n id: 'create-dashboard',\n area: 'Create dashboard',\n label: 'Optional: dashboard for resolved events',\n status: 'pending',\n },\n {\n id: 'upload-notebook',\n area: 'Upload notebook',\n label: 'Write the report into a PostHog notebook',\n status: 'pending',\n },\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport { SPINNER_MESSAGE } from '@programs/framework-config';\nimport { isUsingTypeScript } from '@utils/setup-utils';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { EVENTS_AUDIT_PROGRAM } from './steps.js';\nimport { AUDIT_CHECKS_FILE, AUDIT_CHECKS_KEY } from '@programs/audit/types';\nimport { seedAuditLedger } from '@programs/audit/seed';\nimport { EVENTS_AUDIT_SEED_CHECKS } from './seed.js';\n\n// SETUP_REPORT_FILE is also re-exported for backward compat with existing\n// imports from `@programs/events-audit`. EVENT_INVENTORY_FILE and\n// EVENT_INVENTORY_PART_PATTERN are only used by yara-hooks, which imports\n// them directly from `./constants` — no re-export needed.\nimport { SETUP_REPORT_FILE } from './constants.js';\nexport { SETUP_REPORT_FILE };\n\nconst DOCS_URL = 'https://posthog.com/docs/product-analytics/best-practices';\n\n/**\n * No CLI word of its own since the audit family took over: `wizard audit\n * events` is the live path, and it resolves to the context-mill `audit-events`\n * skill (whose id AuditRunScreen keys its slides on), not to this config.\n * Registered so its id stays resolvable; nothing dispatches to it today.\n */\nexport const eventsAuditConfig: ProgramConfig = {\n description: 'Audit PostHog event tracking in this project',\n id: 'events-audit',\n skillId: 'events-audit',\n steps: EVENTS_AUDIT_PROGRAM,\n // Top-level reportFile so AuditRunScreen can resolve the report path\n // synchronously without unwrapping the deferred `run` function.\n reportFile: SETUP_REPORT_FILE,\n auditLedgerFile: AUDIT_CHECKS_FILE,\n allowedTools: [\n 'Agent',\n WIZARD_TOOL_NAMES.auditSeedChecks,\n WIZARD_TOOL_NAMES.auditAddChecks,\n WIZARD_TOOL_NAMES.auditResolveChecks,\n ],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n\n run: (session: WizardSession): Promise<ProgramRun> => {\n const typeScriptDetected = isUsingTypeScript({\n installDir: session.installDir,\n });\n session.typescript = typeScriptDetected;\n\n // Seed the audit ledger so AuditRunScreen has something to render\n // before the agent emits its first check update. The events-audit\n // ledger is the 6-phase pipeline, not the doctor's 10 integrity checks.\n seedAuditLedger(session.installDir, EVENTS_AUDIT_SEED_CHECKS);\n session.frameworkContext[AUDIT_CHECKS_KEY] = EVENTS_AUDIT_SEED_CHECKS;\n\n return Promise.resolve({\n skillId: 'events-audit',\n integrationLabel: 'events-audit',\n spinnerMessage: SPINNER_MESSAGE,\n successMessage:\n 'Events audit complete! You can view the report at ./posthog-events-audit-report.md',\n estimatedDurationMinutes: 5,\n reportFile: SETUP_REPORT_FILE,\n docsUrl: DOCS_URL,\n errorMessage: 'Events audit failed',\n\n customPrompt: (ctx) =>\n `Audit PostHog event capture in this project. Do not modify any project files — produce a read-only report only.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- TypeScript: ${typeScriptDetected ? 'Yes' : 'No'}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n`,\n\n buildOutroData: (sess, credentials) => {\n const cloudUrl = credentials.host.appHost;\n const continueUrl = sess.signup\n ? `${cloudUrl}/products?source=wizard`\n : undefined;\n // The agent emits `[DASHBOARD_URL] <url>` once it creates the\n // dashboard; the SDK-message interceptor stores it on the session.\n // Fall back to the dashboards index if nothing was emitted.\n const dashboardUrl =\n sess.dashboardUrl ?? (cloudUrl ? `${cloudUrl}/dashboard` : undefined);\n\n // The agent emits `[NOTEBOOK_URL] <url>` once it uploads the report\n // to a PostHog notebook. No fallback: if the notebook upload was\n // skipped (e.g. MCP unavailable) we just don't show a link.\n const notebookUrl = sess.notebookUrl ?? undefined;\n\n return {\n kind: OutroKind.Success as const,\n message: 'Your events audit was successful',\n reportFile: SETUP_REPORT_FILE,\n changes: [],\n docsUrl: DOCS_URL,\n continueUrl,\n dashboardUrl,\n notebookUrl,\n };\n },\n });\n },\n};\n\nexport { EVENTS_AUDIT_PROGRAM } from './steps.js';\n","import type { ProgramStep } from '@programs/program-step';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const POSTHOG_DOCTOR_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'doctor-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'report',\n label: 'Doctor report',\n screenId: 'doctor-report',\n isComplete: (session) => session.outroData !== null,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n];\n","import { z } from 'zod';\n\nexport const HealthIssueSeveritySchema = z.enum([\n 'critical',\n 'warning',\n 'info',\n]);\nexport type HealthIssueSeverity = z.infer<typeof HealthIssueSeveritySchema>;\n\nexport const HealthIssueStatusSchema = z.enum(['active', 'resolved']);\n\nexport const HealthIssueSchema = z.object({\n id: z.string(),\n kind: z.string(),\n severity: HealthIssueSeveritySchema,\n status: HealthIssueStatusSchema,\n dismissed: z.boolean(),\n created_at: z.string(),\n updated_at: z.string(),\n resolved_at: z.string().nullable().optional(),\n});\nexport type HealthIssue = z.infer<typeof HealthIssueSchema>;\n\nexport const HealthIssueListResponseSchema = z.object({\n results: z.array(HealthIssueSchema),\n count: z.number().optional(),\n next: z.string().nullable().optional(),\n previous: z.string().nullable().optional(),\n});\nexport type HealthIssueListResponse = z.infer<\n typeof HealthIssueListResponseSchema\n>;\n\nexport interface HealthIssueSummary {\n total: number;\n by_severity: Record<HealthIssueSeverity, number>;\n}\n","import axios from 'axios';\nimport { analytics } from '@utils/analytics';\nimport { handleApiError } from '@shared/api';\nimport { WIZARD_USER_AGENT } from '@shared/constants';\nimport { HealthIssueListResponseSchema, type HealthIssue } from './types';\n\nexport async function fetchHealthIssues(\n accessToken: string,\n baseUrl: string,\n projectId: number,\n): Promise<HealthIssue[]> {\n const endpoint = `/api/environments/${projectId}/health_issues/`;\n const url = `${baseUrl}${endpoint}?status=active&dismissed=false&limit=250`;\n try {\n const response = await axios.get(url, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n return HealthIssueListResponseSchema.parse(response.data).results;\n } catch (error) {\n const apiError = handleApiError(error, 'fetch health issues');\n analytics.captureException(apiError, { endpoint, baseUrl, projectId });\n throw apiError;\n }\n}\n","import { POSTHOG_DOCS_URL } from '@shared/constants';\n\nexport interface KindMeta {\n title: string;\n description: string;\n docsUrl: string;\n}\n\nexport const KIND_METADATA: Record<string, KindMeta> = {\n ingestion_lag: {\n title: 'Ingestion is delayed',\n description:\n 'Events are being received but are taking longer than usual to appear.',\n docsUrl: `${POSTHOG_DOCS_URL}/support/troubleshooting`,\n },\n ingestion_warning: {\n title: 'Ingestion warnings on recent events',\n description:\n 'Some recent events were rejected or flagged by the ingestion pipeline.',\n docsUrl: `${POSTHOG_DOCS_URL}/support/troubleshooting`,\n },\n sdk_outdated: {\n title: 'SDK version is out of date',\n description:\n 'One or more SDKs are running an old version. Upgrade to get the latest fixes.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries`,\n },\n no_live_events: {\n title: 'No $pageview or $screen events in the last 30 days',\n description:\n 'PostHog is not receiving page or screen events from this project.',\n docsUrl: `${POSTHOG_DOCS_URL}/getting-started/install`,\n },\n no_pageleave_events: {\n title: '$pageleave events not being sent',\n description:\n 'Enable pageleave tracking to power bounce rate and session duration.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries/js#config`,\n },\n scroll_depth: {\n title: 'Scroll depth tracking disabled',\n description:\n 'Turn on scroll depth to capture how far users read each page.',\n docsUrl: `${POSTHOG_DOCS_URL}/libraries/js#config`,\n },\n authorized_urls: {\n title: 'No authorized URLs configured',\n description:\n 'Some web analytics filters require at least one authorized URL to work.',\n docsUrl: `${POSTHOG_DOCS_URL}/web-analytics/faq`,\n },\n reverse_proxy: {\n title: 'No reverse proxy detected',\n description: 'A reverse proxy reduces data loss from ad blockers.',\n docsUrl: `${POSTHOG_DOCS_URL}/advanced/proxy`,\n },\n web_vitals: {\n title: 'Web Vitals tracking disabled',\n description:\n 'Enable Web Vitals to capture LCP, CLS and other performance metrics.',\n docsUrl: `${POSTHOG_DOCS_URL}/web-analytics/web-vitals`,\n },\n materialized_view_failure: {\n title: 'A materialized view is failing',\n description: 'A data modeling pipeline failed its most recent run.',\n docsUrl: `${POSTHOG_DOCS_URL}/data-warehouse`,\n },\n external_data_failure: {\n title: 'External data source is failing',\n description: 'An external data source sync failed and data may be stale.',\n docsUrl: `${POSTHOG_DOCS_URL}/data-warehouse/sources`,\n },\n};\n\nexport const UNKNOWN_KIND_META: KindMeta = {\n title: 'Unknown issue',\n description:\n 'PostHog reported an issue kind the wizard does not yet recognize.',\n docsUrl: POSTHOG_DOCS_URL,\n};\n\nexport function getKindMeta(kind: string): KindMeta {\n return KIND_METADATA[kind] ?? { ...UNKNOWN_KIND_META, title: kind };\n}\n","import type { ProgramConfig } from '@programs/program-step';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { POSTHOG_DOCTOR_PROGRAM } from './steps.js';\n\nexport const posthogDoctorConfig: ProgramConfig = {\n command: 'doctor',\n description: 'Diagnose your PostHog project setup',\n id: 'posthog-doctor',\n requiresAi: false,\n steps: POSTHOG_DOCTOR_PROGRAM,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n};\n\nexport { POSTHOG_DOCTOR_PROGRAM } from './steps.js';\nexport { fetchHealthIssues } from './fetch.js';\nexport { getKindMeta, KIND_METADATA } from './kind-metadata.js';\nexport type { KindMeta } from './kind-metadata.js';\nexport type {\n HealthIssue,\n HealthIssueSeverity,\n HealthIssueSummary,\n} from './types.js';\n","import { existsSync, statSync } from 'fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { findPackageJsons } from '@programs/shared/package-scanning';\n\nexport type WebAnalyticsDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-package-json' }\n | { kind: 'no-posthog'; scannedCount: number };\n\nexport const WEB_ANALYTICS_ABORT_CASES: AbortCase[] = [\n {\n match: /^no web analytics events$/i,\n message: 'No web analytics events',\n body:\n 'The doctor found no $pageview events in the last 30 days, so there is ' +\n 'nothing to audit yet. Make sure PostHog is initialized and capturing ' +\n 'pageviews, then run the doctor again.',\n docsUrl: 'https://posthog.com/docs/web-analytics/getting-started',\n },\n {\n match: /^insufficient permissions$/i,\n errorCode: ErrorCodes.AuthMissingScope,\n message: 'Insufficient permissions',\n body:\n 'The doctor could not query your project — the authenticated token is ' +\n 'missing query access. Re-run the wizard to sign in again, or use a key ' +\n 'with read access to your events.',\n docsUrl: 'https://posthog.com/docs/web-analytics',\n },\n {\n match: /^posthog sdk not installed$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'PostHog SDK not installed',\n body:\n 'The doctor could not find a PostHog SDK in this project. Install and ' +\n 'configure PostHog first (run `npx @posthog/wizard`), then run the ' +\n 'doctor to check your web analytics setup.',\n docsUrl: 'https://posthog.com/docs/libraries/js',\n },\n];\n\nexport function detectWebAnalyticsPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: WebAnalyticsDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n const matches = findPackageJsons(installDir);\n\n if (matches.length === 0) {\n fail({ kind: 'no-package-json' });\n return;\n }\n\n const sdks = [...new Set(matches.flatMap((m) => m.posthogSdks))];\n\n if (sdks.length === 0) {\n fail({ kind: 'no-posthog', scannedCount: matches.length });\n return;\n }\n\n setFrameworkContext('detectedPosthogSdks', sdks);\n}\n","import type { ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { detectWebAnalyticsPrerequisites } from './detect.js';\n\nexport const WEB_ANALYTICS_DOCTOR_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n onReady: (ctx) =>\n detectWebAnalyticsPrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n ...AGENT_SKILL_STEPS,\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport { createSkillProgram } from '../agent-skill/index.js';\nimport { WEB_ANALYTICS_DOCTOR_PROGRAM } from './steps.js';\nimport { WEB_ANALYTICS_ABORT_CASES } from './detect.js';\n\nconst REPORT_FILE = 'posthog-web-analytics-report.md';\nconst DOCS_URL = 'https://posthog.com/docs/web-analytics';\n\nexport const webAnalyticsDoctorConfig: ProgramConfig = {\n ...createSkillProgram({\n skillId: 'web-analytics-doctor',\n command: 'web-analytics',\n id: 'web-analytics-doctor',\n description: 'Audit and fix your PostHog web analytics setup',\n integrationLabel: 'web-analytics-doctor',\n customPrompt:\n \"Run the web-analytics-doctor skill to check this project's PostHog web \" +\n 'analytics setup. Audit read-only first, then present the findings to the ' +\n 'user with a single wizard_ask multi-select and apply only the fixes they ' +\n 'choose — editing project code and/or PostHog project settings via the ' +\n 'MCP — before writing the report.',\n successMessage:\n 'Web analytics check complete! You can view the report at ./posthog-web-analytics-report.md',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Checking your web analytics setup...',\n estimatedDurationMinutes: 5,\n requires: ['posthog-integration'],\n abortCases: WEB_ANALYTICS_ABORT_CASES,\n }),\n steps: WEB_ANALYTICS_DOCTOR_PROGRAM,\n parentCommand: 'audit',\n};\n\nexport { WEB_ANALYTICS_DOCTOR_PROGRAM } from './steps.js';\nexport {\n detectWebAnalyticsPrerequisites,\n WEB_ANALYTICS_ABORT_CASES,\n type WebAnalyticsDetectError,\n} from './detect.js';\n","import type { ProgramStep } from '@programs/program-step';\nimport { RunPhase } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\n\nexport const MIGRATION_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'migration-intro',\n gate: (session) => session.setupConfirmed,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'run',\n label: 'Migration',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","/**\n * Vendor cost stack — the multi-tool baseline a typical migration target has\n * before consolidating onto PostHog. Numbers from each vendor's published\n * starter pricing.\n */\n\nimport { Text } from 'ink';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const VENDOR_STACK_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 600,\n pause: 9000,\n lines: [\n <Text bold>{' Typical pre-migration stack'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color=\"gray\">{' Sentry'}</Text>\n <Text>{' error tracking '}</Text>\n <Text color=\"red\">{'$26/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' LaunchDarkly'}</Text>\n <Text>{' feature flags '}</Text>\n <Text color=\"red\">{'$8.33/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' Amplitude'}</Text>\n <Text>{' product analytics '}</Text>\n <Text color=\"red\">{'$49/mo+'}</Text>\n </Text>,\n <Text>\n <Text color=\"gray\">{' Braintrust'}</Text>\n <Text>{' AI observability '}</Text>\n <Text color=\"red\">{'$50/mo+'}</Text>\n </Text>,\n <Text color=\"gray\">{' ─────────────────────────────────────'}</Text>,\n <Text>\n <Text>{' Total'}</Text>\n <Text>{' '}</Text>\n <Text bold color=\"red\">\n {'$133/mo+'}\n </Text>\n </Text>,\n <Text dimColor>{' plus ~450KB of JavaScript SDKs'}</Text>,\n ],\n};\n","/**\n * PostHog free-tier highlights — the numbers a migrating team gets back when\n * they consolidate. Sourced from posthog.com/pricing.md.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const FREE_TIER_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 9000,\n lines: [\n <Text bold>{' Free every month, on every product'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>events </Text>\n <Text dimColor>product analytics</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>requests </Text>\n <Text dimColor>feature flags + experiments</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 5,000 '}</Text>\n <Text>recordings </Text>\n <Text dimColor>session replay</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 100,000 '}</Text>\n <Text>exceptions </Text>\n <Text dimColor>error tracking</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 100,000 '}</Text>\n <Text>events </Text>\n <Text dimColor>AI observability</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 50 GB '}</Text>\n <Text>logs </Text>\n <Text dimColor>logs</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,500 '}</Text>\n <Text>responses </Text>\n <Text dimColor>surveys</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' 1,000,000 '}</Text>\n <Text>rows </Text>\n <Text dimColor>data warehouse</Text>\n </Text>,\n ],\n};\n","/**\n * Pricing structure block — what happens after the free tier.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PRICING_STRUCTURE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 500,\n pause: 8000,\n lines: [\n <Text bold>{' After the free tier'}</Text>,\n <Text> </Text>,\n <Text>\n <Text color={Colors.accent}>{' $0 '}</Text>\n <Text>base price · pay only for what you use</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>per-event prices decrease with volume</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>no per-seat charges — your whole team is included</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>web analytics bundled with product analytics</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>experiments bundled with feature flags</Text>\n </Text>,\n <Text>\n <Text color={Colors.accent}>{' ◆ '}</Text>\n <Text>revenue analytics bundled with data warehouse</Text>\n </Text>,\n ],\n};\n","/**\n * Migration learn deck (statsig variant). Statsig is the only `migrate`\n * variant today, so this deck plays as-is when the wizard runs\n * `migrate --product=statsig`. Three movements:\n *\n * 1. Welcome and reassure.\n * 2. What to expect — the migration is replacement-only, takes a few\n * minutes, leaves the build green.\n * 3. What's a little different — how flags and experiments work in\n * PostHog, presented as right-way guidance rather than gotchas.\n *\n * FF/experiments guidance paraphrased from PostHog public docs:\n * - posthog.com/docs/feature-flags/best-practices\n * - posthog.com/docs/feature-flags/common-questions\n * - posthog.com/docs/experiments/best-practices\n */\n\nimport { Text } from 'ink';\nimport type { WizardStore } from '@ui/tui/store';\nimport { Colors } from '@ui/tui/styles';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { PRODUCT_SUITE_BLOCK } from '@ui/tui/decks/posthog-integration/product-suite';\nimport { LINE_CHART_BLOCK } from '@ui/tui/decks/posthog-integration/line-chart';\nimport { FUNNEL_BLOCK } from '@ui/tui/decks/posthog-integration/funnel';\nimport { VENDOR_STACK_BLOCK } from './vendor-stack.js';\nimport { FREE_TIER_BLOCK } from './free-tier.js';\nimport { PRICING_STRUCTURE_BLOCK } from './pricing-structure.js';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => [\n // ── Welcome ────────────────────────────────────────────────────────────\n {\n content: 'Hello.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content:\n 'As we speak, it’s making a plan to migrate from Statsig to PostHog.',\n pause: 6000,\n },\n\n {\n content: 'PostHog covers the cost of running this agent.',\n pause: 4000,\n },\n\n { type: 'clear', pause: 2000 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n persist: true,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n { type: 'clear', pause: 2000 },\n\n // ── What to expect ─────────────────────────────────────────────────────\n { content: 'Here’s what to expect.', pause: 3000 },\n\n { content: 'The migration takes about ten minutes.', pause: 3000 },\n\n {\n content:\n 'Every Statsig call gets replaced in place with its PostHog equivalent.',\n pause: 5500,\n },\n\n {\n content:\n 'Nothing new gets added. No extra captures, no surprise instrumentation.',\n pause: 5500,\n },\n\n {\n content:\n 'The Statsig package gets removed at the end. We’ll run build and lint to clean up after ourselves.',\n pause: 6500,\n },\n\n { type: 'clear', pause: 2000 },\n\n // ── What's a little different ─────────────────────────────────────────\n {\n content: 'A few things work a little differently in PostHog.',\n pause: 4500,\n },\n\n {\n content: (\n <Text>\n Flags evaluate against a stable user. Call{' '}\n <Text bold color={Colors.accent}>\n identify()\n </Text>{' '}\n first, then check the flag.\n </Text>\n ),\n pause: 6000,\n persist: true,\n },\n\n {\n content:\n 'For anything in the first paint, evaluate server-side and bootstrap the values into the client.',\n pause: 6500,\n },\n\n {\n content: (\n <Text>\n In production, route requests through a reverse proxy to avoid ad\n blockers breaking your flags.{'\\n'}\n <Text dimColor>https://posthog.com/docs/advanced/proxy</Text>\n </Text>\n ),\n pause: 6500,\n persist: true,\n },\n\n {\n content:\n 'When a flag reaches 100% rollout, retire it. Flags are signals, not switches.',\n pause: 5500,\n },\n\n {\n content: (\n <Text>\n Name flags descriptively. No double negatives. Reflect the return type.{' '}\n <Text dimColor>For example </Text>\n <Text bold>show-new-checkout</Text>\n <Text dimColor>.</Text>\n </Text>\n ),\n pause: 6500,\n persist: true,\n },\n\n { type: 'clear', pause: 1500 },\n\n // ── Experiments ────────────────────────────────────────────────────────\n {\n content: (\n <Text bold color={Colors.accent}>\n Experiments\n </Text>\n ),\n pause: 2500,\n persist: true,\n },\n\n {\n content:\n 'Change one thing per variant. Multiple changes in one variant blur the result.',\n pause: 5500,\n },\n\n {\n content:\n 'Decide the running time up front. PostHog includes a sample-size and duration calculator in the setup flow.',\n pause: 6500,\n },\n\n {\n content: 'Roll out to 5–10% first. Watch the metrics. Then increase.',\n pause: 5000,\n },\n\n {\n content:\n 'Exclude users who already completed the flow. They can’t be affected by the test.',\n pause: 5500,\n },\n\n { type: 'clear', pause: 1500 },\n\n // ── Close ──────────────────────────────────────────────────────────────\n {\n content: 'Flags and experiments live alongside the rest of your data.',\n pause: 4500,\n },\n\n {\n content: 'Ship behind a flag, watch replays, check analytics for impact.',\n pause: 4500,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content:\n 'PostHog also provides every other analytics and AI tool to build your product.',\n pause: 4500,\n },\n\n PRODUCT_SUITE_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'And consolidating onto one platform saves real money.',\n pause: 4500,\n },\n\n { content: 'Here’s the math.', pause: 1500 },\n\n VENDOR_STACK_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'Pricing is usage-based, with a generous free tier.',\n pause: 4000,\n },\n\n FREE_TIER_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n PRICING_STRUCTURE_BLOCK,\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'Gain clarity and really understand your users.',\n pause: 4000,\n },\n\n { content: 'Use trends to measure growth.', pause: 2500 },\n\n LINE_CHART_BLOCK,\n\n { type: 'clear', pause: 500 },\n\n { content: 'Use funnels to reveal bottlenecks.', pause: 2500 },\n\n FUNNEL_BLOCK,\n];\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { AbortCase } from '@agent/types';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { MIGRATION_PROGRAM } from './steps.js';\nimport { getContentBlocks } from '../../ui/tui/decks/migration/index.js';\n\nconst MIGRATION_REPORT_FILE = 'migration-report.md';\n\nconst MIGRATION_ABORT_CASES: AbortCase[] = [\n {\n match: /^no source-sdk calls found$/i,\n message: 'No source-SDK calls found',\n body:\n 'The migration needs an existing third-party SDK to migrate from. No ' +\n 'calls to the source SDK appear anywhere in this project. If you ' +\n \"haven't installed PostHog yet, you don't need this command — run \" +\n '`npx @posthog/wizard@latest` to add PostHog from scratch.',\n },\n];\n\n// Default skill id when nothing else picks one. The `wizard migrate <vendor>`\n// subcommands override this via skillCommandFactory using each manifest\n// entry's skillId, so this default only kicks in for legacy callers (e.g.\n// programmatic uses of migrationConfig directly).\nconst DEFAULT_MIGRATE_SKILL_ID = 'migrate-statsig';\n\nexport const migrationConfig: ProgramConfig = {\n command: 'migrate',\n description: 'Migrate to PostHog from another analytics provider',\n id: 'migration',\n skillId: DEFAULT_MIGRATE_SKILL_ID,\n steps: MIGRATION_PROGRAM,\n reportFile: MIGRATION_REPORT_FILE,\n getContentBlocks,\n allowedTools: ['Agent'],\n disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],\n run: {\n skillId: DEFAULT_MIGRATE_SKILL_ID,\n integrationLabel: 'migration',\n customPrompt: () =>\n 'Migrate this project from its existing third-party analytics, ' +\n 'feature-flag, and observability tools to PostHog. Run the `migrate` ' +\n 'skill end-to-end: follow the step chain starting at ' +\n 'references/1-presence.md. Only replace existing source-SDK call sites ' +\n 'with PostHog equivalents — make zero unrelated changes and no ' +\n `net-new instrumentation. The final report is written to ./${MIGRATION_REPORT_FILE}.`,\n successMessage: `Migration complete! View the report at ./${MIGRATION_REPORT_FILE}`,\n reportFile: MIGRATION_REPORT_FILE,\n docsUrl: '',\n spinnerMessage: 'Migrating to PostHog...',\n estimatedDurationMinutes: 8,\n abortCases: MIGRATION_ABORT_CASES,\n },\n requires: ['posthog-integration'],\n};\n\nexport { MIGRATION_PROGRAM } from './steps.js';\n","/**\n * Source maps upload prerequisite detection.\n *\n * Scans the project for signals that identify the platform and build system,\n * then maps to one of the context-mill `error-tracking-upload-source-maps-*`\n * skill variants. Results are written to frameworkContext for the intro\n * screen to render and for the agent prompt to consume.\n */\n\nimport type { Dirent } from 'fs';\nimport { readdirSync, existsSync, statSync } from 'fs';\nimport { join, relative } from 'path';\nimport {\n IGNORED_DIRS,\n MAX_DIR_ENTRIES,\n MAX_WALK_FILES,\n safeReadFile,\n} from '@utils/bounded-fs';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\n\n/**\n * Skill variants published under the `error-tracking-upload-source-maps`\n * category in context-mill. The agent loads\n * `error-tracking-upload-source-maps-<variant>`.\n */\nexport type SkillVariant =\n | 'web'\n | 'nextjs'\n | 'node'\n | 'react'\n | 'angular'\n | 'nuxt'\n | 'react-native'\n | 'android'\n | 'flutter'\n | 'ios'\n | 'vite'\n | 'webpack'\n | 'rollup'\n | 'go'\n | 'rust';\n\nconst DISPLAY_NAME: Record<SkillVariant, string> = {\n web: 'Web (JavaScript)',\n nextjs: 'Next.js',\n node: 'Node.js',\n react: 'React',\n angular: 'Angular',\n nuxt: 'Nuxt',\n 'react-native': 'React Native',\n android: 'Android',\n flutter: 'Flutter',\n ios: 'iOS',\n vite: 'Vite',\n webpack: 'Webpack',\n rollup: 'Rollup',\n go: 'Go',\n rust: 'Rust',\n};\n\n/**\n * Variants the wizard can wire up source-map upload for automatically. Go and\n * Rust upload native debug symbols (`posthog-cli symbol-sets upload`) instead\n * of source maps, but the wiring is the same shape.\n *\n * Invariant: every variant listed here must have a published\n * `error-tracking-upload-source-maps-<variant>` skill in context-mill —\n * STEP 2 of the run installs that exact id, so an entry without a shipped\n * skill fails every run for that stack.\n */\nexport const AUTOMATABLE_VARIANTS: readonly SkillVariant[] = [\n 'android',\n 'ios',\n 'react-native',\n 'flutter',\n 'go',\n 'rust',\n 'web',\n 'nextjs',\n 'node',\n 'react',\n 'angular',\n 'nuxt',\n 'vite',\n 'webpack',\n 'rollup',\n];\n\n/**\n * Variants the wizard pre-installs a machine-global `posthog-cli` for — their\n * build shells out to it with no npx / local-dep fallback. Web JS variants stay\n * out. React Native counts as native here — its Xcode build phases and Gradle\n * tasks invoke the global CLI. Flutter counts too despite emitting plain\n * `.js.map` files: a Flutter project has no `package.json`, so there is no npx\n * or local dependency to fall back to when the post-build step invokes the CLI.\n */\nexport const VARIANTS_REQUIRING_POSTHOG_CLI: ReadonlySet<SkillVariant> =\n new Set(['ios', 'android', 'react-native', 'flutter', 'go', 'rust']);\n\n/**\n * Automatable variants whose SDK the wizard's default flow cannot install —\n * remediation for a missing SDK is a manual install (posthog-go /\n * posthog-rs), not `npx @posthog/wizard`. Drives the missing-SDK copy in the\n * picker and the classifier.\n */\nexport const MANUAL_SDK_INSTALL: Partial<Record<SkillVariant, string>> = {\n go: 'add the posthog-go module first',\n rust: 'add the posthog-rs crate first',\n};\n\nexport const MANUAL_SDK_VARIANTS: readonly SkillVariant[] = Object.keys(\n MANUAL_SDK_INSTALL,\n) as SkillVariant[];\n\n/** Dependency string that identifies the Rust SDK in Cargo.toml. */\nexport const RUST_SDK_CRATE = 'posthog-rs';\n\n/** Module path that identifies the Go SDK in go.mod. */\nexport const GO_SDK_MODULE = 'github.com/posthog/posthog-go';\n\nconst POSTHOG_SDKS = new Set([\n 'posthog-js',\n 'posthog-node',\n 'posthog-react-native',\n 'posthog-android',\n 'posthog-ios',\n]);\n\nconst GRADLE_FILES = new Set([\n 'build.gradle',\n 'build.gradle.kts',\n 'settings.gradle',\n 'settings.gradle.kts',\n]);\n\n/**\n * Structured detection errors. The screen renders each kind into JSX\n * with proper formatting — keeps error data separate from presentation.\n */\nexport type SourceMapsDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-project-files' }\n | { kind: 'unsupported-platform'; detected: string }\n | { kind: 'no-posthog-sdk'; platform: SkillVariant };\n\n/** `[ABORT] <reason>` cases the source maps skill can emit. */\nexport const SOURCE_MAPS_ABORT_CASES: AbortCase[] = [\n {\n match: /^no posthog sdk detected$/i,\n errorCode: ErrorCodes.DetectNoPosthogSdk,\n message: 'No PostHog SDK detected',\n body:\n 'The agent could not find a PostHog SDK in your project. ' +\n 'Source map upload requires the SDK to already be installed so it can ' +\n 'report errors. Run `npx @posthog/wizard` first to install the SDK ' +\n '(for Go and Rust, add posthog-go / posthog-rs by hand first — the ' +\n 'wizard cannot install the SDK for those stacks yet).',\n docsUrl: 'https://posthog.com/docs/error-tracking',\n },\n {\n match: /^build command not found$/i,\n message: 'Build command not found',\n body:\n 'The agent could not identify how to build your project. Source map ' +\n 'upload runs as part of the production build. Add a build script to ' +\n 'your project and run this wizard again.',\n docsUrl: 'https://posthog.com/docs/error-tracking/upload-source-maps',\n },\n {\n match: /^bare react native not supported$/i,\n errorCode: ErrorCodes.DetectUnsupportedPlatform,\n message: 'Bare React Native is not supported',\n body:\n 'Source-map upload for React Native requires Expo, and this project ' +\n 'has no `expo` package. Bare React Native builds cannot inject the ' +\n 'chunk IDs PostHog needs to resolve stack traces.',\n docsUrl:\n 'https://posthog.com/docs/error-tracking/upload-source-maps/react-native',\n },\n];\n\n// ── File / dependency probes ─────────────────────────────────────────\n\ninterface ProjectSignals {\n packageJsons: Array<{ path: string; deps: Set<string> }>;\n hasXcodeProject: boolean;\n hasPodfile: boolean;\n hasSwiftPackage: boolean;\n hasGradle: boolean;\n hasPubspec: boolean;\n scannedFileCount: number;\n}\n\nfunction collectSignals(installDir: string, maxDepth = 3): ProjectSignals {\n const signals: ProjectSignals = {\n packageJsons: [],\n hasXcodeProject: false,\n hasPodfile: false,\n hasSwiftPackage: false,\n hasGradle: false,\n hasPubspec: false,\n scannedFileCount: 0,\n };\n\n // Explicit stack — no call-stack recursion.\n const stack: Array<{ dir: string; depth: number }> = [\n { dir: installDir, depth: 0 },\n ];\n\n while (stack.length > 0 && signals.scannedFileCount < MAX_WALK_FILES) {\n const { dir, depth } = stack.pop()!;\n\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n // A directory contributes at most MAX_DIR_ENTRIES entries.\n if (entries.length > MAX_DIR_ENTRIES) entries.length = MAX_DIR_ENTRIES;\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') && entry.name !== '.') continue;\n if (IGNORED_DIRS.has(entry.name)) continue;\n\n const fullPath = join(dir, entry.name);\n\n if (entry.isFile()) {\n signals.scannedFileCount += 1;\n if (entry.name === 'package.json') {\n const content = safeReadFile(fullPath);\n if (content === null) continue;\n try {\n const pkg = JSON.parse(content) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = new Set([\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ]);\n signals.packageJsons.push({\n path: relative(installDir, fullPath) || 'package.json',\n deps,\n });\n } catch {\n // skip malformed package.json\n }\n } else if (entry.name === 'Podfile') {\n signals.hasPodfile = true;\n } else if (entry.name === 'Package.swift') {\n signals.hasSwiftPackage = true;\n } else if (entry.name === 'pubspec.yaml') {\n signals.hasPubspec = true;\n } else if (GRADLE_FILES.has(entry.name)) {\n signals.hasGradle = true;\n }\n } else if (entry.isDirectory()) {\n if (entry.name.endsWith('.xcodeproj')) {\n signals.hasXcodeProject = true;\n } else if (depth < maxDepth) {\n stack.push({ dir: fullPath, depth: depth + 1 });\n }\n }\n }\n }\n\n return signals;\n}\n\n// ── Skill selection ──────────────────────────────────────────────────\n\nfunction pickJsVariant(deps: Set<string>): SkillVariant {\n // Opinionated full-stack frameworks first — they own their build pipeline\n // and have dedicated skill variants, so bundler detection underneath\n // them is irrelevant.\n if (deps.has('react-native')) return 'react-native';\n if (deps.has('nuxt')) return 'nuxt';\n if (deps.has('next')) return 'nextjs';\n if (deps.has('@angular/core')) return 'angular';\n // Bundlers next — prefer these over the bare `react` variant because\n // their skills are simpler (one bundler-plugin config) than wiring\n // posthog-cli into an arbitrary React setup.\n if (deps.has('vite')) return 'vite';\n if (deps.has('webpack')) return 'webpack';\n if (deps.has('rollup')) return 'rollup';\n // Plain React with no recognised bundler.\n if (deps.has('react')) return 'react';\n // Server-only Node project\n if (deps.has('posthog-node')) return 'node';\n // Fallback: generic web\n return 'web';\n}\n\nfunction selectVariant(signals: ProjectSignals): SkillVariant | null {\n // Mobile / native first — they don't coexist with JS bundlers in the\n // detection signals we look at.\n if (signals.hasPubspec) return 'flutter';\n if (signals.hasXcodeProject || signals.hasPodfile || signals.hasSwiftPackage)\n return 'ios';\n if (signals.hasGradle) return 'android';\n\n if (signals.packageJsons.length > 0) {\n // Union all deps across package.json files (covers monorepos)\n const allDeps = new Set<string>();\n for (const pkg of signals.packageJsons) {\n for (const dep of pkg.deps) allDeps.add(dep);\n }\n return pickJsVariant(allDeps);\n }\n\n return null;\n}\n\nfunction hasPostHogSdk(signals: ProjectSignals): boolean {\n for (const pkg of signals.packageJsons) {\n for (const sdk of POSTHOG_SDKS) {\n if (pkg.deps.has(sdk)) return true;\n }\n }\n // For native platforms the PostHog SDK lives outside package.json and\n // is detected by the agent during the skill run. Assume present here.\n return (\n signals.hasXcodeProject ||\n signals.hasPodfile ||\n signals.hasSwiftPackage ||\n signals.hasGradle ||\n signals.hasPubspec\n );\n}\n\n// ── Entry point ──────────────────────────────────────────────────────\n\nexport const SOURCE_MAPS_CONTEXT_KEYS = {\n skillVariant: 'sourceMapsSkillVariant',\n displayName: 'sourceMapsDisplayName',\n packagePaths: 'sourceMapsPackagePaths',\n detectError: 'detectError',\n // Set by the agentic picker once the user chooses a project to instrument.\n selectedVariant: 'sourceMapsSelectedVariant',\n selectedDisplayName: 'sourceMapsSelectedDisplayName',\n selectedPath: 'sourceMapsSelectedPath',\n} as const;\n\n/**\n * Scan `session.installDir` for platform / build-system signals. Writes\n * detection results into frameworkContext via the callback — either the\n * picked skill variant + display name, or a `SourceMapsDetectError`.\n *\n * The skill install happens later in the agent run, not here. This step\n * only picks which variant the prompt should ask the agent to load.\n */\nexport function detectSourceMapsPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: SourceMapsDetectError) =>\n setFrameworkContext(SOURCE_MAPS_CONTEXT_KEYS.detectError, error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n const signals = collectSignals(installDir);\n const variant = selectVariant(signals);\n\n // The legacy filesystem detector does not automate native platforms. The\n // live source-map picker uses detectSourceMapsProjects instead.\n if (\n variant &&\n ['react-native', 'flutter', 'ios', 'android'].includes(variant)\n ) {\n fail({ kind: 'unsupported-platform', detected: variant });\n return;\n }\n\n if (!variant) {\n if (signals.scannedFileCount === 0) {\n fail({ kind: 'no-project-files' });\n } else {\n fail({ kind: 'unsupported-platform', detected: 'unknown' });\n }\n return;\n }\n\n if (!hasPostHogSdk(signals)) {\n fail({ kind: 'no-posthog-sdk', platform: variant });\n return;\n }\n\n setFrameworkContext(SOURCE_MAPS_CONTEXT_KEYS.skillVariant, variant);\n setFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.displayName,\n DISPLAY_NAME[variant],\n );\n setFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.packagePaths,\n signals.packageJsons.map((p) => p.path),\n );\n}\n\nexport { DISPLAY_NAME as VARIANT_DISPLAY_NAME };\n","/**\n * Error tracking source maps upload program step list.\n *\n * Flow: a static intro (no detection yet) → login → an agentic detect+pick\n * screen that scans the repo on Haiku and lets the user choose a project →\n * agent run → outro. Detection runs after auth because the detection agent\n * needs credentials.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { RunPhase } from '@lib/wizard-session';\nimport { SOURCE_MAPS_CONTEXT_KEYS } from './detect.js';\n\nfunction projectSelected(session: WizardSession): boolean {\n return (\n session.frameworkContext[SOURCE_MAPS_CONTEXT_KEYS.selectedVariant] != null\n );\n}\n\nexport const ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM: ProgramStep[] = [\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'source-maps-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n id: 'detect',\n label: 'Detecting projects',\n // The Haiku agent scans the repo, surfaces an instrumentable / not-yet map,\n // and the user picks the project to wire up. Advances once a project is\n // chosen (its variant is written to frameworkContext). The gate lets the\n // agent runner park after auth until the pick lands, so the run prompt sees\n // the chosen variant.\n screenId: 'source-maps-detect',\n isComplete: projectSelected,\n gate: projectSelected,\n },\n {\n id: 'run',\n label: 'Upload source maps',\n screenId: 'run',\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'source-maps-outro',\n isComplete: (session) => session.outroDismissed,\n },\n {\n id: 'skills',\n label: 'Skills',\n screenId: 'keep-skills',\n },\n];\n","import { AgentSignals } from '@agent';\nimport type { SkillVariant } from './detect.js';\n\nexport type SourceMapsUploadPromptParams = {\n displayName: string | undefined;\n variant: SkillVariant;\n skillId: string;\n /** Project to instrument, relative to the wizard working directory (\".\" or undefined = root). */\n projectPath?: string;\n projectId: number;\n host: string;\n settingsUrl: string;\n uiHost: string;\n /** Hand-off report the agent writes in STEP 9; the outro points the user at it. */\n reportFile: string;\n};\n\nexport const SOURCE_MAPS_DETECTION_FAILED_PROMPT = `Detection did not pick a source maps skill variant for this project.\nEmit: ${AgentSignals.ABORT} unsupported-platform\nThen halt.`;\n\nexport function buildSourceMapsUploadPrompt(\n params: SourceMapsUploadPromptParams,\n): string {\n const {\n displayName,\n variant,\n skillId,\n projectPath,\n projectId,\n host,\n settingsUrl,\n uiHost,\n reportFile,\n } = params;\n const platformLabel = displayName ?? variant;\n const inSubproject = projectPath != null && projectPath !== '.';\n const projectLine = inSubproject\n ? `- Project directory (relative to the wizard's working directory): ${projectPath}`\n : \"- Project directory: the wizard's working directory (even when it sits inside a larger git repo, this directory is the project root)\";\n const envFilePathGuidance = inSubproject\n ? `Tool filePaths are relative to the wizard's working directory, not the selected project directory. Treat the skill's env path as relative to the selected project and prefix it with \\`${projectPath}/\\` when calling the tools: for example, pass \\`${projectPath}/.env\\`, not \\`.env\\` (which would target the wizard's working directory).${\n variant === 'rust'\n ? ` Cargo workspace exception: when the skill places the env file at the workspace root instead, pass the path of the ROOT manifest's directory relative to the wizard's working directory (e.g. \\`rust/.env\\` for a workspace root at \\`rust/\\`, or plain \\`.env\\` when the workspace root IS the working directory) — not the member path.`\n : ''\n }`\n : `Tool filePaths are relative to the wizard's working directory. For an env file at this project root, pass \\`.env\\`; never prefix it with this directory's path inside an ancestor repository.`;\n\n const credentialSteps = `STEP 4 — Make the credentials readable at build time. (skill: \"Make credentials available at build time\")\n Follow the skill's step. Wizard-specific: if it calls for a loader (e.g.\n \\`dotenv\\`), install it SILENTLY — do NOT ask the user or call wizard_ask.\n Skip this step entirely if the platform already auto-loads .env.\n\nSTEP 5 — Write the credentials to the env file. (skill: \"Write credentials to the env file\")\n Use the wizard-tools MCP server. Reuse the env file the skill tells you to\n pick — the prerequisite PostHog integration usually already wrote\n POSTHOG_* vars to one, so seed your keys alongside them.\n - First call check_env_keys with the key names and that file as filePath.\n It answers { status: \"present\" | \"missing\", foundIn: [paths] } per key —\n names and paths only, never values, so don't read the file directly.\n \"present\" means a real env file sets the key; a key found only in a\n committed template (.env.example and friends) reads as \"missing\",\n because a template documents a key rather than setting it.\n - Env tool path rule: ${envFilePathGuidance}\n - Then call set_env_values, passing the STEP 1 secretRef as a value\n object, not a literal string. Take the three variable NAMES from the\n skill's per-uploader list in \"Write credentials to the env file\" — they\n differ by uploader, and only the shape below is fixed:\n values: {\n \"<key var for the uploader you wired>\": { secretRef: \"<the ref from STEP 1>\" },\n \"<project id var>\": \"${projectId}\",\n \"<host var>\": \"${uiHost}\"\n }\n Write that one set only. Do not also write another uploader's variables\n as a fallback — a credential nothing reads is an unused secret sitting\n in the user's env file.\n The wizard resolves the ref locally before writing, so you never see the\n key value.`;\n\n return `You are wiring up PostHog Error Tracking source map upload for this ${platformLabel} project.\n\nProject context:\n- PostHog Project ID: ${projectId}\n- PostHog Host: ${host}\n- Detected platform: ${platformLabel}\n- Skill to use: ${skillId}\n${projectLine}\n- Personal API keys settings page: ${settingsUrl}\n\nAll file changes, build/run commands, and config edits target the project directory above${\n inSubproject\n ? ` — this is a monorepo, so scope your work to \\`${projectPath}\\` and do not touch other packages`\n : ''\n }.${\n variant === 'rust' && inSubproject\n ? `\nCargo workspace exception: if the selected project is a member of a Cargo\nworkspace (resolve the root with \\`cargo locate-project --workspace\n--message-format plain\\`), the workspace root's Cargo.toml (for \\`[profile.*]\\`\nedits), the workspace-level \\`target/\\` directory, and a \\`.env\\` next to the\nroot manifest are IN SCOPE — treat them as part of the selected project, and\nfollow the skill's workspace guidance for which paths to use. Every other\npackage stays off-limits.`\n : ''\n }\n\nThe skill you install in STEP 2 is the source of truth for the HOW of every\nstep: its \"## Steps\" section has an overview, tips and per-technology\nexamples for each named step, and its reference files carry the exact\nper-framework API. The STEPS below give the order, the conditionals, and the\nwizard-specific mechanics (which MCP tool to call, signals to emit) — read\nthe matching skill step (named in parentheses) before doing the work, and do\nnot invent steps the skill doesn't describe.\n\nFollow these steps IN ORDER. Do not skip or reorder.\n\nYour FIRST message must contain ONLY parallel tool calls, in this order:\n - the STEP 1 wizard_ask call FIRST — tool calls execute as they stream,\n so this puts the API-key prompt in front of the user within seconds —\n then\n - one TaskCreate call PER task below, all in this same message (the tool\n takes a single task per call). The TUI shows only the subject, so keep\n every description to a few words — never a sentence.\nDo not read files, explore the project, or write any text first, and keep\nany thinking before the calls to a single short sentence.\n\nUse exactly these tasks, in this order — do not collapse, rename, or omit\nany of them. Getting the API key is NOT a task — its prompt is already on\nscreen by the time the list renders:\n 1. Install source maps skill\n 2. Apply build-config changes (per skill)\n 3. Make credentials readable at build time\n 4. Write keys to .env\n 5. Identify build & run commands\n 6. Set up CI for auto-upload\n 7. Test the local setup\n 8. Summarise & hand off\nDrive the list with TaskUpdate — mark a task in_progress when you start it\nand completed when done. ALWAYS keep task 7 (\"Test the local setup\") in the\nlist even if the user declines it in STEP 8: mark it completed rather than\ndeleting it, so the user can see it was offered.\n\nSTEP 1 — Get a personal API key from the user. (skill: \"Get a personal API key\")\n This wizard_ask call ships in your FIRST message, per the rule above.\n The wizard cannot mint keys — never call the PostHog API or any tool to\n create one. Ask the user with the wizard_ask MCP tool; you receive the\n answer as a vaulted secretRef (never the raw value), which you reuse in\n STEP 5:\n {\n id: \"api-key\",\n prompt: \"Paste your PostHog personal API key below.\\\\n\\\\nDon't have one yet? Create one here:\\\\n${settingsUrl}\\\\n\\\\nWhen creating the key, choose the 'Source map upload' preset, then come back and paste it here.\",\n kind: \"text\",\n sensitive: true\n }\n Keep the \\\\n line breaks exactly as written — Ink's <Text> renders them\n as separate lines. The answer is { secretRef: \"secret:...\" }.\n If wizard_ask is unavailable (CI / non-interactive), emit\n ${AgentSignals.ABORT} requires-interactive-mode and halt.\n\nSTEP 2 — Install the skill.\n Call install_skill (wizard-tools MCP server) with skillId \"${skillId}\".\n Do NOT run shell commands to install skills. Then read the installed\n SKILL.md and its reference files — they drive STEPS 3-9.\n If install fails, emit ${\n AgentSignals.ERROR_RESOURCE_MISSING\n } skill ${skillId} could not be installed.\n\nSTEP 3 — Apply build-config changes. (skill: \"Apply build-config changes\")\n Make the bundler / build-config changes the skill's step instructs. The\n skill and its reference are the source of truth for this platform.\n\n${credentialSteps}\n\nSTEP 6 — Identify the build AND run commands. (skill: \"Identify the build and run commands\")\n Per the skill, resolve the production BUILD command and the RUN command\n for THIS project (use detect_package_manager for the package manager). Do\n NOT run either yourself — the user runs them. If you cannot identify a\n build command, emit ${AgentSignals.ABORT} build command not found.\n\nSTEP 7 — Set up CI for automatic uploads. (skill: \"Set up CI for automatic uploads\")\n Source maps only upload when the production build runs, so the build's\n CI/CD must carry the same upload credentials you wrote in STEP 5. Do this\n step without asking — there is no opt-in question for it. Follow the\n skill's \"Set up CI for automatic uploads\" step — it is the source of\n truth for tracing where the production build runs and wiring the\n credentials through every layer, whatever the CI provider.\n Wizard-specific rules on top:\n - Trace the deploy path by reading the project's files — do NOT ask the\n user, and do NOT invent config that isn't there.\n - Carry every manual follow-up the skill has you hand off (secrets the\n user must create, an untraceable build path) into STEP 9.\n\nSTEP 8 — Offer to test the local setup. (skill: \"Test the local setup\")\n Call wizard_ask:\n {\n id: \"test-affordance\",\n prompt: \"Want me to help you test your local setup? I'll add a temporary test button (or route) to your app so you can confirm errors show up in Error Tracking with readable stack traces after your next build. I'll remove it once you've confirmed it works.\",\n kind: \"single\",\n options: [\n { label: \"Yes, help me test it\", value: \"yes\" },\n { label: \"No, I'll test on my own later\", value: \"no\" }\n ]\n }\n\n If \"no\", skip to STEP 9.\n\n If \"yes\", follow the skill's \"Test the local setup\" step for the\n platform-appropriate affordance, the captureException shape, the\n placement, and the read-before-edit / always-revert rules. Then pause for\n the user with wizard_ask, baking the build-and-run flow (and the exact\n button label / route) into the prompt as literal, copy-pasteable\n numbered steps:\n - Steps 1-2: the production build, then launching the app and\n triggering the test affordance. Source them from STEP 6 and the\n skill's \"Test the local setup\" step — when the skill gives the\n platform's test flow as verbatim steps (IDE-driven platforms like\n Xcode do), use its wording and do NOT invent CLI build commands,\n debugger steps, or relaunch steps it doesn't state. Quote CLI\n commands verbatim. When build and run are one action (an IDE Run),\n fold them into step 1 and make step 2 just triggering the affordance.\n - The last step is always the Error Tracking check, exactly as in the\n template.\n Separate each numbered step with \\\\n\\\\n so the TUI renders them as\n distinct lines:\n {\n id: \"test-done\",\n prompt: \"1) <production build step — it uploads source maps and builds the app with the test affordance>\\\\n\\\\n2) <run step>, then click the \\\\\"<your test button label>\\\\\" button (or hit \\`<your test route>\\`).\\\\n\\\\n3) Open Error Tracking in PostHog (${uiHost}/project/${projectId}/error_tracking) and confirm the test error appears with a source-resolved stack trace pointing at real source files (not minified bundle paths).\\\\n\\\\nWhen you're done, select Continue and I'll revert the test code.\",\n kind: \"single\",\n options: [{ label: \"Continue (revert test code)\", value: \"continue\" }]\n }\n After the user continues, revert the test code per the skill's rules and\n surface any failure in STEP 9.\n\nSTEP 9 — Summarise and hand off. (skill: \"Verify and hand off\")\n Follow the skill's \"Verify and hand off\" step. Write the hand-off to\n \\`${reportFile}\\` at the WIZARD'S WORKING DIRECTORY — pass exactly\n \\`${reportFile}\\` as the file path, never prefixed with the selected\n project directory; this file is the one exception to the project-scope\n rule above. Cover: the files you changed (paths only), the exact build\n and upload commands, every CI secret the user still has to create, and\n how to verify the upload — then give the same summary in chat. Never\n write secret values into the report, only variable names. The success\n screen points the user at this file, so do not skip it. The Symbol sets page for this project — where the user\n confirms the upload landed — is:\n ${uiHost}/project/${projectId}/error_tracking/configuration\n`;\n}\n","/**\n * Source-maps learn-deck — the narrative played in the run screen's left\n * pane (LearnCard) while the agent wires source-map upload into the build.\n *\n * It educates the user on what source maps are and why uploading them\n * matters, built around a before/after stack-trace contrast: a minified\n * production trace nobody can read, then the same trace resolved back to\n * real source. Program-owned; wired onto the program's getContentBlocks.\n *\n * Lines stay narrow (~36 cols) because this renders in the left half of a\n * split pane — see LearnCard's paneWidth math.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport {\n isClearBlock,\n type ContentBlock,\n} from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\n\n/**\n * Per-slide dwell multiplier. Each block stays on screen for `pause * SLIDE_PACE`\n * ms after it finishes animating, before the deck advances. Bump this single\n * knob to give every slide more reading time. Clear (page-break) blocks are\n * left untouched so the blank gap between slides stays snappy.\n */\nconst SLIDE_PACE = 1.5;\n\nconst withPace = (block: ContentBlock): ContentBlock => {\n if (typeof block === 'string' || isClearBlock(block) || block.pause == null) {\n return block;\n }\n return { ...block, pause: Math.round(block.pause * SLIDE_PACE) };\n};\n\n/** Apply the dwell multiplier to every block in a deck. */\nconst pace = (blocks: ContentBlock[]): ContentBlock[] => blocks.map(withPace);\n\n/**\n * A minified production stack trace — the problem source maps solve. Framed as\n * a labelled, muted example (no error-red ✘) so a glance reads it as\n * illustrative content, not as the wizard itself having errored mid-run.\n */\nconst MINIFIED_TRACE: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 7000,\n lines: [\n <Text dimColor>{'example — minified production trace'}</Text>,\n <Text color={Colors.muted}>{' TypeError: cart is undefined'}</Text>,\n <Text dimColor>{' at t.min.js:1:48213'}</Text>,\n <Text dimColor>{' at t.min.js:1:9402'}</Text>,\n <Text dimColor>{' at t.min.js:1:71150'}</Text>,\n ],\n};\n\n/** The same trace, resolved through uploaded source maps. */\nconst RESOLVED_TRACE: ContentBlock = {\n type: 'lines',\n interval: 400,\n pause: 8000,\n lines: [\n <Text dimColor>{'example — resolved with source maps'}</Text>,\n <Text color={Colors.success}>{' ✔ TypeError: cart is undefined'}</Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">Cart.tsx:42</Text>\n <Text dimColor>{' loadCart'}</Text>\n </Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">App.tsx:88</Text>\n <Text dimColor>{' render'}</Text>\n </Text>,\n <Text>\n <Text dimColor>{' at '}</Text>\n <Text color=\"cyan\">index.tsx:5</Text>\n <Text dimColor>{' main'}</Text>\n </Text>,\n ],\n};\n\n/**\n * How a bundle is tied to its map: PostHog injects a chunk-ID marker into the\n * built JS and stamps the matching source map with the same ID.\n */\nconst CHUNK_ID_LINK: ContentBlock = {\n type: 'lines',\n interval: 450,\n pause: 7500,\n lines: [\n <Text dimColor>app.min.js</Text>,\n <Text dimColor>{' …minified code…'}</Text>,\n <Text>\n <Text color=\"cyan\">{' //# chunkId=a1b2c3d4'}</Text>\n <Text dimColor>{' ← injected'}</Text>\n </Text>,\n <Text dimColor>{' ↕ matched by id'}</Text>,\n <Text>\n <Text dimColor>app.min.js.map</Text>\n <Text dimColor>{' ← uploaded'}</Text>\n </Text>,\n ],\n};\n\n/** Many similar exceptions collapse into a single issue. */\nconst GROUPING: ContentBlock = {\n type: 'lines',\n interval: 450,\n pause: 7000,\n lines: [\n <Text dimColor>{'exception ─┐'}</Text>,\n <Text>\n <Text dimColor>{'exception ─┼──→ '}</Text>\n <Text color={Colors.accent} bold>\n 1 issue\n </Text>\n </Text>,\n <Text dimColor>{'exception ─┘'}</Text>,\n ],\n};\n\n/** The two lines that say what the run wires. The rest of the deck is shared. */\nexport type SourceMapsDeckCopy = { intro: string; wiring: string };\n\n/** The source-maps narrative. The error-tracking program reuses it with its own copy. */\nexport const buildSourceMapsDeck = (\n store: WizardStore | undefined,\n copy: SourceMapsDeckCopy,\n): ContentBlock[] =>\n pace([\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n {\n content: copy.intro,\n pause: 5000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'When you ship to production, your code gets minified.',\n pause: 5000,\n },\n {\n content: 'Thousands of readable lines collapse into one dense bundle.',\n pause: 5000,\n },\n {\n content: 'So a thrown error gives you a stack trace like this:',\n pause: 2000,\n },\n\n MINIFIED_TRACE,\n\n { content: 'Just offsets into a file no human can read.', pause: 5000 },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'Source maps are the key.', pause: 3500 },\n {\n content:\n 'They map every position in that bundle back to your original source — the real file, line, and function.',\n pause: 6000,\n },\n {\n content: copy.wiring,\n pause: 6000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'But how does PostHog know which map belongs to which build?',\n pause: 4500,\n },\n {\n content:\n 'During the build, it injects a unique chunk ID into each bundle:',\n pause: 2500,\n },\n\n CHUNK_ID_LINK,\n\n {\n content:\n 'The matching source map is stamped with that same ID before it ships to PostHog.',\n pause: 6000,\n },\n {\n content:\n 'When an error comes in, PostHog reads the chunk ID off the bundle, finds the map with the exact same ID, and uses it to map each frame back to your source — even for a release you shipped weeks ago.',\n pause: 8000,\n },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'So that same error becomes:', pause: 2000 },\n\n RESOLVED_TRACE,\n\n {\n content: 'Readable stack traces, straight from production.',\n pause: 5000,\n },\n {\n content: 'You debug a live error like it happened on your own machine.',\n pause: 6000,\n },\n\n { type: 'clear', pause: 1500 },\n\n { content: 'Zooming out — this is how Error Tracking works.', pause: 4000 },\n {\n content: 'Every error your app throws is captured as an exception.',\n pause: 5000,\n },\n {\n content: 'PostHog groups similar exceptions into a single issue:',\n pause: 2500,\n },\n\n GROUPING,\n\n {\n content:\n 'So a bug that fires ten thousand times is one issue to triage — not ten thousand alerts.',\n pause: 6500,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n content: 'And this is where source maps earn their keep again.',\n pause: 4500,\n },\n {\n content:\n 'Grouping reads the stack trace. Minified frames all look alike — so unrelated crashes get merged, and one real bug scatters across many issues.',\n pause: 8000,\n },\n {\n content:\n 'With source maps, PostHog groups on your real frames — so each distinct bug lands as one clean issue.',\n pause: 7000,\n },\n\n { type: 'clear', pause: 1500 },\n\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n {\n pause: 90000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to follow along — or sit tight, I'll let you know when it's done.\n </Text>\n ),\n },\n ]);\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] =>\n buildSourceMapsDeck(store, {\n intro: \"I'm wiring PostHog Error Tracking into your build.\",\n wiring:\n \"Right now I'm hooking source-map generation and upload into your build, tied to each release you ship.\",\n });\n","import { spawnSync } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { debug } from '@utils/debug';\n\n/**\n * A coding agent whose global instructions file the PostHog CLI steering\n * snippet can be installed into. Project-level installs are handled by\n * `posthog-cli api agents-md install` in the target repo; this step only\n * manages the per-user (global) instruction files.\n */\nexport interface CliSteeringTarget {\n id: string;\n name: string;\n /** Global agent instructions file the steering snippet is written to. */\n instructionsPath(): string;\n /** Whether the agent appears to be installed on this machine. */\n isDetected(): boolean;\n}\n\nconst dirExists = (...segments: string[]): boolean => {\n try {\n return fs.existsSync(path.join(os.homedir(), ...segments));\n } catch {\n return false;\n }\n};\n\nexport const CLI_STEERING_TARGETS: readonly CliSteeringTarget[] = [\n {\n id: 'claude-code',\n name: 'Claude Code',\n instructionsPath: () => path.join(os.homedir(), '.claude', 'CLAUDE.md'),\n isDetected: () => dirExists('.claude'),\n },\n {\n id: 'codex',\n name: 'Codex',\n instructionsPath: () => path.join(os.homedir(), '.codex', 'AGENTS.md'),\n isDetected: () => dirExists('.codex'),\n },\n {\n id: 'gemini',\n name: 'Gemini CLI',\n instructionsPath: () => path.join(os.homedir(), '.gemini', 'GEMINI.md'),\n isDetected: () => dirExists('.gemini'),\n },\n];\n\nexport function findTarget(id: string): CliSteeringTarget | undefined {\n return CLI_STEERING_TARGETS.find((target) => target.id === id);\n}\n\nexport function detectTargets(): CliSteeringTarget[] {\n return CLI_STEERING_TARGETS.filter((target) => target.isDetected());\n}\n\nexport interface SteeringInstallResult {\n success: boolean;\n filePath: string;\n error?: string;\n}\n\nexport interface CliInstallResult {\n success: boolean;\n error?: string;\n /**\n * The underlying failure as an Error, for callers that report to error\n * tracking. Carries the real spawn error (with its stack) when npm couldn't\n * launch; a synthesized Error for a non-zero exit. Set whenever success is\n * false.\n */\n errorObject?: Error;\n}\n\nconst spawnOptions = {\n encoding: 'utf-8' as const,\n // npm/posthog-cli are npm.cmd/posthog-cli.cmd on Windows; spawnSync only\n // resolves them through a shell.\n shell: process.platform === 'win32',\n};\n\n/**\n * Install or update the PostHog CLI in the user's environment. `npm install\n * --global @posthog/cli@latest` covers both first-time installs and upgrades\n * for existing npm-installed CLIs.\n */\nexport function installOrUpdatePostHogCli(): CliInstallResult {\n const args = ['install', '--global', '@posthog/cli@latest'];\n debug(`Running npm ${args.join(' ')}`);\n\n const result = spawnSync('npm', args, spawnOptions);\n\n if (result.error) {\n return {\n success: false,\n error: `Failed to run npm: ${result.error.message}. Is Node.js installed?`,\n errorObject: result.error,\n };\n }\n if (result.status !== 0) {\n const detail = (result.stderr || result.stdout || '').trim();\n const message =\n detail ||\n `npm install --global @posthog/cli@latest exited with status ${\n result.status ?? 'unknown'\n }`;\n return {\n success: false,\n error: message,\n errorObject: new Error(message),\n };\n }\n return { success: true };\n}\n\n/**\n * Delegate the actual write to the installed `posthog-cli api agents-md\n * install`. The steering snippet lives in the CLI (its single source of truth),\n * so the command should run only after `installOrUpdatePostHogCli` refreshes\n * the CLI to the latest release.\n */\nexport function installSteeringSnippet(\n filePath: string,\n): SteeringInstallResult {\n const args = ['api', 'agents-md', 'install', '--path', filePath];\n debug(`Running posthog-cli ${args.join(' ')}`);\n\n const result = spawnSync('posthog-cli', args, {\n ...spawnOptions,\n env: { ...process.env, POSTHOG_CLI_EXPERIMENTAL_API: '1' },\n });\n\n if (result.error) {\n return {\n success: false,\n filePath,\n error: `Failed to run posthog-cli: ${result.error.message}. Make sure npm's global bin directory is on your PATH.`,\n };\n }\n if (result.status !== 0) {\n const detail = (result.stderr || result.stdout || '').trim();\n return {\n success: false,\n filePath,\n error:\n detail ||\n `posthog-cli exited with status ${result.status ?? 'unknown'}`,\n };\n }\n return { success: true, filePath };\n}\n","/**\n * Machine-global posthog-cli pre-install, shared by the programs whose symbol\n * upload shells out to it (error-tracking and error-tracking-upload-source-maps).\n *\n * The agent cannot install a global package — warlock blocks `npm install -g`\n * — so the wizard does it in-process, at most once per process. Warn, don't\n * fail: the run still instruments capture, and only the release build's upload\n * needs the CLI.\n */\n\nimport type { RunnerContext } from '@programs/runner-context';\nimport { installOrUpdatePostHogCli } from '@steps/install-cli-steering';\nimport { analytics } from '@utils/analytics';\n\nlet attempted = false;\n\nexport function preinstallPostHogCliOnce(\n failureEvent: string,\n properties: Record<string, string>,\n log: RunnerContext['log'],\n): void {\n if (attempted) return;\n attempted = true;\n\n const result = installOrUpdatePostHogCli();\n if (result.success) return;\n\n // No npm, or no permission for a global install, is the user's environment,\n // not a wizard bug: an event, never captureException, which would mint an\n // error-tracking issue per machine.\n analytics.wizardCapture(failureEvent, {\n ...properties,\n error: String(result.error).slice(0, 500),\n });\n log.warn(\n `Could not pre-install posthog-cli (${result.error}). Your release build ` +\n `will fail to upload debug symbols until it's installed: npm install -g @posthog/cli@latest`,\n );\n}\n\n/** Test seam: forget that the install was attempted. */\nexport function resetPostHogCliPreinstallForTests(): void {\n attempted = false;\n}\n","import type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { OutroKind } from '@lib/wizard-session';\nimport type { RunnerContext } from '@programs/runner-context';\nimport { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';\nimport {\n buildSourceMapsUploadPrompt,\n SOURCE_MAPS_DETECTION_FAILED_PROMPT,\n} from './prompt.js';\nimport {\n SOURCE_MAPS_ABORT_CASES,\n SOURCE_MAPS_CONTEXT_KEYS,\n VARIANTS_REQUIRING_POSTHOG_CLI,\n type SkillVariant,\n} from './detect.js';\nimport { getContentBlocks } from '../../ui/tui/decks/error-tracking-upload-source-maps/index.js';\nimport { preinstallPostHogCliOnce } from '@programs/shared/posthog-cli-preinstall';\n\nconst REPORT_FILE = 'posthog-source-maps-report.md';\nconst DOCS_URL = 'https://posthog.com/docs/error-tracking/upload-source-maps';\n\n/**\n * Pre-install posthog-cli for variants that need a machine-global copy\n * (`VARIANTS_REQUIRING_POSTHOG_CLI`). See `preinstallPostHogCliOnce` for the\n * once-per-process guard and the warn-don't-fail handling.\n */\nfunction ensurePostHogCli(\n variant: SkillVariant,\n log: RunnerContext['log'],\n): void {\n preinstallPostHogCliOnce(\n 'source maps posthog-cli preinstall failed',\n { variant },\n log,\n );\n}\n\nexport const errorTrackingUploadSourceMapsConfig: ProgramConfig = {\n command: 'upload-source-maps',\n description: 'Upload source maps to PostHog Error Tracking',\n id: 'error-tracking-upload-source-maps',\n requiresAi: true,\n steps: ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM,\n reportFile: REPORT_FILE,\n getContentBlocks,\n requires: ['posthog-integration'],\n\n run: (\n _session: WizardSession,\n runner: RunnerContext,\n ): Promise<ProgramRun> => {\n // Read the picked project LIVE at prompt-build time, not here: the picker\n // screen runs AFTER this run config is resolved (post-auth), and the store\n // forks the session reference, so the `session` passed in never sees the\n // choice. The runner reads the live store session.\n const readSelection = () => {\n const variant = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedVariant,\n ) as SkillVariant | undefined;\n const displayName = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedDisplayName,\n ) as string | undefined;\n const projectPath = runner.getFrameworkContext(\n SOURCE_MAPS_CONTEXT_KEYS.selectedPath,\n ) as string | undefined;\n const skillId = variant\n ? `error-tracking-upload-source-maps-${variant}`\n : undefined;\n return { variant, displayName, projectPath, skillId };\n };\n\n return Promise.resolve({\n integrationLabel: 'error-tracking-upload-source-maps',\n // Skill is installed by the agent (after the API-key choice is made)\n // rather than pre-installed by the runner, so leave skillId unset.\n successMessage: 'Source maps wired up!',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Wiring up source maps...',\n estimatedDurationMinutes: 3,\n abortCases: SOURCE_MAPS_ABORT_CASES,\n // The flow parks on wizard_ask while the user does slow work — create\n // a personal API key in the browser (STEP 1), or run a production\n // build, trigger the test error, and check Error Tracking (STEP 8).\n // The 5-minute default cancels the question mid-task and the agent\n // wraps up to the outro, so give these answers half an hour.\n askTimeoutMs: 30 * 60 * 1000,\n\n customPrompt: (ctx) => {\n const { variant, displayName, projectPath, skillId } = readSelection();\n if (!skillId || !variant) {\n // No project was selected — abort with a structured signal so the\n // runner renders a friendly outro.\n return SOURCE_MAPS_DETECTION_FAILED_PROMPT;\n }\n\n if (VARIANTS_REQUIRING_POSTHOG_CLI.has(variant))\n ensurePostHogCli(variant, runner.log);\n\n const uiHost = ctx.host.appHost.replace(/\\/$/, '');\n\n return buildSourceMapsUploadPrompt({\n displayName,\n variant,\n skillId,\n projectPath,\n projectId: ctx.projectId,\n host: ctx.host.apiHost,\n settingsUrl: `${uiHost}/project/${ctx.projectId}/settings/user-api-keys`,\n uiHost,\n reportFile: REPORT_FILE,\n });\n },\n\n postRun: () => {\n // Stash a hint for the outro about what variant we shipped.\n const { variant } = readSelection();\n if (variant) {\n runner.setFrameworkContext('sourceMapsCompletedVariant', variant);\n }\n return Promise.resolve();\n },\n\n buildOutroData: () => {\n // SourceMapsOutroScreen renders static \"what we did + how it works\"\n // guidance, so no per-run `changes` list is needed here.\n return {\n kind: OutroKind.Success as const,\n message: 'Source maps wired up!',\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n };\n },\n });\n },\n};\n\nexport { ERROR_TRACKING_UPLOAD_SOURCE_MAPS_PROGRAM } from './steps.js';\nexport {\n detectSourceMapsPrerequisites,\n SOURCE_MAPS_ABORT_CASES,\n SOURCE_MAPS_CONTEXT_KEYS,\n VARIANT_DISPLAY_NAME,\n MANUAL_SDK_VARIANTS,\n type SkillVariant,\n type SourceMapsDetectError,\n} from './detect.js';\n","/** Error-tracking learn-deck: the source-maps narrative, worded to also fit platforms that upload nothing. */\n\nimport type { WizardStore } from '@ui/tui/store';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { buildSourceMapsDeck } from '@ui/tui/decks/error-tracking-upload-source-maps/index';\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] =>\n buildSourceMapsDeck(store, {\n intro: \"I'm wiring PostHog Error Tracking into your project.\",\n wiring:\n 'If your platform ships minified code or stripped binaries, I also hook source-map or debug-symbol upload into your build, tied to each release you ship.',\n });\n","import type { AbortCase } from '@agent/types';\nimport { Integration } from '@shared/constants';\nimport {\n detectFramework,\n gatherFrameworkContext,\n} from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport type { CiRunnerContext } from '@programs/runner-context';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport { createSkillProgram } from '@programs/agent-skill/index';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { detectPostHogIntegration } from '@programs/posthog-integration/detect';\nimport type {\n ProgramConfig,\n ProgramReadyContext,\n ProgramStep,\n} from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport { analytics } from '@utils/analytics';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\n\nconst REPLAY_VISION_REPORT_FILE = 'posthog-replay-vision-report.md';\n\n/**\n * The platforms session replay can actually record on. Replay vision watches\n * recordings, so a platform with no recordings has nothing to set up — the\n * run must stop before any work, not after a pointless agent run.\n *\n * Web frameworks record through posthog-js (server-rendered frameworks\n * included — they serve pages), and the mobile SDKs with replay support are\n * React Native, Android, iOS, and Flutter. Excluded: pure backend targets\n * (`javascript_node`, `python`, `ruby`) and KMP, which has no replay support\n * yet.\n */\nexport const REPLAY_VISION_SUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.nextjs,\n Integration.nuxt,\n Integration.vue,\n Integration.reactRouter,\n Integration.tanstackStart,\n Integration.tanstackRouter,\n Integration.angular,\n Integration.astro,\n Integration.sveltekit,\n Integration.javascript_web,\n Integration.django,\n Integration.flask,\n Integration.fastapi,\n Integration.laravel,\n Integration.rails,\n Integration.reactNative,\n Integration.android,\n Integration.swift,\n Integration.flutter,\n]);\n\nasync function abortUnsupportedPlatform(\n integration: Integration,\n): Promise<void> {\n const name = FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration;\n // This is a clean, intentional exit, not a crash. Count it with a normal\n // event keyed on the platform so aborts roll up into one series. Do not hand\n // `wizardAbort` an `error` — that forwards to captureException and mints a\n // new error-tracking issue per install location and per platform.\n analytics.wizardCapture('replay-vision unsupported platform', {\n integration,\n });\n await wizardAbort({\n code: ErrorCodes.DetectUnsupportedPlatform,\n message:\n `Session replay isn't available for ${name} projects, and Replay ` +\n 'vision needs session recordings to watch — so there is nothing to ' +\n 'set up here.\\n\\n' +\n 'If this repo also contains a web or mobile app, run the command from ' +\n 'that project directory instead. See what replay supports at:\\n' +\n ' https://posthog.com/docs/session-replay',\n });\n}\n\n/**\n * `[ABORT]` reasons the replay-vision skill emits when the run can't proceed.\n * Kept in sync with the stop conditions in the skill's `description.md`\n * (context-mill `context/skills/replay-vision`).\n */\nexport const REPLAY_VISION_ABORT_CASES: AbortCase[] = [\n {\n match: /^replay vision not available for this project$/i,\n message: 'Replay vision is not available for this project',\n body:\n 'Every Replay vision scanner endpoint reported that the feature is not ' +\n 'available here yet. Session replay setup done so far is kept. See ' +\n 'https://posthog.com/docs/replay-vision for availability.',\n },\n];\n\n/**\n * Framework detection ahead of the run, exactly like the default integration\n * program. The orchestrator requires it: `session.skillId` must hold the\n * detected framework id before the run arm starts, because the runner\n * resolves the reference integration skill and every task's mini-skill\n * variants (`integration-v2-install`, `integration-v2-init`, …) against it in\n * preflight. Without this step the session would still carry the program's\n * own skill id and preflight would abort.\n */\nconst DETECT_STEP: ProgramStep = {\n id: 'detect',\n label: 'Detecting framework',\n // The platform gate runs on a direct detectFramework call BEFORE the full\n // detect writes to the store: store setters replace the session with a\n // shallow copy, so `ctx.session` read after detectPostHogIntegration would\n // be the stale pre-copy object (see the warning in detect.ts).\n onReady: async (ctx: ProgramReadyContext) => {\n const integration = await detectFramework(ctx.session.installDir);\n if (integration && !REPLAY_VISION_SUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n await detectPostHogIntegration(ctx);\n },\n};\n\nconst base = createSkillProgram({\n // The menu ids this skill `<dir>-<variant>`, and context-mill's\n // `replay-vision/config.yaml` declares a single variant, `setup`. The bare\n // `replay-vision` id does not exist — the orchestrator never installs this\n // (it resolves per-task mini-skills instead), but the linear path does, and\n // aborts `skill-not-found` on a miss.\n skillId: 'replay-vision-setup',\n command: 'replay-vision',\n id: 'replay-vision',\n description: 'Set up PostHog Replay Vision scanners for your product',\n integrationLabel: 'replay-vision',\n customPrompt:\n 'Set up PostHog Replay vision. Run the `replay-vision` skill end-to-end: ' +\n 'make sure session replay is recording (server-side enable plus a ' +\n 'posthog-js init check), then create the vision scanners the skill ' +\n \"defines, scoped to this product's key flows read out of the repo. If \" +\n 'PostHog is not integrated yet, install and initialize the SDK first as ' +\n 'the skill instructs — do not abort. The final report is written to ' +\n `./${REPLAY_VISION_REPORT_FILE}.`,\n successMessage: `Replay vision configured! View the report at ./${REPLAY_VISION_REPORT_FILE}`,\n reportFile: REPLAY_VISION_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/replay-vision',\n spinnerMessage: 'Setting up Replay vision...',\n estimatedDurationMinutes: 6,\n abortCases: REPLAY_VISION_ABORT_CASES,\n});\n\n/**\n * `wizard replay-vision` — flat skill command on the orchestrator sequence.\n *\n * Makes session replay record (server toggle + client init check), then\n * creates vision scanners scoped to the product's key flows, read out of the\n * repo. The orchestrator runs the `replay-vision` agent flow (context-mill\n * `context/agents/replay-vision`): the seed enqueues the install/init tasks\n * (copied from integration-v2, sharing its step-skills) when the project has\n * no PostHog yet, so the command works on uninstrumented repos instead of\n * aborting.\n *\n * Departures from a plain `createSkillProgram`:\n * - `DETECT_STEP` in front, so `session.skillId` carries the framework id the\n * orchestrator's preflight resolves reference + mini-skill variants with.\n * - `agentFlow` pinned (the id would default to the same value — explicit so\n * renaming the program can't silently detach the flow).\n * - `ciPreRun` mirrors the default integration program: scope the install dir\n * to the right project (monorepos), then detect the framework — the\n * headless equivalent of the detect step's onReady hook.\n */\nexport const replayVisionConfig: ProgramConfig = {\n ...base,\n agentFlow: 'replay-vision',\n steps: [DETECT_STEP, ...AGENT_SKILL_STEPS],\n\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n if (!REPLAY_VISION_SUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n\n const frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.frameworkConfig = frameworkConfig;\n session.skillId = integration;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: true,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n },\n};\n","/** Sidebar tips for the error-tracking run: product features the learn deck does not cover. */\n\nimport type { Tip } from '@ui/tui/components/TipsCard';\nimport { REPLAY_VISION_SUPPORTED } from '@programs/replay-vision/index';\n\nexport const ERROR_TRACKING_TIPS: Tip[] = [\n {\n id: 'session-replay',\n title: 'Watch the crash happen',\n description:\n 'An exception from a recorded session links to its replay. See the clicks that led to the error, not only the stack trace.',\n // Replay records only on platforms with a client SDK.\n visible: (store) => {\n const integration = store.session.integration;\n return integration != null && REPLAY_VISION_SUPPORTED.has(integration);\n },\n },\n {\n id: 'alerts',\n title: 'Know when a bug comes back from the dead',\n description:\n 'Set alerts for new issues, issues that return after you resolve them, and sudden spikes. Hear about them before your users do.',\n },\n {\n id: 'external-issues',\n title: 'From crash to ticket in one click',\n description:\n 'Create a GitHub, GitLab, Jira or Linear issue straight from an error.',\n },\n];\n\nexport const getTips = (): Tip[] => ERROR_TRACKING_TIPS;\n","/**\n * Error-tracking adapter over the shared integration scan: classifies each\n * project for the post-login picker and scopes the run to the picked one.\n *\n * Self-driving classifies the same scan (`toIntegrationCandidates`) with a\n * different rule, so each program keeps its own:\n * - A project that already has PostHog is a setup target here. Self-driving\n * offers it as \"continue with existing\" instead.\n * - KMP is not offered here (see `ERROR_TRACKING_UNSUPPORTED`).\n * - The scan asks for a recommended project, and the picker lists it first.\n * - The scan bills to error-tracking.\n */\n\nimport { Integration } from '@shared/constants';\nimport {\n resolveProjectDir,\n type AgenticDetectionReport,\n type DetectEvent,\n} from '@programs/detection/agentic';\nimport { gatherFrameworkContext } from '@programs/detection/index';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '@programs/detection/project-scope';\nimport type { WizardSession } from '@lib/wizard-session';\n\n/** frameworkContext key for the picked project's path, relative to the repo root. */\nexport const ERROR_TRACKING_PROJECT_PATH_KEY = 'errorTrackingProjectPath';\n\n/** KMP skill variants carry no `framework` tag, so orchestrator preflight aborts every KMP run. */\nexport const ERROR_TRACKING_UNSUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.kmp,\n]);\n\n/** One project, classified for the picker. */\nexport type ErrorTrackingProject = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A wizard framework when the project matches one, else null. */\n integration: Integration | null;\n /** The flow can run here. PostHog does not have to be installed yet. */\n instrumentable: boolean;\n};\n\nexport type ErrorTrackingDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: ErrorTrackingProject[];\n};\n\n/** Classify the scan for the picker, recommended project first (exported for tests). */\nexport function toErrorTrackingReport(\n report: AgenticDetectionReport,\n): ErrorTrackingDetectionReport {\n const candidates = toIntegrationCandidates(report).sort(\n (a, b) => Number(b.recommended) - Number(a.recommended),\n );\n return {\n repoType: report.repoType,\n projects: candidates.map((p) => ({\n path: p.path,\n framework: p.framework,\n integration: p.integration,\n instrumentable:\n p.integration != null && !ERROR_TRACKING_UNSUPPORTED.has(p.integration),\n })),\n };\n}\n\n/** Scan the repo for projects, billed to error tracking. */\nexport async function detectErrorTrackingProjects(\n session: WizardSession,\n onEvent?: DetectEvent,\n): Promise<ErrorTrackingDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'error-tracking',\n recommend: true,\n onEvent,\n });\n return toErrorTrackingReport(report);\n}\n\n/** The run's working directory: the picked project, else the repo root. */\nexport function errorTrackingProjectDir(session: WizardSession): string {\n return resolveProjectDir(\n session.installDir,\n session.frameworkContext[ERROR_TRACKING_PROJECT_PATH_KEY],\n );\n}\n\n/** Gather framework context for `session.installDir`, keeping keys already set. */\nexport async function gatherErrorTrackingContext(\n session: WizardSession,\n): Promise<void> {\n const frameworkConfig = session.frameworkConfig;\n if (!frameworkConfig) return;\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n}\n","import { Integration } from '@shared/constants';\nimport { detectFramework } from '@programs/detection/index';\nimport { scopeInstallDirToProject } from '@programs/detection/project-scope';\nimport { FRAMEWORK_REGISTRY } from '@programs/frameworks/registry';\nimport type { ProgramRun } from '@programs/program-run';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/steps';\nimport { getContentBlocks } from '@ui/tui/decks/error-tracking/index';\nimport { getTips } from '@ui/tui/decks/error-tracking/tips';\nimport {\n ERROR_TRACKING_UNSUPPORTED,\n errorTrackingProjectDir,\n gatherErrorTrackingContext,\n} from '@programs/error-tracking/detect-agentic';\nimport type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { CiRunnerContext, RunnerContext } from '@programs/runner-context';\nimport { preinstallPostHogCliOnce } from '@programs/shared/posthog-cli-preinstall';\nimport { analytics } from '@utils/analytics';\nimport { wizardAbort } from '@utils/wizard-abort';\nimport { ErrorCodes } from '@shared/errors';\n\nconst ERROR_TRACKING_REPORT_FILE = 'posthog-error-tracking-report.md';\nconst ERROR_TRACKING_DOCS_URL = 'https://posthog.com/docs/error-tracking';\n\n/**\n * Frameworks whose symbol upload shells out to a machine-global `posthog-cli`\n * with no npx / local-dep fallback. The wizard pre-installs the CLI for them\n * because warlock blocks the agent's `npm install -g`. Mirrors\n * `VARIANTS_REQUIRING_POSTHOG_CLI` in the source-maps program, but keyed by\n * wizard `Integration` because here the framework is known before the flow's\n * seed picks an uploader variant (`swift` maps to the `ios` uploader).\n */\nexport const SYMBOL_UPLOAD_CLI_FRAMEWORKS: ReadonlySet<Integration> = new Set([\n Integration.swift,\n Integration.android,\n Integration.reactNative,\n Integration.flutter,\n Integration.go,\n Integration.rust,\n]);\n\nasync function abortUnsupportedPlatform(\n integration: Integration,\n): Promise<void> {\n const name = FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration;\n // A clean exit, not a crash: an event, never an `error` for captureException.\n analytics.wizardCapture('error tracking unsupported platform', {\n integration,\n });\n await wizardAbort({\n code: ErrorCodes.DetectUnsupportedPlatform,\n message:\n `The wizard cannot set up error tracking for ${name} projects yet.\\n\\n` +\n `Set it up manually:\\n ${ERROR_TRACKING_DOCS_URL}`,\n });\n}\n\n/**\n * Pre-install posthog-cli when the detected framework's symbol upload will\n * shell out to it. See `preinstallPostHogCliOnce` for the once-per-process\n * guard and the warn-don't-fail handling.\n */\nfunction maybePreinstallPostHogCli(\n integration: Integration | null,\n log: RunnerContext['log'],\n): void {\n if (!integration || !SYMBOL_UPLOAD_CLI_FRAMEWORKS.has(integration)) return;\n preinstallPostHogCliOnce(\n 'error tracking posthog-cli preinstall failed',\n { integration },\n log,\n );\n}\n\n/**\n * After login, the scan lists the repo's projects and the user picks one, as in\n * the legacy upload-source-maps program. The pick sets the framework preflight\n * resolves task skills against, and the project path the run is scoped to.\n */\nconst PICK_PROJECT_STEP: ProgramStep = {\n id: 'detect',\n label: 'Detecting projects',\n screenId: 'error-tracking-detect',\n isComplete: (session) => session.integration != null,\n};\n\nconst ERROR_TRACKING_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.flatMap(\n (step): ProgramStep[] => {\n if (step.id === 'intro') {\n return [{ ...step, screenId: 'error-tracking-intro' }];\n }\n if (step.id === 'auth') return [step, PICK_PROJECT_STEP];\n if (step.id === 'run') {\n // targetDir makes run-wizard walk the steps and run in the picked project.\n return [\n {\n ...step,\n targetDir: errorTrackingProjectDir,\n onRunPrep: gatherErrorTrackingContext,\n },\n ];\n }\n return [step];\n },\n);\n\n/**\n * Run instructions for a linear override (`--sequence=linear`), the only\n * sequence that reads `customPrompt`. The orchestrator runs the flow's own\n * prompts, so these spell out the skill-menu lookups its tasks perform.\n */\nconst ERROR_TRACKING_PROMPT = `Set up PostHog error tracking end-to-end:\n\n1. If PostHog is not integrated yet, install and initialize the SDK first —\n do not abort. Pick the matching variant from the skill menu's\n \"integration-v2/install\" and \"integration-v2/init\" categories.\n\n2. Wire up exception capture: install the \"error-tracking\" skill variant that\n matches this project's platform (\\`load_skill_menu\\` with\n \\`category: \"error-tracking\"\\`) and follow it. Set capture up in one place —\n the SDK's own mechanism, never manual capture calls sprinkled across files.\n\n3. When the platform ships minified bundles or stripped binaries (browser JS,\n React Native, iOS, Android, Flutter, Go, Rust), wire up source-map /\n debug-symbol upload too: install the matching\n \"error-tracking-upload-source-maps\" skill variant and follow it, including\n credentials and CI. Skip this step on platforms with readable stack traces\n (plain Python, Ruby, PHP, Elixir, JVM servers).\n\nThe final report is written to ./${ERROR_TRACKING_REPORT_FILE}.`;\n\nconst ERROR_TRACKING_RUN: ProgramRun = {\n integrationLabel: 'error-tracking',\n customPrompt: () => ERROR_TRACKING_PROMPT,\n successMessage: `Error tracking configured! View the report at ./${ERROR_TRACKING_REPORT_FILE}`,\n reportFile: ERROR_TRACKING_REPORT_FILE,\n docsUrl: ERROR_TRACKING_DOCS_URL,\n spinnerMessage: 'Setting up error tracking...',\n estimatedDurationMinutes: 8,\n // The flow can park on wizard_ask while the user does slow work (mint a\n // personal API key in the browser, run a build and trigger the test\n // error). The orchestrator caps per-task asks itself; this covers the\n // linear fallback.\n askTimeoutMs: 30 * 60 * 1000,\n};\n\n/**\n * `wizard error-tracking` — flat command on the orchestrator sequence.\n *\n * Makes uncaught errors reach PostHog with readable stack traces. The\n * orchestrator runs the `error-tracking` agent flow (context-mill\n * `context/agents/error-tracking`): the seed enqueues the install/init tasks\n * (sharing integration-v2's step-skills, like replay-vision) when the project\n * has no PostHog yet, then exception capture, then — when the platform needs\n * it — the source-map subgraph adapted from the standalone\n * `upload-source-maps` flow.\n *\n * Departures from a plain `createSkillProgram`:\n * - No `run.skillId`: the flow's tasks resolve per-framework mini-skills\n * themselves (there is no bare `error-tracking` menu entry), so the intro is\n * a custom screen rather than the generic skill intro.\n * - `PICK_PROJECT_STEP` after auth: the user picks the project, which sets the\n * framework preflight needs and the directory the run is scoped to.\n * - `run` is a function: `runAgent` resolves it after the pick (and after\n * `ciPreRun` headless), so the posthog-cli pre-install, which the agent\n * cannot do (warlock blocks \\`npm install -g\\`), waits for the user.\n * - `agentFlow` pinned (the id would default to the same value — explicit so\n * renaming the program can't silently detach the flow).\n * - `ciPreRun` mirrors replay-vision: scope the install dir to the right\n * project (monorepos), then detect the framework — the headless equivalent\n * of the project picker.\n */\nexport const errorTrackingConfig: ProgramConfig = {\n command: 'error-tracking',\n description: 'Set up PostHog error tracking, source-map upload included',\n id: 'error-tracking',\n agentFlow: 'error-tracking',\n steps: ERROR_TRACKING_STEPS,\n reportFile: ERROR_TRACKING_REPORT_FILE,\n getContentBlocks,\n getTips,\n\n run: (session: WizardSession, runner: RunnerContext): Promise<ProgramRun> => {\n maybePreinstallPostHogCli(session.integration, runner.log);\n return Promise.resolve(ERROR_TRACKING_RUN);\n },\n\n ciPreRun: async (\n session: WizardSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n await wizardAbort({\n code: ErrorCodes.DetectNoFramework,\n message: 'Could not auto-detect your framework for this project.',\n });\n return;\n }\n if (ERROR_TRACKING_UNSUPPORTED.has(integration)) {\n await abortUnsupportedPlatform(integration);\n return;\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n session.frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.skillId = integration;\n\n await gatherErrorTrackingContext(session);\n },\n};\n","/**\n * Self-driving prerequisite detection + abort vocabulary.\n *\n * The only thing worth verifying before auth is local and cheap: that\n * `session.installDir` is a real, readable directory. We deliberately do\n * NOT require the base posthog-integration report to be present — it is a\n * report many users never commit, and `requires: ['posthog-integration']`\n * is metadata, not a hard runtime gate.\n *\n * Self-driving is now in OPEN beta — available to every team — so STEP 1\n * no longer probes the Signals API as an access gate; it completes\n * instantly so the run opens with a fast first checkmark. The\n * `self-driving is not available for this project` abort below is kept\n * only as a safety net: if the Signals API genuinely can't be reached\n * during the run (a hard error that is unexpected in open beta), the skill\n * emits it and the wizard renders a friendly \"try again\" screen — now with\n * open-beta wording, not the old closed, per-team \"join the beta\" copy. The\n * PostHog-side flags (`product-autonomy`, `signals-scout`) are unchanged by\n * the wizard-side \"self-driving\" rename.\n */\n\nimport {\n existsSync,\n statSync,\n readFileSync,\n readdirSync,\n type Dirent,\n} from 'fs';\nimport { join } from 'path';\nimport { analytics } from '@utils/analytics';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { detectWarehouseSources } from '@programs/warehouse-sources/detect';\nimport type { DetectedSource } from '@programs/warehouse-sources/types';\n\n/** frameworkContext key holding the deterministic PostHog-presence result. */\nexport const POSTHOG_PRESENT_KEY = 'postHogPresent';\n\n/**\n * frameworkContext key holding the repo-relative path of the project the user\n * picked on the integration-detect screen (\".\" for the repo root). The\n * integrate-run phase scopes its install dir to this so a monorepo integrates\n * into the chosen sub-app, not the root.\n */\nexport const SELF_DRIVING_INTEGRATE_PATH_KEY = 'selfDrivingIntegratePath';\n\n/** Self-driving's own detected-tools key — not the warehouse one, which the integration program reads. */\nexport const SELF_DRIVING_DETECTED_TOOLS_KEY = 'selfDrivingDetectedTools';\n\n/** Read the detected tools out of frameworkContext. */\nexport function getSelfDrivingDetectedTools(\n session: WizardSession,\n): DetectedSource[] {\n return (\n (session.frameworkContext[SELF_DRIVING_DETECTED_TOOLS_KEY] as\n | DetectedSource[]\n | undefined) ?? []\n );\n}\n\n// Matches `posthog` at a dependency boundary (line start, or after \"'/=:.@ or\n// whitespace): catches `com.posthog:posthog-android` and `@posthog/ai`, skips\n// substrings inside other words.\nconst POSTHOG_PACKAGE_RE = /(^|[\"'\\s/=:.@])posthog/im;\n\n// Manifests grepped for a posthog dependency. Distinct from PROJECT_MANIFESTS\n// in @programs/detection/agentic (project-root discovery); keep the two in sync\n// (a test asserts the shared ecosystem names appear in both). Exported for it.\nexport const POSTHOG_MANIFESTS = [\n 'package.json',\n 'requirements.txt',\n 'pyproject.toml',\n 'Pipfile',\n 'setup.py',\n 'Gemfile',\n 'go.mod',\n 'composer.json',\n 'pubspec.yaml',\n // Apple: SPM manifest, CocoaPods (+ lockfile), XcodeGen spec.\n 'Package.swift',\n 'Podfile',\n 'Podfile.lock',\n 'project.yml',\n // Android / JVM (libs.versions.toml holds gradle coordinates).\n 'build.gradle',\n 'build.gradle.kts',\n 'gradle/libs.versions.toml',\n 'pom.xml',\n // Elixir / Rust.\n 'mix.exs',\n 'Cargo.toml',\n];\n\n// Named sub-app dirs always checked (ios/android: RN/Flutter native shells).\n// scanDirs unions these with a bounded depth-2 walk of the install dir.\nconst POSTHOG_DIRS = [\n '.',\n 'app',\n 'frontend',\n 'backend',\n 'ios',\n 'android',\n 'android/app',\n];\n\n// Heavy or vendored trees the shallow walk never descends into — they hold\n// dependencies, not a project's own manifest, and would false-positive on a\n// bundled posthog package.\nconst WALK_SKIP_DIRS = new Set([\n 'node_modules',\n 'Pods',\n 'Carthage',\n 'DerivedData',\n '.build',\n 'build',\n 'dist',\n 'out',\n '.next',\n 'coverage',\n 'vendor',\n '.venv',\n 'site-packages',\n 'target',\n '.git',\n]);\n\n// How deep below the install dir the shallow walk goes. 2 covers the common\n// monorepo shape (apps/<name>, packages/<name>) without a full tree walk.\nconst WALK_MAX_DEPTH = 2;\n\n/**\n * The install dir plus every directory within WALK_MAX_DEPTH levels (skipping\n * heavy/vendored trees and dotdirs), unioned with the explicit POSTHOG_DIRS. A\n * full shallow walk on top of the named dirs, so a monorepo's nested apps\n * (e.g. `apps/mobile`) are seen without hardcoding the path.\n */\nfunction scanDirs(installDir: string): string[] {\n const rels = new Set<string>(POSTHOG_DIRS);\n // Explicit stack — no call-stack recursion.\n const stack: Array<{ rel: string; depth: number }> = [{ rel: '.', depth: 0 }];\n while (stack.length > 0) {\n const { rel, depth } = stack.pop()!;\n rels.add(rel);\n if (depth >= WALK_MAX_DEPTH) continue;\n let entries: Dirent[];\n try {\n entries = readdirSync(join(installDir, rel), { withFileTypes: true });\n } catch {\n continue;\n }\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n if (e.name.startsWith('.') || WALK_SKIP_DIRS.has(e.name)) continue;\n // .xcodeproj/.xcworkspace are inspected via projectFileHasPostHog at the\n // parent dir — descending into the wrapper finds nothing useful.\n if (e.name.endsWith('.xcodeproj') || e.name.endsWith('.xcworkspace')) {\n continue;\n }\n stack.push({\n rel: rel === '.' ? e.name : `${rel}/${e.name}`,\n depth: depth + 1,\n });\n }\n }\n return [...rels];\n}\n\n/**\n * True if a `*.xcodeproj/project.pbxproj` or `*.csproj`/`*.fsproj` in\n * `dirPath` references PostHog.\n */\nfunction projectFileHasPostHog(dirPath: string): boolean {\n let entries: string[];\n try {\n entries = readdirSync(dirPath);\n } catch {\n return false;\n }\n for (const entry of entries) {\n const target = entry.endsWith('.xcodeproj')\n ? join(dirPath, entry, 'project.pbxproj')\n : entry.endsWith('.csproj') || entry.endsWith('.fsproj')\n ? join(dirPath, entry)\n : null;\n if (!target || !existsSync(target)) continue;\n try {\n if (POSTHOG_PACKAGE_RE.test(readFileSync(target, 'utf8'))) return true;\n } catch {\n /* unreadable — ignore */\n }\n }\n return false;\n}\n\n/**\n * Deterministic, offline check: does the project already have a PostHog SDK?\n * Scans the common dependency manifests at the install dir for a `posthog`\n * package — the same signal the agentic detector reports as `hasPostHog`, but\n * instant and credential-free. Drives whether self-driving asks to integrate\n * first (\"not found\") or proceeds straight to setup (\"found\").\n */\nexport function detectPostHogPresent(installDir: string): boolean {\n for (const dir of scanDirs(installDir)) {\n const dirPath = join(installDir, dir);\n for (const name of POSTHOG_MANIFESTS) {\n const path = join(dirPath, name);\n if (!existsSync(path)) continue;\n try {\n if (POSTHOG_PACKAGE_RE.test(readFileSync(path, 'utf8'))) return true;\n } catch {\n /* unreadable — ignore */\n }\n }\n // Variably-named project files (.xcodeproj, .csproj/.fsproj).\n if (projectFileHasPostHog(dirPath)) return true;\n }\n return false;\n}\n\n/**\n * Structured detection errors. The intro screen renders each kind into\n * JSX — keeps error data separate from presentation.\n */\nexport type SelfDrivingDetectError = {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n};\n\n/**\n * `[ABORT] <reason>` cases the self-driving skill can emit. The\n * reason strings are part of the skill contract — the context-mill\n * `self-driving-setup` skill emits these exact strings.\n */\n/** Headline shown when the run ends because GitHub was not connected. */\nexport const GITHUB_REQUIRED_MESSAGE = 'GitHub connection required';\n\n/** Body for {@link GITHUB_REQUIRED_MESSAGE}. Shared by the pre-run gate and the abort case. */\nexport const GITHUB_REQUIRED_BODY =\n 'Self-driving needs GitHub access to research issues in your code and ' +\n 'open fixes, so setup cannot finish without it. Nothing was changed. ' +\n 'When you are ready to install the PostHog GitHub App, run the wizard again.';\n\nexport const SELF_DRIVING_ABORT_CASES: AbortCase[] = [\n {\n // Skill emits: [ABORT] self-driving is not available for this project\n match: /^self-driving is not available for this project$/i,\n message: 'PostHog Self-driving is not available for this project',\n body:\n 'Self-driving is in open beta and available to every team, so this ' +\n 'is unexpected — the PostHog Signals API could not be reached for ' +\n 'this project. Nothing was changed. Try again in a moment, and if it ' +\n 'keeps happening reach out to wizard@posthog.com.',\n },\n {\n // Retained as a safety net: the GitHub gate now runs before the agent, but\n // a cached older skill can still emit this mid-run.\n match: /^github connection declined$/i,\n message: GITHUB_REQUIRED_MESSAGE,\n body: GITHUB_REQUIRED_BODY,\n },\n {\n // Skill emits: [ABORT] requires-interactive-mode\n match: /^requires-interactive-mode$/i,\n errorCode: ErrorCodes.CliInteractiveRequired,\n message: 'Interactive terminal required',\n body:\n 'Self-driving setup asks questions along the way (GitHub and ' +\n 'issue trackers), so it needs an interactive terminal. Run ' +\n 'the wizard outside CI / non-interactive mode.',\n },\n {\n // The wizard_ask tool's own error texts (non-interactive host, ask cap\n // reached) instruct the agent to emit this reason — cover it so those\n // paths render a friendly screen instead of the generic abort outro.\n match: /^requirements-incomplete$/i,\n message: 'Setup needs your input',\n body:\n 'The wizard could not collect the answers this setup needs (the ' +\n 'environment was non-interactive, or the question budget ran out). ' +\n 'Nothing was left half-configured. Run the wizard again in an ' +\n 'interactive terminal.',\n },\n];\n\n/**\n * Verify `session.installDir` is a readable directory. Writes a\n * `SelfDrivingDetectError` to frameworkContext on failure — the intro\n * screen renders it and blocks.\n */\nexport function detectSelfDrivingPrerequisites(\n session: WizardSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: SelfDrivingDetectError) =>\n setFrameworkContext('detectError', error);\n\n const installDir = session.installDir;\n\n if (!existsSync(installDir)) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'missing' });\n return;\n }\n try {\n if (!statSync(installDir).isDirectory()) {\n fail({ kind: 'bad-directory', path: installDir, reason: 'not-dir' });\n return;\n }\n } catch {\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n // Deterministic PostHog-presence check — drives the integration-check\n // screen: found → skip straight to self-driving; not found → ask to set up\n // PostHog first.\n setFrameworkContext(POSTHOG_PRESENT_KEY, detectPostHogPresent(installDir));\n\n detectConnectedTools(installDir, setFrameworkContext);\n}\n\n/** Step 5's ask options intersected with `SOURCE_DETECTORS` — reconcile when the skill's catalog grows. */\nexport const SELF_DRIVING_TOOL_KINDS: ReadonlySet<string> = new Set([\n // Issue trackers / code hosts\n 'Github',\n 'GitLab',\n 'Gitea',\n 'Linear',\n 'Jira',\n 'Shortcut',\n // Error tracking\n 'Sentry',\n 'Rollbar',\n 'Bugsnag',\n 'Honeybadger',\n 'Raygun',\n // Support desks\n 'Zendesk',\n 'Freshdesk',\n 'Front',\n 'Gorgias',\n 'Kustomer',\n 'Plain',\n // Security scanners\n 'Snyk',\n // Product feedback\n 'Canny',\n 'Productboard',\n // Search analytics\n 'GoogleSearchConsole',\n]);\n\n/** Scan for inbox-connectable tools so STEP 5 can surface them first. Best-effort — never blocks detection. */\nfunction detectConnectedTools(\n installDir: string,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n try {\n const tools = detectWarehouseSources(installDir).filter((s) =>\n SELF_DRIVING_TOOL_KINDS.has(s.kind),\n );\n\n // Tagged even at zero, so \"found nothing\" is distinguishable from \"never ran\".\n analytics.setTag('connected_tools_detected_count', tools.length);\n if (tools.length === 0) return;\n\n analytics.setTag(\n 'connected_tools_detected',\n tools.map((s) => s.kind).join(','),\n );\n setFrameworkContext(SELF_DRIVING_DETECTED_TOOLS_KEY, tools);\n } catch (error) {\n // -1, not absent, so a failed scan stays distinguishable from one that never ran.\n analytics.setTag('connected_tools_detected_count', -1);\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectConnectedTools' },\n );\n }\n}\n","/**\n * Agentic (Haiku) detection for self-driving's integration phase.\n *\n * Mirrors source-maps: the integration-detect screen runs `detectSelfDriving-\n * IntegrationProjects` (the shared integration scan from\n * @programs/detection/project-scope), shows a project map, and the user picks one.\n * `toIntegrationCandidates` matches each project to an `Integration`; this file\n * classifies it as instrumentable (a framework the wizard supports that doesn't\n * already have PostHog). The screen writes the choice to the session; `prepSelfDriving-\n * Integration` then gathers the chosen project's framework context before the\n * integration agent runs (the runner scopes the install dir).\n */\n\nimport type {\n AgenticDetectionReport,\n DetectEvent,\n} from '@programs/detection/agentic';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '@programs/detection/project-scope';\nimport { gatherFrameworkContext } from '@programs/detection/index';\nimport type { Integration } from '@shared/constants';\nimport type { WizardSession } from '@lib/wizard-session';\n\nexport type { DetectEvent };\n\n/** One project, classified for a PostHog SDK integration. */\nexport type IntegrationProject = {\n /** Path relative to the repo root (\".\" for the root). */\n path: string;\n /** Human-readable framework the agent detected (e.g. \"Next.js\"). */\n framework: string;\n /** A supported framework when it matches one, else null. */\n integration: Integration | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** integration != null && !hasPostHog — PostHog can be set up here. */\n instrumentable: boolean;\n /** hasPostHog: skip integration and continue straight to Self-driving. */\n continuable: boolean;\n /** Why the project can't be set up (only when !instrumentable). */\n reason?: string;\n};\n\nexport type IntegrationDetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: IntegrationProject[];\n};\n\nfunction classify(\n integration: Integration | null,\n hasPostHog: boolean,\n): { instrumentable: boolean; reason?: string } {\n if (integration == null) {\n return {\n instrumentable: false,\n reason: 'Not a framework the wizard can set up yet',\n };\n }\n if (hasPostHog) {\n return { instrumentable: false, reason: 'Already has the PostHog SDK' };\n }\n return { instrumentable: true };\n}\n\n/** Map a detection report into classified projects (exported for tests). */\nexport function toIntegrationReport(\n report: AgenticDetectionReport,\n): IntegrationDetectionReport {\n return {\n repoType: report.repoType,\n projects: toIntegrationCandidates(report).map((p) => ({\n path: p.path,\n framework: p.framework,\n integration: p.integration,\n hasPostHog: p.hasPostHog,\n continuable: p.hasPostHog,\n ...classify(p.integration, p.hasPostHog),\n })),\n };\n}\n\n/** Run the Haiku detector over the repo and classify projects for integration. */\nexport async function detectSelfDrivingIntegrationProjects(\n session: WizardSession,\n onEvent?: DetectEvent,\n): Promise<IntegrationDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'self-driving',\n onEvent,\n });\n return toIntegrationReport(report);\n}\n\n/**\n * Prepare the integration phase after the user picked a project on the\n * integration-detect screen (which set `session.integration`, `frameworkConfig`,\n * and the chosen path). Scopes `installDir` to the chosen project — so a\n * monorepo integrates into the picked sub-app — and gathers that project's\n * framework context, mirroring posthog-integration's `ciPreRun`. Used as the\n * integrate-run step's `onRunPrep`.\n */\nexport async function prepSelfDrivingIntegration(\n session: WizardSession,\n): Promise<void> {\n // `session` is the phase's derived session — its installDir is already the\n // picked project (the integrate-run step's `targetDir`), so just gather that\n // project's framework context.\n const frameworkConfig = session.frameworkConfig;\n if (!frameworkConfig) return;\n\n const context = await gatherFrameworkContext(frameworkConfig, {\n installDir: session.installDir,\n debug: session.debug,\n signup: session.signup,\n ci: session.ci,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n });\n for (const [key, value] of Object.entries(context)) {\n if (!(key in session.frameworkContext)) {\n session.frameworkContext[key] = value;\n }\n }\n}\n","/**\n * Self-driving program step list.\n *\n * detect → intro → integration-check → health-check → auth → integrate-detect →\n * integrate-run → self-driving-handoff → self-driving-github → run → outro. A deterministic check in\n * `detect` decides whether PostHog is already in the project: found → the\n * integration screens are skipped and the integrate-run phase never shows; not\n * found → integration-check reports it and the only action sets up PostHog.\n * After auth, `integrate-detect` runs the Haiku detector and has the user pick\n * which project to set PostHog up in (a monorepo can have several);\n * `integrate-run` then runs the real integration program's agent (its own task\n * list) in that project. `self-driving-handoff` then bridges to Self-driving\n * (\"PostHog is installed — now set up Self-driving\"), then `self-driving-github`\n * gates on the GitHub App connection the run cannot proceed without. No keep-skills step: the setup skill is transient, so postRun removes it.\n */\n\nimport type { ProgramStep } from '@programs/program-step';\nimport { resolveProjectDir } from '@programs/detection/agentic';\nimport { RunPhase, type WizardSession } from '@lib/wizard-session';\nimport { HEALTH_CHECK_STEP } from '@programs/shared/health-check-step';\nimport { integrationRunStep } from '@programs/posthog-integration/index';\nimport {\n detectSelfDrivingPrerequisites,\n POSTHOG_PRESENT_KEY,\n SELF_DRIVING_INTEGRATE_PATH_KEY,\n} from './detect.js';\nimport { prepSelfDrivingIntegration } from './detect-agentic.js';\n\n/** True once detection found PostHog already present in the project. */\nconst postHogPresent = (session: WizardSession): boolean =>\n session.frameworkContext[POSTHOG_PRESENT_KEY] === true;\n\n/** Absolute dir to integrate into: the picked sub-app (LLM output — the shared resolver clamps escapes), else the repo root. */\nconst integrationDir = (session: WizardSession): string =>\n resolveProjectDir(\n session.installDir,\n session.frameworkContext[SELF_DRIVING_INTEGRATE_PATH_KEY],\n );\n\nexport const SELF_DRIVING_PROGRAM: ProgramStep[] = [\n {\n id: 'detect',\n label: 'Detecting prerequisites',\n // Headless: validates the install dir and runs the deterministic\n // PostHog-presence check (writes frameworkContext.postHogPresent).\n onReady: (ctx) =>\n detectSelfDrivingPrerequisites(ctx.session, ctx.setFrameworkContext),\n },\n {\n id: 'intro',\n label: 'Welcome',\n screenId: 'self-driving-intro',\n gate: (session) => session.setupConfirmed,\n },\n {\n // Shown only when PostHog wasn't detected and the decision is still open:\n // \"Set up PostHog first?\". On \"yes\" the integrate-run phase runs the\n // integration. When PostHog is already present (or `--integrate` pre-set\n // it), this is skipped. The gate lets run-wizard settle the decision —\n // resolved immediately when no question is needed.\n id: 'integration-check',\n label: 'Integration',\n screenId: 'self-driving-integration-check',\n show: (session) => !postHogPresent(session) && session.integrate === null,\n isComplete: (session) =>\n postHogPresent(session) || session.integrate !== null,\n gate: (session) => postHogPresent(session) || session.integrate !== null,\n },\n HEALTH_CHECK_STEP,\n {\n id: 'auth',\n label: 'Authentication',\n screenId: 'auth',\n isComplete: (session) => session.credentials !== null,\n },\n {\n // After auth, before the integration runs: the detector scans the repo and\n // the user picks which project to set PostHog up in — a single project or\n // the repo root is still a one-item confirm. The pick writes the framework +\n // path to the session. Shown only while integrating and undecided; complete\n // once a project is picked (the orchestrator waits on this live).\n id: 'integrate-detect',\n label: 'Detecting',\n screenId: 'self-driving-integration-detect',\n show: (session) =>\n session.integrate === true && session.integration == null,\n // Complete on a picked project OR \"continue with existing\"\n // (integrate=false); without the latter the orchestrator's waitUntil hangs.\n isComplete: (session) =>\n session.integration != null || session.integrate === false,\n },\n {\n // The integration's own run step, imported and composed here: it runs the\n // integration agent (its prompt, tools, task list) in the picked project's\n // dir. Shown only when integrating; prep gathers that project's framework\n // context. Completion is tracked via `completedRuns`, separate from the\n // Self-driving run's `runPhase`.\n ...integrationRunStep,\n id: 'integrate-run',\n onRunPrep: prepSelfDrivingIntegration,\n targetDir: integrationDir,\n show: (session) => session.integrate === true,\n isComplete: (session) => session.completedRuns.includes('integrate-run'),\n },\n {\n // Handoff after the integration run: \"PostHog is installed — now set up\n // Self-driving\". Only in the integrate path; the already-has-PostHog path\n // skips it. Complete once acknowledged (the orchestrator waits on this).\n id: 'self-driving-handoff',\n label: 'Ready',\n screenId: 'self-driving-handoff',\n show: (session) => session.integrate === true,\n isComplete: (session) => session.selfDrivingHandoffConfirmed,\n },\n {\n // Hard gate before the agent starts: Self-driving cannot research findings\n // or open fixes without repo access. Asking here rather than mid-run means\n // a user who steps away isn't read as declining, and a user who won't\n // connect hasn't paid for an agent start. Complete once GitHub is\n // connected, or once the user says they can't — which hides `run` below.\n id: 'self-driving-github',\n label: 'GitHub',\n screenId: 'self-driving-github',\n isComplete: (session) =>\n session.githubConnected === true || session.githubDeclined,\n gate: (session) =>\n session.githubConnected === true || session.githubDeclined,\n },\n {\n id: 'run',\n label: 'Self-driving',\n screenId: 'run',\n show: (session) => !session.githubDeclined,\n isComplete: (session) =>\n session.runPhase === RunPhase.Completed ||\n session.runPhase === RunPhase.Error,\n },\n {\n id: 'outro',\n label: 'Done',\n screenId: 'outro',\n isComplete: (session) => session.outroDismissed,\n },\n];\n","import { AgentSignals } from '@agent';\nimport type { PromptContext } from '@agent/types';\nimport type { DetectedSource } from '@programs/warehouse-sources/types';\n\n/**\n * Render the deterministic codebase-tool scan for the prompt. STEP 4 and\n * STEP 5 read this instead of the agent doing its own flaky \"light scan\":\n * enable the matching signal sources, and surface detected tools first in the\n * connected-tools ask rather than dumping the full ~500-source catalog. When\n * nothing is detected we still say so explicitly, so the agent falls back to\n * the skill's default ordering instead of inventing a scan.\n */\nfunction renderDetectedTools(sources: DetectedSource[]): string {\n if (sources.length === 0) {\n return `Tools detected in this codebase: none found by the dependency + env scan. Use the skill's default ordering for STEP 5.`;\n }\n\n const lines = sources.map(\n (s) => `- ${s.label} (source_type: ${s.kind}) — ${s.matchedSignal}`,\n );\n return [\n 'Tools detected in this codebase (deterministic dependency + env-key scan — this is evidence, not a guess):',\n ...lines,\n '',\n 'Use this list in STEP 4 (enable the matching signal sources) and STEP 5 ' +\n '(surface these detected tools first in the connected-tools ask, ahead ' +\n 'of the SaaS basics and the \"others\" option), exactly as the skill ' +\n 'describes. Never enable or pre-select a tool the user has not confirmed.',\n ].join('\\n');\n}\n\n/**\n * Build the self-driving run prompt. The installed\n * `self-driving-setup` skill is the source of truth for the HOW of\n * every step (which MCP tools to call, which sources/scouts apply, how\n * to verify); this prompt carries the order, the wizard-specific\n * mechanics (wizard_ask, abort signals), and the project URLs.\n *\n * `detectedSources` is the deterministic codebase scan (from the detect step);\n * it drives STEP 4/STEP 5 tool prioritisation. Empty is fine — the block then\n * tells the agent to use the skill's default ordering.\n *\n * Integration (when the project has no PostHog yet) runs as a separate phase\n * before this — the real integration program, with its own screens and task\n * list — so this prompt only covers the Self-driving steps.\n */\nexport function buildSelfDrivingPrompt(\n ctx: PromptContext,\n detectedSources: DetectedSource[] = [],\n): string {\n const uiHost = ctx.host.appHost.replace(/\\/$/, '');\n const projectBase = `${uiHost}/project/${ctx.projectId}`;\n const integrationsSettingsUrl = `${projectBase}/settings/environment-integrations`;\n const orgAiSettingsUrl = `${uiHost}/settings/organization#organization-ai-consent`;\n const newWarehouseSourceUrl = `${projectBase}/pipeline/new/source`;\n const inboxUrl = `${projectBase}/inbox`;\n const optIn = (value: boolean | null | undefined): string =>\n value === true ? 'ON' : value === false ? 'OFF' : 'unknown';\n const optIns = ctx.teamProductOptIns;\n\n return `You are setting up PostHog Self-driving for this project: you will enable the right signal sources, tune the scout troop, design custom scouts for what this product uniquely needs, put Replay Vision scanners on its key flows, and hand the user a configured inbox.\n\nProject URLs:\n- Integrations settings: ${integrationsSettingsUrl}\n- Organization AI settings: ${orgAiSettingsUrl}\n- New data warehouse source (Linear / Zendesk / GitHub issues / pganalyze): ${newWarehouseSourceUrl}\n- Self-driving inbox: ${inboxUrl}\n\nProject state read at auth time (PostHog project settings — authoritative\nfor whether a product is enabled, regardless of what this repo\ninstruments; products are often instrumented from other repos or the\nsnippet, so repo evidence may rule a product IN but never OUT):\n- Session replay recording: ${optIn(optIns?.sessionReplay)}\n- Exception autocapture (error tracking): ${optIn(optIns?.exceptionAutocapture)}\n- Surveys: ${optIn(optIns?.surveys)}\n\n${renderDetectedTools(detectedSources)}\n\nThe installed skill is the source of truth for the HOW of every step:\nwhich MCP tools to call, which sources and scouts apply to this product,\nand how to verify each change. The STEPS below give the order and the\nwizard-specific mechanics — read the matching skill reference before\ndoing the work, and do not invent steps the skill doesn't describe.\n\nBefore doing any work, create your FULL task list in a single TaskCreate\ncall so the user can follow your progress in the TUI. Use exactly these\ntasks, in this order:\n 1. Check Self-driving access\n 2. Read project and current Self-driving state\n 3. Enable products (replay, error tracking, support)\n 4. Enable signal sources\n 5. Offer issue-tracker integrations\n 6. Configure the scout troop\n 6b. Design custom scouts\n 6c. Set up Replay Vision scanners\n 7. Write report and hand off\nDrive the list with TaskUpdate — mark a task in_progress when you start\nit and completed when done. If a step turns out to be a no-op (e.g. a\nproduct is already enabled), still mark its task completed.\n\nThe PostHog GitHub App is already connected — the wizard verified it\nbefore this run started, so never ask the user to connect or install it.\n\nWizard mechanics:\n- Ask the user things ONLY with the wizard_ask MCP tool, and batch\n related questions (e.g. one multi-select for all issue trackers, not\n one ask per tool). The per-run ask budget is limited.\n- If wizard_ask is unavailable (CI / non-interactive), emit\n ${AgentSignals.ABORT} requires-interactive-mode and halt.\n- When a step requires the user to do something in the browser, give\n them the exact URL inside the wizard_ask prompt text — do not try to\n open a browser yourself.\n- Emit ${AgentSignals.STATUS} lines as you complete each step so the\n user sees progress.\n\nFollow these steps IN ORDER. Do not skip or reorder.\n\nSTEP 1 — Check Self-driving access. (skill: \"Check access\")\n Self-driving is in open beta and available to every team, so there is\n no access gate to probe. Do NOT call any MCP tool here — mark this task\n in_progress and then completed right away and emit the\n ${AgentSignals.STATUS} line, so the user sees an immediate first step.\n Only if the Signals API later turns out to be genuinely unreachable for\n this project (a hard error on every Signals call, unexpected in open\n beta) should you emit\n ${AgentSignals.ABORT} self-driving is not available for this project\n and halt.\n\nSTEP 2 — Read project and current Signals state. (skill: \"Read context\")\n If ./posthog-setup-report.md exists, read it as a strong hint for what\n THIS repo instruments — but it is often absent (users frequently don't\n commit it), so do NOT depend on it. Combine whatever you find with the\n project-state block above and the skill's server-side usage probes —\n repo evidence rules products in, never out. Do a light scan ONLY for\n what neither covers. List the currently enabled signal sources so every\n later write is idempotent.\n\nSTEP 3 — Enable products. (skill: \"Enable products\")\n Turn ON the PostHog products Signals reads from — Session Replay,\n Error Tracking, and Support — so the sources you enable next, and the\n Replay Vision scanners in STEP 6c, have data to read. These are\n server-side enables with conservative defaults (owned by the server,\n not you). The project-state block above covers\n only Session Replay and Error Tracking, so you can skip those if they\n are already ON; it does not show Support, but every enable is idempotent\n so enabling any of them again is safe regardless. For a web app, also\n make sure this repo's posthog-js\n init doesn't disable them; for a pure backend or mobile app the server\n flip is inert, so just record that for the report and move on. The skill\n names the exact tool and the per-platform detail.\n\nSTEP 4 — Enable signal sources. (skill: \"Enable sources\")\n Enable the sources that match what this product actually uses, per\n the skill. Never enable a source for a tool the user hasn't\n confirmed they use.\n\nSTEP 5 — Offer issue-tracker integrations. (skill: \"Connected tools\")\n One batched multi-select wizard_ask for the external tools the skill\n lists. Order it per the skill: the tools from the \"Tools detected in\n this codebase\" list above come first, then the SaaS basics, then an\n \"others\" option for the long tail — never dump the whole catalog.\n The run auto-connects the ones it can (GitHub Issues, and\n Linear via a one-click OAuth link), verifying each with a single\n silent check — never nudge. For GitHub Issues: when the GitHub\n integration has exactly one repository connected, use that repo by\n default and skip repo research entirely — research which repo\n matches this project only when several are connected. It arms the\n rest as dormant responders to\n finish later: for tools it can't auto-connect (Zendesk, pganalyze) it\n never sends the user to paste credentials and never re-prompts. Enable\n a source only for a tool the user picked.\n\nSTEP 6 — Configure the scout troop. (skill: \"Scouts\")\n Materialize the troop, read the project's enforced scout-run budget\n (100 runs a day by default), then enable a selective set — the\n \"general\" scout plus the three to five specialists for the products\n this project uses most — and disable the rest, per the skill. The\n whole troop, including step 6b's custom scouts, stays at or under\n ten enabled scouts.\n\n Then tell the user what the troop is, before STEP 6b asks them to add\n to it. Scouts are PostHog's built-in ones, and the user has not seen\n the list: without it, STEP 6b asks them to approve additions to a\n baseline they cannot picture. Send one short message naming each\n scout you enabled with a plain one-line description of what it\n watches, and say how many you left disabled and that they can turn\n those on later from the inbox. No wizard_ask here, it is not a\n question. Keep each line to one sentence and do not restate the\n skill's internals.\n\nSTEP 6b — Design custom scouts for this product. (skill: \"Custom scouts\")\n You are the only actor that has read this repo — turn that into\n coverage per the skill: a real gap analysis of the project's\n watchable surfaces against what the built-in troop already covers,\n then custom scouts for the uncovered ones. Start the gap analysis\n from the repo's for-agents context when present (AGENTS.md,\n CLAUDE.md, ARCHITECTURE.md, .cursor/rules, agent-facing docs) — it\n is a maintained map of the product's surfaces and vocabulary, so\n read it before scanning source. Keep scout bodies\n high-level: describe the behavior and signal conditions to watch,\n referencing repo evidence by file/function name — never paste raw\n source, secrets, env values, or customer data into a scout body.\n Never edit built-in scout bodies. Propose all candidates in ONE\n batched wizard_ask\n before creating anything; the user declining everything (or finding\n no gap at all) is a valid outcome, not an abort. Mark the task\n completed either way.\n\nSTEP 6c — Set up Replay Vision scanners. (skill: \"Replay Vision scanners\")\n Create the scanner skeletons the skill defines, filling the per-product\n blanks it leaves you from this repo's code. Scanners need Session Replay\n on (STEP 3). Every failure here — no recordings yet, a backend-only\n project, the scanner API missing — is a follow-up, never an abort; mark\n the task completed regardless.\n\nSTEP 7 — Write the report and hand off. (skill: \"Report\")\n Write the report per the skill, including follow-ups for anything\n deferred. Tell the user findings will start appearing in their inbox\n at ${inboxUrl} within about 30 minutes.`;\n}\n","/**\n * Canonical step keys for the Self-driving run's `wizard: step` analytics.\n *\n * The agent writes its own task labels, so the same step reaches analytics under whatever wording\n * that run's model picked — \"Enable products\", \"Enabling products\" and \"Turn on PostHog products\"\n * are all STEP 3. A funnel keyed on that label silently loses runs the moment the wording drifts,\n * which is invisible: the number just drops.\n *\n * These rules map a label back to the step it belongs to, so a funnel can key on `step_key` and stay\n * correct across rewordings. `step_name` still ships alongside it, unchanged, for anything that wants\n * the label the user actually saw.\n */\n\n/**\n * Canonical keys, in the order the prompt's STEPs run. Frozen: these are event property values.\n * `connect_github` is retained for historical runs — the GitHub App is now connected on a wizard\n * screen before the agent starts, so no current run emits it.\n */\nexport const SELF_DRIVING_STEP_KEYS = [\n 'check_access',\n 'read_context',\n 'connect_github',\n 'enable_products',\n 'enable_signal_sources',\n 'connect_issue_trackers',\n 'configure_scouts',\n 'design_custom_scouts',\n 'setup_replay_vision',\n 'write_report',\n] as const;\n\nexport type SelfDrivingStepKey = (typeof SELF_DRIVING_STEP_KEYS)[number];\n\n/**\n * First match wins, so order is the rule. Entity words come before verbs: \"Checking GitHub\n * connection\" is the GitHub step, not the access-check step, and \"Connecting issue trackers\" must\n * not fall through to GitHub just because a later run words it \"Connecting GitHub issues\".\n */\nconst RULES: readonly { key: SelfDrivingStepKey; match: RegExp }[] = [\n { key: 'connect_issue_trackers', match: /issue[\\s-]?track/i },\n { key: 'setup_replay_vision', match: /replay vision|vision scanner/i },\n { key: 'design_custom_scouts', match: /custom scout|designing scout/i },\n {\n key: 'configure_scouts',\n match: /scout troop|tun(e|ing) .*scout|configur\\w* .*scout/i,\n },\n { key: 'enable_signal_sources', match: /signal source/i },\n { key: 'connect_github', match: /github/i },\n { key: 'enable_products', match: /product/i },\n { key: 'write_report', match: /report/i },\n { key: 'check_access', match: /access/i },\n { key: 'read_context', match: /read|context|project state/i },\n];\n\n/**\n * The canonical key for an agent-authored step label, or `undefined` when nothing matches — an\n * unmatched step is left unkeyed rather than bucketed, so a new step shows up as a gap to name\n * instead of quietly inflating whichever key it landed nearest.\n */\nexport function resolveSelfDrivingStepKey(\n stepName: string | undefined,\n): SelfDrivingStepKey | undefined {\n if (!stepName) return undefined;\n return RULES.find((rule) => rule.match.test(stepName))?.key;\n}\n","/**\n * Self-driving pricing, in one place. It's stated at six points across the run (intro, tips,\n * learn deck, outro), and a price quoted six ways is a price the reader stops trusting.\n *\n * Careful with the word \"free\". The tracking this wizard installs bills as ordinary PostHog\n * usage, and scouts are heading for usage-based pricing too — so anything called free here is\n * a promise with an expiry date. State what the PR costs and leave the rest to usage-based\n * billing, which stays true either side of that change.\n */\n\n/** Flat USD charge per report that ships a pull request. */\nexport const PRICE_PER_PR_USD = 15;\n\n/** One line, for a screen that only has room to say what it costs. */\nexport const PRICING_SHORT = `Agents charge a flat $${PRICE_PER_PR_USD} per pull request they ship.`;\n\n/** The same fact at length, plus the bit people are surprised by later. */\nexport const PRICING_LONG =\n `A report that ships a pull request costs a flat $${PRICE_PER_PR_USD}. ` +\n `Everything else follows your usual PostHog usage-based pricing.`;\n\n/**\n * Nothing caps the spend unless you set it, which is worth saying out loud. Checked against\n * billing: `custom_limits_map` starts empty and a product with no custom limit resolves to\n * `usage_limit: None`, i.e. uncapped. A free plan is capped by its own allocation instead,\n * hence the qualifier.\n */\nexport const NO_DEFAULT_LIMIT =\n 'A paid plan has no monthly cap until you set one. Add a PR limit in your inbox sidebar, ' +\n 'and agents pause when they hit it.';\n","import { NO_DEFAULT_LIMIT, PRICING_LONG } from './pricing.js';\n\n/**\n * Sidebar tips for the self-driving run — short footnotes on the\n * vocabulary ladder (signal source → scout → signal → report → measured\n * loop). The learn deck (`./index.tsx`) carries the teaching; these stay\n * one to two lines each so the pane never overflows.\n *\n * Product knowledge lives here in the program, not in the generic\n * `TipsCard`. Wired onto the program's `getTips`.\n */\n\nimport type { Tip } from '@ui/tui/components/TipsCard';\n\nexport const SELF_DRIVING_TIPS: Tip[] = [\n {\n id: 'signal-source',\n title: 'Signal source',\n description:\n 'A stream PostHog watches: errors, replays, support, GitHub or Linear issues.',\n },\n {\n id: 'scout',\n title: 'Scout',\n description:\n 'An agent on a schedule, watching your sources for anomalies and patterns. Your project gets up to 100 scout runs a day by default.',\n },\n {\n id: 'scanner',\n title: 'Scanner',\n description:\n 'Where a scout reads your data, a scanner watches session recordings — an LLM spotting what broke on screen. It spends Replay Vision quota, so it stays scoped to your key flows.',\n },\n {\n id: 'signal-report',\n title: 'Signal → report',\n description:\n 'One finding with evidence and a suggested action. Signals group into prioritized reports in your inbox.',\n },\n {\n id: 'the-loop',\n title: 'The loop',\n description:\n 'After a fix ships, PostHog measures the result. If the pattern persists, a new signal reopens the work.',\n },\n {\n id: 'pricing',\n title: 'What it costs',\n description: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`,\n },\n {\n id: 'work-anywhere',\n title: 'Work anywhere',\n description:\n 'Your inbox is in the PostHog app, Slack (tag @PostHog), and MCP. You can work from anywhere.',\n },\n];\n\nexport const getTips = (): Tip[] => SELF_DRIVING_TIPS;\n","/**\n * PIPELINE_BLOCK — ASCII diagram of the Self-driving loop: sources feed\n * scouts, scouts raise signals, signals group into reports, reports land\n * in the inbox, shipped fixes get measured, and misses loop back as new\n * signals. Kept under ~38 chars wide so it fits the LearnCard pane at an\n * 80-column terminal.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\n\nexport const PIPELINE_BLOCK: ContentBlock = {\n type: 'lines',\n interval: 300,\n pause: 8000,\n lines: [\n <Text>\n {' '}\n <Text bold color=\"cyan\">\n signal sources\n </Text>\n </Text>,\n <Text dimColor>{' errors · replays · support'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n scouts\n </Text>\n </Text>,\n <Text dimColor>{' watching on a schedule'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n signals\n </Text>\n </Text>,\n <Text dimColor>{' finding + evidence + action'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n reports\n </Text>\n </Text>,\n <Text dimColor>{' grouped and prioritized'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color=\"green\">\n your inbox\n </Text>\n </Text>,\n <Text dimColor>{' investigate · tag · ship a PR'}</Text>,\n <Text>\n <Text color=\"gray\">{' ↓ '}</Text>\n <Text bold color={Colors.accent}>\n measured\n </Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ↺ '}</Text>\n <Text dimColor>didn't fix it? a new signal</Text>\n </Text>,\n ],\n};\n","import { NO_DEFAULT_LIMIT, PRICING_LONG } from './pricing.js';\n/**\n * Self-driving learn-deck — the narrative script played while the agent\n * sets up Self-driving. Teaches the vocabulary ladder (signal source →\n * scout → signal → report → action → measured loop) before the agent's\n * questions use those nouns, and explains why GitHub is required before\n * the connect ask lands.\n *\n * One deck for every self-driving run. On the integrate path it plays\n * during the install run (the pane belongs to whichever run screen shows\n * first), so it doubles as the wait-time read.\n */\n\nimport { Text } from 'ink';\nimport { Colors } from '@ui/tui/styles';\nimport type { WizardStore } from '@ui/tui/store';\nimport { TextRevealMode } from '@ui/tui/primitives/TextBlock';\nimport type { ContentBlock } from '@ui/tui/primitives/content-types';\nimport { StatusPeekTrigger } from '@ui/tui/components/StatusPeekTrigger';\nimport { PIPELINE_BLOCK } from './pipeline-diagram.js';\n\nconst CLEAR: ContentBlock = { type: 'clear', pause: 2000 };\n\nexport const getContentBlocks = (store?: WizardStore): ContentBlock[] => {\n return [\n // Scene 1 — orient\n {\n content: 'Welcome.',\n pause: 3000,\n mode: TextRevealMode.Typewriter,\n animationInterval: 160,\n },\n\n { content: 'The Wizard is an agent.', pause: 4000 },\n\n {\n content: \"It's setting up PostHog Self-driving for this project.\",\n pause: 5000,\n },\n\n {\n content: \"We'll occasionally need your input. Stick around.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 2 — controls\n {\n pause: 5000,\n persist: true,\n content: <StatusPeekTrigger store={store} />,\n },\n\n {\n pause: 6000,\n content: (\n <Text>\n Press{' '}\n <Text color={Colors.accent} bold>\n S\n </Text>{' '}\n to expand or collapse the status.\n </Text>\n ),\n },\n\n CLEAR,\n\n // Scene 3 — platform story\n {\n content: 'Your PostHog tools all share one data layer.',\n pause: 4000,\n },\n\n {\n content:\n 'Analytics, replays, errors, flags: they all share the same users, mapped across every tool.',\n pause: 6000,\n },\n\n {\n content: 'Together, they show how people really use your product.',\n pause: 5000,\n },\n\n {\n content:\n \"Piecing that together used to be manual work. That's what Self-driving automates.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 4 — the differentiator, and why GitHub\n {\n content:\n 'A coding agent sees your code, but not how your product is used.',\n pause: 5000,\n },\n\n { content: 'PostHog knows what your users actually do.', pause: 4000 },\n\n { content: 'Self-driving puts an agent in the loop.', pause: 5000 },\n\n {\n content:\n \"That's why setup will ask to connect GitHub: so Self-driving can access your code and your context, together.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 5 — signal sources\n {\n content: 'Each PostHog tool you enable is a signal source.',\n pause: 4000,\n },\n\n {\n content:\n \"Errors, replays, support tickets, GitHub or Linear issues: they're all streams of product usage.\",\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 6 — scouts\n {\n content: 'Scouts are agents that watch those sources on a schedule.',\n pause: 5000,\n },\n\n {\n content:\n 'They look for anomalies and patterns: a spike in errors or a funnel quietly dropping.',\n pause: 6000,\n },\n\n { content: 'Setup tunes your scouts to fit your product.', pause: 4000 },\n\n CLEAR,\n\n // Scene 7 — signals and reports\n {\n content:\n \"When a scout finds something worth your attention, that's a signal.\",\n pause: 5000,\n },\n\n {\n content:\n 'Each signal carries the finding, the evidence, and a suggested action.',\n pause: 5000,\n },\n\n {\n content: 'Related signals group into reports, sorted by priority.',\n pause: 4000,\n },\n\n CLEAR,\n\n { content: \"Here's the whole loop.\", pause: 1000 },\n\n PIPELINE_BLOCK,\n\n CLEAR,\n\n // Scene 8 — payoff: the inbox\n { content: 'Reports land in your Self-driving inbox.', pause: 4000 },\n\n {\n content: 'Investigate with the agent, tag teammates, or kick off a PR.',\n pause: 6000,\n },\n\n {\n // No lead-in: PRICING_LONG already names the PR as what's charged, and the\n // pane only fits ~148 chars per beat.\n content: PRICING_LONG,\n pause: 5000,\n },\n\n {\n content: NO_DEFAULT_LIMIT,\n pause: 5000,\n },\n\n CLEAR,\n\n // Scene 9 — the loop kicker\n {\n content: 'And after the PR ships, PostHog measures the result.',\n pause: 5000,\n },\n\n { content: 'Did the fix actually address the pattern?', pause: 4000 },\n\n {\n content: 'If not, a new signal surfaces, and the loop keeps working.',\n pause: 6000,\n },\n\n CLEAR,\n\n // Scene 10 — surfaces + closer\n {\n content: 'Work with Self-driving wherever you already are:',\n pause: 2000,\n },\n\n {\n type: 'lines',\n interval: 800,\n pause: 6000,\n lines: [\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>Inbox</Text>\n <Text dimColor>{' the PostHog web app'}</Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>Slack</Text>\n <Text dimColor>{' tag @PostHog'}</Text>\n </Text>,\n <Text>\n <Text color=\"cyan\">{' ◆ '}</Text>\n <Text bold>MCP</Text>\n <Text dimColor>{' your agents and editors'}</Text>\n </Text>,\n ],\n },\n\n {\n content: 'Product usage data becomes real, shippable change.',\n pause: 5000,\n },\n\n { content: 'Your product drives itself.', pause: 10000 },\n ];\n};\n","import { join } from 'path';\nimport { access, rm } from 'node:fs/promises';\nimport type { ProgramConfig } from '@programs/program-step';\nimport type { ProgramRun } from '@programs/program-run';\nimport { OutroKind, type WizardSession } from '@lib/wizard-session';\nimport { createSkillProgram } from '../agent-skill/index.js';\nimport { SELF_DRIVING_PROGRAM } from './steps.js';\nimport {\n SELF_DRIVING_ABORT_CASES,\n getSelfDrivingDetectedTools,\n} from './detect.js';\nimport { buildSelfDrivingPrompt } from './prompt.js';\nimport { resolveSelfDrivingStepKey } from './step-keys.js';\nimport {\n NO_DEFAULT_LIMIT,\n PRICE_PER_PR_USD,\n PRICING_LONG,\n} from '../../ui/tui/decks/self-driving/pricing.js';\nimport { getTips } from '../../ui/tui/decks/self-driving/tips.js';\nimport { getContentBlocks } from '../../ui/tui/decks/self-driving/index.js';\n\nexport const SELF_DRIVING_SKILL_ID = 'self-driving-setup';\nconst REPORT_FILE = 'posthog-self-driving-report.md';\nconst DOCS_URL = 'https://posthog.com/docs';\nconst SUCCESS_MESSAGE =\n 'Self-driving is on. PostHog is scanning your project; first findings ' +\n 'hit your inbox within ~30 minutes.';\nconst WIZARD_MARKER = '.posthog-wizard';\n\n/**\n * Remove the installed setup skill. It is transient orchestration\n * knowledge (unlike integration skills such as the Next.js one, which\n * are worth keeping for the user's coding agents), so the program\n * cleans it up instead of showing the keep-skills prompt. Marker-\n * guarded: only directories the wizard installed are touched.\n */\nasync function removeInstalledSkill(installDir: string): Promise<void> {\n const skillDir = join(installDir, '.claude', 'skills', SELF_DRIVING_SKILL_ID);\n try {\n await access(join(skillDir, WIZARD_MARKER));\n } catch {\n return;\n }\n await rm(skillDir, { recursive: true, force: true }).catch(() => undefined);\n}\n\n// A session closure (not a static object) so `customPrompt` can read the\n// tools detected in the codebase — written to frameworkContext by the detect\n// step — and hand them to the prompt for STEP 4/STEP 5 prioritisation.\nconst buildRun = (session: WizardSession): Promise<ProgramRun> =>\n Promise.resolve({\n skillId: SELF_DRIVING_SKILL_ID,\n integrationLabel: SELF_DRIVING_SKILL_ID,\n customPrompt: (ctx) =>\n buildSelfDrivingPrompt(ctx, getSelfDrivingDetectedTools(session)),\n successMessage: SUCCESS_MESSAGE,\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Setting up PostHog Self-driving...',\n estimatedDurationMinutes: 10,\n abortCases: SELF_DRIVING_ABORT_CASES,\n // The flow legitimately needs several interactions (GitHub connect +\n // verify, issue-tracker picks, the scout-tailoring proposal), so raise\n // the wizard_ask budget a little above the default 10.\n maxQuestions: 13,\n // This flow hands the user long OAuth/authorize URLs (Linear, GitHub\n // fallback, Zendesk) in wizard_ask prompts. Render them as OSC 8\n // hyperlinks + clipboard copy so the overlay's line wrapping can't break\n // the click target. Scoped to this program only.\n richLinks: true,\n // STEP 3 (GitHub App install) and STEP 5 (Linear OAuth) park on wizard_ask\n // while the user does slow browser work; a first-time GitHub App install\n // routinely exceeds the 5-min default, and a timeout is indistinguishable\n // from a decline (both resolve to __cancelled__). Match upload-source-maps.\n askTimeoutMs: 30 * 60 * 1000,\n\n // Emit a `wizard: step` analytics event on each agent task transition so we\n // can build a step-level drop-off funnel (where a run stops — GitHub connect,\n // scout enable, etc.), including silent steps with no wizard_ask. Opt-in, so\n // only self-driving runs emit these; every other program is unchanged.\n trackStepProgress: true,\n\n // Key those events by step as well as by label, so a funnel over them (GitHub connect\n // conversion, scout enable rate) keeps counting when a run words its tasks differently.\n resolveStepKey: resolveSelfDrivingStepKey,\n\n postRun: async (session) => {\n await removeInstalledSkill(session.installDir);\n },\n\n buildOutroData: (_session, credentials) => {\n const uiHost = credentials.host.appHost.replace(/\\/$/, '');\n const inboxUrl = `${uiHost}/project/${credentials.projectId}/inbox`;\n return {\n kind: OutroKind.Success as const,\n message: SUCCESS_MESSAGE,\n primaryLink: { label: 'Your Self-driving inbox', url: inboxUrl },\n nextSteps: {\n heading: 'In your inbox you can:',\n items: [\n 'Investigate reports with the agent',\n 'Tag teammates to loop them in',\n `Kick off a PR when you like the proposed fix ($${PRICE_PER_PR_USD} flat)`,\n 'Cap the spend with a monthly PR limit in the sidebar',\n 'Or work from Slack (tag @PostHog) and MCP',\n ],\n },\n body: `${PRICING_LONG} ${NO_DEFAULT_LIMIT}`,\n reportFile: REPORT_FILE,\n };\n },\n });\n\nexport const selfDrivingConfig: ProgramConfig = {\n ...createSkillProgram({\n skillId: SELF_DRIVING_SKILL_ID,\n command: 'self-driving',\n id: 'self-driving',\n description: 'Set up PostHog Self-driving for this project',\n integrationLabel: SELF_DRIVING_SKILL_ID,\n successMessage: SUCCESS_MESSAGE,\n reportFile: REPORT_FILE,\n docsUrl: DOCS_URL,\n spinnerMessage: 'Setting up PostHog Self-driving...',\n estimatedDurationMinutes: 10,\n requires: ['posthog-integration'],\n abortCases: SELF_DRIVING_ABORT_CASES,\n }),\n steps: SELF_DRIVING_PROGRAM,\n run: buildRun,\n getTips,\n getContentBlocks,\n};\n\nexport { SELF_DRIVING_PROGRAM } from './steps.js';\nexport {\n detectSelfDrivingPrerequisites,\n SELF_DRIVING_ABORT_CASES,\n type SelfDrivingDetectError,\n} from './detect.js';\n","/**\n * MCP add / remove / tutorial programs.\n *\n * None of these run the agent pipeline — they're TUI-only flows invoked\n * by the `mcp add` / `mcp remove` / `mcp tutorial` subcommands in\n * bin.ts. They live in the program registry so the screen sequence is\n * derived alongside every other program (no special-cases in\n * screen-sequences.ts).\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\nimport { McpOutcome } from '@lib/wizard-session';\n\nexport const mcpAddConfig: ProgramConfig = {\n id: 'mcp-add',\n requiresAi: false,\n description: 'Add PostHog MCP server to supported clients',\n // Order: install → Slack → tutorial. Slack runs before the tutorial\n // because it renders gracefully without credentials (no surprise\n // OAuth on the loginless install path). The tutorial is last so its\n // explicit \"Start tutorial\" opt-in is the moment OAuth fires — and\n // skipping the tutorial doesn't bury Slack discovery behind a\n // dismissal screen.\n steps: [\n {\n id: 'mcp-add',\n label: 'Add MCP server',\n screenId: 'mcp-add',\n isComplete: (s) => s.mcpComplete,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n // Gate on a successful install so no-clients / skipped / failed\n // outcomes go straight to program end without a \"what's next\" prompt.\n show: (s) => s.mcpOutcome === McpOutcome.Installed,\n isComplete: (s) => s.slackStepDismissed,\n },\n {\n id: 'mcp-suggested-prompts',\n label: 'Suggested prompts',\n screenId: 'mcp-suggested-prompts',\n // Same install gate — without a working MCP there's nothing to\n // talk to from the tutorial.\n show: (s) => s.mcpOutcome === McpOutcome.Installed,\n isComplete: (s) => s.mcpSuggestedPromptsDismissed,\n // This step *is* the tutorial, so it reports there rather than to\n // `mcp-add`. Literal avoids a runtime cycle with the program registry;\n // the `ProgramId` type still catches a rename.\n reportsAsProgramId: 'mcp-tutorial',\n },\n ],\n};\n\n/**\n * `wizard mcp remove` — single-step uninstall flow.\n *\n * DO NOT append `mcp-suggested-prompts` (or any other tutorial-shaped\n * step) here. A user who just removed MCP is opting OUT of the agent having\n * access to PostHog; immediately pivoting into a tutorial that asks\n * them to log in and try prompts is wrong on intent and confusing on\n * UX. The screen also reads `session.mcpInstalledClients` for its\n * Choose-phase copy (\"MCP is installed for X\") — that array is empty\n * post-remove, so the copy would be a lie.\n *\n * If you want a \"did you mean to keep it?\" confirmation, build that as\n * a screen earlier in this program — don't reuse the tutorial.\n */\nexport const mcpRemoveConfig: ProgramConfig = {\n id: 'mcp-remove',\n requiresAi: false,\n description: 'Remove PostHog MCP server from supported clients',\n steps: [\n {\n id: 'mcp-remove',\n label: 'Remove MCP server',\n screenId: 'mcp-remove',\n isComplete: (s) => s.mcpComplete,\n },\n ],\n};\n\n/**\n * Standalone tutorial flow — boots directly into the Choose phase of\n * McpSuggestedPromptsScreen without going through MCP install first.\n * Useful for users who already installed MCP and want to revisit the\n * tutorial, or anyone who just wants to try the agent against PostHog\n * without touching their IDE config.\n *\n * The screen handles its own OAuth (via services.performLogin) so this\n * program doesn't pre-populate credentials.\n */\nexport const mcpTutorialConfig: ProgramConfig = {\n id: 'mcp-tutorial',\n requiresAi: false,\n description: 'Try the PostHog MCP with your agent — no install needed',\n steps: [\n {\n id: 'mcp-suggested-prompts',\n label: 'MCP tutorial',\n screenId: 'mcp-suggested-prompts',\n isComplete: (s) => s.mcpSuggestedPromptsDismissed,\n },\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n isComplete: (s) => s.slackStepDismissed,\n },\n ],\n};\n","import type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { createSkillProgram } from '@programs/agent-skill/index';\n\nconst MCP_ANALYTICS_REPORT_FILE = 'posthog-mcp-analytics-report.md';\n\n/**\n * `[ABORT]` reasons the mcp-analytics skill emits when the project can't be\n * instrumented. Kept in sync with the stop conditions in the skill's\n * `description.md` (context-mill `context/skills/mcp-analytics`).\n */\nexport const MCP_ANALYTICS_ABORT_CASES: AbortCase[] = [\n {\n match: /^unsupported language for mcp analytics$/i,\n errorCode: ErrorCodes.DetectUnsupportedPlatform,\n message: 'Unsupported language for MCP analytics',\n body:\n 'MCP analytics supports TypeScript/JavaScript (`@posthog/mcp`) and Python ' +\n '(`posthog.mcp`, shipped inside the `posthog` package). This project ' +\n \"doesn't look like either, so there's nothing to instrument. \" +\n 'See https://posthog.com/docs/mcp-analytics for the supported setups.',\n },\n {\n match: /^no mcp server found$/i,\n message: 'No MCP server found',\n body:\n 'This command instruments an existing MCP server with PostHog analytics, ' +\n 'but no MCP server was found in this project. If you just want PostHog ' +\n 'product analytics, run `npx @posthog/wizard` instead.',\n },\n {\n match: /^could not locate the server entry point$/i,\n message: 'Could not locate the MCP server entry point',\n body:\n \"This project has MCP signals, but the agent couldn't find where the \" +\n \"server is constructed or requests are dispatched, so there's nowhere \" +\n 'safe to add instrumentation. See https://posthog.com/docs/mcp-analytics ' +\n 'for the supported server styles, or point the wizard at the package ' +\n \"that defines the server if it's in a monorepo subdirectory.\",\n },\n];\n\n/**\n * `wizard mcp-analytics` — flat skill command.\n *\n * Instruments the user's own MCP server with the `@posthog/mcp` SDK so it\n * reports `$mcp_*` analytics about itself. This is the opposite of\n * `wizard mcp add` (which installs the PostHog MCP *server* into a coding\n * agent) — keep the two distinct.\n *\n * Flat while instrumenting is the only action. If an uninstrument / `remove`\n * leaf ever lands, restructure into a family with `familyCommandFactory` and\n * publish each leaf as a `cliEntries` entry with `parentCommand:\n * 'mcp-analytics'` from context-mill — a deliberate breaking change, done then,\n * not pre-emptively.\n */\nexport const mcpAnalyticsConfig = createSkillProgram({\n skillId: 'mcp-analytics',\n command: 'mcp-analytics',\n id: 'mcp-analytics',\n description: 'Add PostHog MCP Analytics to your MCP server',\n integrationLabel: 'mcp-analytics',\n customPrompt:\n \"Instrument this project's MCP server with PostHog MCP analytics. Run the \" +\n '`mcp-analytics` skill end-to-end: detect the server style, install ' +\n '`@posthog/mcp` and `posthog-node`, wrap the server (or use `PostHogMCP` ' +\n 'for a custom dispatcher), wire the project API key and host, and verify. ' +\n 'Make only additive changes — do not alter tool behavior. The final report ' +\n `is written to ./${MCP_ANALYTICS_REPORT_FILE}.`,\n successMessage: `MCP analytics configured! View the report at ./${MCP_ANALYTICS_REPORT_FILE}`,\n reportFile: MCP_ANALYTICS_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/mcp-analytics',\n spinnerMessage: 'Setting up MCP analytics...',\n estimatedDurationMinutes: 5,\n abortCases: MCP_ANALYTICS_ABORT_CASES,\n});\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/index';\nimport { getContentBlocks } from '@ui/tui/decks/agent-skill/index';\nimport { headlessOption, regionOption } from '@lib/headless-mode';\n\nconst AI_OBSERVABILITY_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.map((step) =>\n step.id === 'intro' ? { ...step, screenId: 'ai-observability-intro' } : step,\n);\n\nconst AI_OBSERVABILITY_REPORT_FILE = 'posthog-ai-observability-report.md';\n\n/**\n * `wizard ai-observability` — wrap the project's LLM client calls so they emit\n * `$ai_generation` events into AI Observability.\n *\n * No `run.skillId`: the context-mill `ai-observability` group ships one variant\n * per (LLM provider × language) and the wizard does no provider detection —\n * the agent loads the menu, matches the manifest's vendor SDK, and installs\n * the right variant itself (see `customPrompt`). Stays flat while a single\n * \"add AIO to a project\" flow is the only action.\n */\nexport const aiObservabilityConfig: ProgramConfig = {\n command: 'ai-observability',\n description: 'Add PostHog AI Observability to your LLM calls',\n id: 'ai-observability',\n cliOptions: { ...headlessOption, ...regionOption },\n steps: AI_OBSERVABILITY_STEPS,\n reportFile: AI_OBSERVABILITY_REPORT_FILE,\n getContentBlocks,\n run: {\n integrationLabel: 'ai-observability',\n // No `skillId`: linear.ts skips its pre-install step (see the gate on\n // `linear.ts:47`), so the agent must load the menu and install the right\n // variant itself. The prompt below tells it how.\n customPrompt:\n () => `Instrument this project's LLM calls with PostHog AI Observability.\n\nThis flow has no pre-installed skill — you install the right one yourself:\n\n1. Call \\`load_skill_menu\\` with \\`category: \"ai-observability\"\\`. The menu is\n the source of truth: one variant per (LLM provider × language), plus a\n \\`manual-capture\\` variant for projects with no vendor SDK.\n\n2. Scan the project manifest (\\`package.json\\`, \\`pyproject.toml\\`,\n \\`requirements.txt\\`) for a vendor LLM SDK and pick the variant that matches\n it — the language follows the manifest (\\`package.json\\` → Node, Python\n tooling → Python). Multiple SDKs (e.g. LangChain wrapping OpenAI) → prefer\n the higher abstraction. No vendor SDK → the \\`manual-capture\\` variant.\n Genuinely ambiguous → \\`wizard_ask\\` with a multi-choice picker.\n\n3. Call \\`install_skill\\` with the picked variant id. Then follow that skill's\n \\`SKILL.md\\` and references end-to-end. The skill itself will install\n packages, wire OTel, set env vars, and describe verification.\n\nMake only additive changes — do not touch existing PostHog init, identify\ncalls, event capture, or dashboards. Those belong to other skills. The\nfinal report is written to ./${AI_OBSERVABILITY_REPORT_FILE}.`,\n successMessage: `AI Observability configured! View the report at ./${AI_OBSERVABILITY_REPORT_FILE}`,\n reportFile: AI_OBSERVABILITY_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/ai-observability',\n spinnerMessage: 'Setting up AI Observability...',\n estimatedDurationMinutes: 5,\n },\n};\n","import type { ProgramConfig, ProgramStep } from '@programs/program-step';\nimport { AGENT_SKILL_STEPS } from '@programs/agent-skill/index';\nimport { getContentBlocks } from '@ui/tui/decks/agent-skill/index';\n\nconst METRICS_STEPS: ProgramStep[] = AGENT_SKILL_STEPS.map((step) =>\n step.id === 'intro' ? { ...step, screenId: 'metrics-intro' } : step,\n);\n\nconst METRICS_REPORT_FILE = 'posthog-metrics-report.md';\n\n/**\n * `wizard metrics` — instrument the project with PostHog application metrics\n * (`posthog.metrics` counters, gauges, and histograms).\n *\n * No `run.skillId`: the context-mill `metrics` group ships one variant per\n * platform (python, nodejs, javascript, kubernetes, other/OTLP) and the agent\n * pulls the matching one itself — the flow's tasks and the linear\n * `customPrompt` both pick from the menu and install it. Stays flat while a\n * single \"add metrics to a project\" flow is the only action.\n */\nexport const metricsConfig: ProgramConfig = {\n command: 'metrics',\n description: 'Add PostHog application metrics to your project',\n id: 'metrics',\n // Orchestrator flow (context-mill `context/agents/metrics`): the seed queues\n // verify-sdk → instrument-metrics → report; the tasks install the matching\n // platform variant themselves. Explicit so renaming the program can't\n // silently detach the flow.\n agentFlow: 'metrics',\n steps: METRICS_STEPS,\n reportFile: METRICS_REPORT_FILE,\n getContentBlocks,\n run: {\n integrationLabel: 'metrics',\n // No `skillId`: the agent must load the menu and install the right\n // variant itself. The prompt below tells it how.\n customPrompt:\n () => `Instrument this project with PostHog application metrics.\n\nThis flow has no pre-installed skill — you install the right one yourself:\n\n1. Call \\`load_skill_menu\\` with \\`category: \"metrics\"\\`. The menu is the\n source of truth: one variant per platform.\n\n2. Pick the variant that matches the project:\n - Python tooling (\\`pyproject.toml\\`, \\`requirements.txt\\`, \\`Pipfile\\`) →\n \\`metrics-python\\` (needs \\`posthog\\` >= 7.23.0)\n - \\`package.json\\` with server-side Node code → \\`metrics-nodejs\\`\n (needs \\`posthog-node\\` >= 5.43.0)\n - \\`package.json\\` that is browser-only → \\`metrics-javascript\\`\n (needs \\`posthog-js\\` >= 1.399.0)\n - Kubernetes manifests / Helm charts and the user wants cluster-level\n scraping → \\`metrics-kubernetes\\`\n - Any other language → \\`metrics-other\\` (plain OTLP exporter)\n A full-stack app (e.g. Next.js) usually wants the server variant — metrics\n measure service work, not user actions. Genuinely ambiguous →\n \\`wizard_ask\\` with a multi-choice picker.\n\n3. Call \\`install_skill\\` with the picked variant id. Then follow that skill's\n \\`SKILL.md\\` and references end-to-end — it covers where to place metrics\n (middleware, background jobs, external calls, business commit sites) and\n the low-cardinality attribute rules.\n\nMake only additive changes — reuse an existing PostHog client by adding the\n\\`metrics\\` config to it rather than constructing a second client, and do not\ntouch existing identify calls, event capture, or dashboards. The final report\nis written to ./${METRICS_REPORT_FILE}.`,\n successMessage: `Application metrics configured! View the report at ./${METRICS_REPORT_FILE}`,\n reportFile: METRICS_REPORT_FILE,\n docsUrl: 'https://posthog.com/docs/metrics',\n spinnerMessage: 'Setting up application metrics...',\n estimatedDurationMinutes: 5,\n },\n};\n","/**\n * Slack connect program — TUI-only flow invoked by `wizard slack`. One\n * step: the same Connect Slack screen the MCP flows end on. The screen\n * renders the no-creds nudge (marketing copy + \"Open Slack setup\" link);\n * we deliberately don't force OAuth here because connecting Slack itself\n * happens in the browser, so a wizard login adds nothing for the user.\n */\n\nimport type { ProgramConfig } from '@programs/program-step';\n\nexport const slackConnectConfig: ProgramConfig = {\n id: 'slack',\n description: 'Connect PostHog to your Slack',\n steps: [\n {\n id: 'slack-connect',\n label: 'Connect Slack',\n screenId: 'slack-connect',\n isComplete: (s) => s.slackStepDismissed,\n },\n ],\n};\n","/**\n * Central registry of all wizard programs.\n *\n * Adding a new program:\n * 1. Create src/programs/<name>/ with index.ts exporting a ProgramConfig\n * 2. Import and add it to PROGRAM_REGISTRY below\n * 3. (If custom intro screen) add to src/ui/tui/screen-registry.tsx\n *\n * screen-sequences.ts, store.ts, and bin.ts all derive their wiring from\n * this array — no need to touch those files when adding a program.\n */\n\nimport type { ProgramConfig } from './program-step.js';\nimport { POSTHOG_DOCS_URL } from '@shared/constants.js';\nimport { posthogIntegrationConfig } from './posthog-integration/index.js';\nimport { revenueAnalyticsConfig } from './revenue-analytics/index.js';\nimport { warehouseSourceConfig } from './warehouse-source/index.js';\nimport { auditConfig } from './audit/index.js';\nimport { eventsAuditConfig } from './events-audit/index.js';\nimport { posthogDoctorConfig } from './posthog-doctor/index.js';\nimport { webAnalyticsDoctorConfig } from './web-analytics-doctor/index.js';\nimport { migrationConfig } from './migration/index.js';\nimport { errorTrackingUploadSourceMapsConfig } from './error-tracking-upload-source-maps/index.js';\nimport { errorTrackingConfig } from './error-tracking/index.js';\nimport { selfDrivingConfig } from './self-driving/index.js';\nimport { AGENT_SKILL_STEPS } from './agent-skill/index.js';\nimport { getContentBlocks as agentSkillContentBlocks } from '../ui/tui/decks/agent-skill/index.js';\nimport {\n mcpAddConfig,\n mcpRemoveConfig,\n mcpTutorialConfig,\n} from './mcp/index.js';\nimport { mcpAnalyticsConfig } from './mcp-analytics/index.js';\nimport { replayVisionConfig } from './replay-vision/index.js';\nimport { aiObservabilityConfig } from './ai-observability/index.js';\nimport { metricsConfig } from './metrics/index.js';\nimport { slackConnectConfig } from './slack/index.js';\n\n// Generic skill program — runs an arbitrary context-mill skill chosen at\n// dispatch time (session.skillId) rather than a registered named program.\n// Backs `wizard skill <name>` and the narrow `audit` leaves (events,\n// feature-flags, identify, session-replay, autocapture); each injects its\n// skillId onto the config, which lands on session.skillId before the run.\n//\n// The `run` recipe is a function rather than a static block because the\n// skillId isn't known until dispatch. Without a `run` recipe the runner's\n// `skipAgent` guard (run-wizard.ts) fires and the skill never executes — so we\n// derive generic run metadata from the resolved skill id at run time.\nexport const agentSkillConfig: ProgramConfig = {\n id: 'agent-skill',\n description: 'Run an arbitrary context-mill skill',\n steps: AGENT_SKILL_STEPS,\n getContentBlocks: agentSkillContentBlocks,\n allowedTools: ['Agent'],\n run: (session) => {\n const skillId = session.skillId ?? 'agent-skill';\n return Promise.resolve({\n skillId,\n integrationLabel: skillId,\n spinnerMessage: `Running ${skillId}...`,\n successMessage: `${skillId} complete!`,\n estimatedDurationMinutes: 5,\n reportFile: `posthog-${skillId}-report.md`,\n docsUrl: POSTHOG_DOCS_URL,\n });\n },\n};\n\nexport const PROGRAM_REGISTRY = [\n posthogIntegrationConfig,\n revenueAnalyticsConfig,\n warehouseSourceConfig,\n errorTrackingUploadSourceMapsConfig,\n errorTrackingConfig,\n auditConfig,\n eventsAuditConfig,\n posthogDoctorConfig,\n webAnalyticsDoctorConfig,\n migrationConfig,\n selfDrivingConfig,\n agentSkillConfig,\n mcpAddConfig,\n mcpRemoveConfig,\n mcpTutorialConfig,\n mcpAnalyticsConfig,\n replayVisionConfig,\n aiObservabilityConfig,\n metricsConfig,\n slackConnectConfig,\n] as const satisfies readonly ProgramConfig[];\n\n/**\n * Typed program names. Values come from each config's `id`, so there's\n * no parallel string list to keep in sync — adding `Program.Foo` here is\n * just exposing `fooConfig.id` under a friendly name for call sites.\n */\nexport const Program = {\n PostHogIntegration: posthogIntegrationConfig.id,\n RevenueAnalyticsSetup: revenueAnalyticsConfig.id,\n WarehouseSource: warehouseSourceConfig.id,\n ErrorTrackingUploadSourceMaps: errorTrackingUploadSourceMapsConfig.id,\n ErrorTracking: errorTrackingConfig.id,\n Migration: migrationConfig.id,\n Audit: auditConfig.id,\n EventsAudit: eventsAuditConfig.id,\n PosthogDoctor: posthogDoctorConfig.id,\n WebAnalyticsDoctor: webAnalyticsDoctorConfig.id,\n SelfDriving: selfDrivingConfig.id,\n AgentSkill: agentSkillConfig.id,\n McpAdd: mcpAddConfig.id,\n McpRemove: mcpRemoveConfig.id,\n McpTutorial: mcpTutorialConfig.id,\n McpAnalytics: mcpAnalyticsConfig.id,\n ReplayVision: replayVisionConfig.id,\n AiObservability: aiObservabilityConfig.id,\n Metrics: metricsConfig.id,\n SlackConnect: slackConnectConfig.id,\n} as const;\n\n/** Compile-time union of every registered program id. */\nexport type ProgramId = (typeof PROGRAM_REGISTRY)[number]['id'];\n\n/**\n * Look up a program config by its id. `ProgramId` is a union of every\n * registered id, so the lookup is statically guaranteed to find a match\n * — the `!` is a load-bearing assertion of that invariant, not a hope.\n */\nexport function getProgramConfig(id: ProgramId): ProgramConfig {\n return PROGRAM_REGISTRY.find((c) => c.id === id)!;\n}\n\n/** A program config that is exposed as a CLI subcommand. */\nexport type SubcommandProgram = ProgramConfig & { command: string };\n\n/** All program configs that are exposed as CLI subcommands. */\nexport function getSubcommandPrograms(): SubcommandProgram[] {\n return PROGRAM_REGISTRY.filter(\n (c): c is SubcommandProgram => c.command != null,\n );\n}\n\n/** What a user types to reach the program. Nested ones go through its parent. */\nexport function getCommandPath(config: SubcommandProgram): string {\n return config.parentCommand\n ? `${config.parentCommand} ${config.command}`\n : config.command;\n}\n\n/** What the intro offers, in order. Curated: no config field ranks these. */\nconst INTRO_PROGRAMS = [\n 'self-driving',\n 'error-tracking-upload-source-maps',\n 'warehouse-source',\n 'audit',\n 'posthog-doctor',\n 'mcp-analytics',\n 'replay-vision',\n 'ai-observability',\n 'metrics',\n 'revenue-analytics-setup',\n];\n\n/** The programs the intro can hand off to, in the order it lists them. */\nexport function getLaunchablePrograms(): SubcommandProgram[] {\n const byId = new Map(getSubcommandPrograms().map((c) => [c.id, c]));\n return INTRO_PROGRAMS.map((id) => byId.get(id)).filter(\n (config): config is SubcommandProgram => config != null,\n );\n}\n","import { OutroKind, type WizardSession } from '@lib/wizard-session';\n\nexport const MINT_FAILURE_MESSAGE = \"The Wizard's a little busy\";\n\nexport const MINT_FAILURE_BODY =\n 'The wizard can still share spells. Grab a skill and let your agent take over.';\n\nexport const MINT_FAILURE_CONTACT =\n 'Email wizard@posthog.com and tell us what happened. Please attach this log:';\n\n/** The agent run ended in an error, whatever the reason. Login failures also\n * set an error outro but never had credentials, so they stay on the outro. */\nexport function isRunFailure(\n session: Pick<WizardSession, 'outroData' | 'credentials'>,\n): boolean {\n return (\n session.outroData?.kind === OutroKind.Error && session.credentials !== null\n );\n}\n"],"mappings":"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GACA,mBAAmB;GACpB;EAED;GAAE,SAAS;GAA2B,OAAO;GAAM;EAEnD;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,OAAO;GACP,SAAS;GACT,SAAS,oBAAC,mBAAD,EAA0B,OAAS,CAAA;GAC7C;EAED;GACE,OAAO;GACP,SACE,qBAAC,MAAD,EAAA,UAAA;IAAM;IACE;IACN,oBAAC,MAAD;KAAM,OAAO,OAAO;KAAQ,MAAA;eAAK;KAE1B,CAAA;IAAC;IAAI;IAEP,EAAA,CAAA;GAEV;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SACE;GACF,OAAO;GACR;EAED;GAAE,SAAS;GAA8C,OAAO;GAAM;EAEtE;GAAE,SAAS;GAA2C,OAAO;GAAM;EAEnE;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GAAE,SAAS;GAAgD,OAAO;GAAM;EAExE;EAGA;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SACE;GACF,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAEA;GAAE,SAAS;GAA0B,OAAO;GAAM;EAElD;EAEA;EAGA;GAAE,SAAS;GAA4C,OAAO;GAAM;EAEpE;GACE,SAAS;GACT,OAAO;GACR;EAED;GAGE,SAAS;GACT,OAAO;GACR;EAED;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GAAE,SAAS;GAA6C,OAAO;GAAM;EAErE;GACE,SAAS;GACT,OAAO;GACR;EAED;EAGA;GACE,SAAS;GACT,OAAO;GACR;EAED;GACE,MAAM;GACN,UAAU;GACV,OAAO;GACP,OAAO;IACL,qBAAC,MAAD,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"}