@posthog/wizard 2.83.0 → 2.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +1 -29
  2. package/dist/{PickerMenu-BGGeYqd-.js → PickerMenu-BtheKi1o.js} +1 -1
  3. package/dist/{PickerMenu-BGGeYqd-.js.map → PickerMenu-BtheKi1o.js.map} +1 -1
  4. package/dist/_dev-only-stub-B0db0Klc.js +1 -0
  5. package/dist/{agent-interface-fcDXqb-_.js → agent-interface-BNssNetq.js} +12 -12
  6. package/dist/agent-interface-BNssNetq.js.map +1 -0
  7. package/dist/{agent-prompt-loader-F9gGPMt0.js → agent-prompt-loader-iV11XVEF.js} +16 -8
  8. package/dist/agent-prompt-loader-iV11XVEF.js.map +1 -0
  9. package/dist/{analytics-GQv--mQm.js → analytics-Drox5LSW.js} +6 -5
  10. package/dist/analytics-Drox5LSW.js.map +1 -0
  11. package/dist/analytics-Ix9HEra3.js +2 -0
  12. package/dist/{api-WUOSLXPJ.js → api-Dfu655Qt.js} +2 -2
  13. package/dist/{api-WUOSLXPJ.js.map → api-Dfu655Qt.js.map} +1 -1
  14. package/dist/api-H2UaPgjv.js +2 -0
  15. package/dist/{api-key-login-tysMeZnR.js → api-key-login-BgwfAXfv.js} +5 -5
  16. package/dist/{api-key-login-tysMeZnR.js.map → api-key-login-BgwfAXfv.js.map} +1 -1
  17. package/dist/api-key-login-Cow3UDAN.js +2 -0
  18. package/dist/bin.js +1 -1
  19. package/dist/{bounded-fs-B31zq2ir.js → bounded-fs-TRL6IsoU.js} +32 -16
  20. package/dist/bounded-fs-TRL6IsoU.js.map +1 -0
  21. package/dist/{ci-install-CYSFbFXd.js → ci-install-nc1IKuX2.js} +6 -6
  22. package/dist/{ci-install-CYSFbFXd.js.map → ci-install-nc1IKuX2.js.map} +1 -1
  23. package/dist/{console-log-DJiTqYT2.js → console-log-BMhBoX-o.js} +2 -2
  24. package/dist/{console-log-DJiTqYT2.js.map → console-log-BMhBoX-o.js.map} +1 -1
  25. package/dist/{debug-BTUMatFI.js → debug-BfO_Txo8.js} +3 -3
  26. package/dist/debug-BfO_Txo8.js.map +1 -0
  27. package/dist/{defaults-DDVl3tKN.js → defaults-BWLIffh5.js} +1 -1
  28. package/dist/{defaults-DDVl3tKN.js.map → defaults-BWLIffh5.js.map} +1 -1
  29. package/dist/{errors-DhFeR8Sp.js → errors-BXlWujEw.js} +1 -1
  30. package/dist/{errors-DhFeR8Sp.js.map → errors-BXlWujEw.js.map} +1 -1
  31. package/dist/{family-picker-BKs-tTyu.js → family-picker-CAW_VtYf.js} +2 -2
  32. package/dist/{family-picker-BKs-tTyu.js.map → family-picker-CAW_VtYf.js.map} +1 -1
  33. package/dist/{gateway-CJ8Jqbll.js → gateway-CLQQb96F.js} +25 -9
  34. package/dist/{gateway-CJ8Jqbll.js.map → gateway-CLQQb96F.js.map} +1 -1
  35. package/dist/headless-CHGNEzO3.js +3 -0
  36. package/dist/{headless-le-QqOuj.js → headless-DMO8N_FD.js} +2 -2
  37. package/dist/{headless-le-QqOuj.js.map → headless-DMO8N_FD.js.map} +1 -1
  38. package/dist/{host-resolution-Cht8bL39.js → host-resolution-DftfSjJD.js} +4 -4
  39. package/dist/{host-resolution-Cht8bL39.js.map → host-resolution-DftfSjJD.js.map} +1 -1
  40. package/dist/{install-BhnFP3p3.js → install-C9FPM37m.js} +6 -6
  41. package/dist/{install-BhnFP3p3.js.map → install-C9FPM37m.js.map} +1 -1
  42. package/dist/{install-55JbRsO-.js → install-MWoo1hXL.js} +1 -1
  43. package/dist/{interactive-9qRL8259.js → interactive-DOnolJHB.js} +3 -3
  44. package/dist/{interactive-9qRL8259.js.map → interactive-DOnolJHB.js.map} +1 -1
  45. package/dist/{links-5Blqk4Im.js → links-CatXPRxH.js} +3 -3
  46. package/dist/{links-5Blqk4Im.js.map → links-CatXPRxH.js.map} +1 -1
  47. package/dist/{main-CUIcozEG.js → main-CINmZtoW.js} +23 -23
  48. package/dist/{main-CUIcozEG.js.map → main-CINmZtoW.js.map} +1 -1
  49. package/dist/{mcp-D73aCOHK.js → mcp-KVV6Z_bq.js} +3 -3
  50. package/dist/{mcp-D73aCOHK.js.map → mcp-KVV6Z_bq.js.map} +1 -1
  51. package/dist/{mcp-prompt-streaming-B5lIQ0l1.js → mcp-prompt-streaming-M6UnLgGX.js} +5 -5
  52. package/dist/{mcp-prompt-streaming-B5lIQ0l1.js.map → mcp-prompt-streaming-M6UnLgGX.js.map} +1 -1
  53. package/dist/{mode-label-DB3ol_sj.js → mode-label--bJuXny5.js} +1 -1
  54. package/dist/{mode-label-DB3ol_sj.js.map → mode-label--bJuXny5.js.map} +1 -1
  55. package/dist/{models-CLTglHFZ.js → models-B0huHItG.js} +2 -2
  56. package/dist/{models-CLTglHFZ.js.map → models-B0huHItG.js.map} +1 -1
  57. package/dist/{non-interactive-DkldwWDM.js → non-interactive-DiEu4_6l.js} +3 -3
  58. package/dist/{non-interactive-DkldwWDM.js.map → non-interactive-DiEu4_6l.js.map} +1 -1
  59. package/dist/{oauth-session-CfkZ339p.js → oauth-session-9EU3ky0v.js} +2 -2
  60. package/dist/{oauth-session-CfkZ339p.js.map → oauth-session-9EU3ky0v.js.map} +1 -1
  61. package/dist/{orchestrator-tools-lvNVQhzs.js → orchestrator-tools-QWyDLu_7.js} +3 -3
  62. package/dist/{orchestrator-tools-lvNVQhzs.js.map → orchestrator-tools-QWyDLu_7.js.map} +1 -1
  63. package/dist/package-json-BAtJRHSq.js +2 -0
  64. package/dist/{package-json-2UrhE6kP.js → package-json-z1agAYXs.js} +1 -1
  65. package/dist/{package-json-2UrhE6kP.js.map → package-json-z1agAYXs.js.map} +1 -1
  66. package/dist/{package-manager-BqRNp5y_.js → package-manager-B-9I_fAT.js} +4 -4
  67. package/dist/{package-manager-BqRNp5y_.js.map → package-manager-B-9I_fAT.js.map} +1 -1
  68. package/dist/{pi-2pYz2OY3.js → pi-BSnSk6rv.js} +19 -20
  69. package/dist/pi-BSnSk6rv.js.map +1 -0
  70. package/dist/{playground-jxZNxRwC.js → playground-Drz1qszs.js} +4 -4
  71. package/dist/{playground-jxZNxRwC.js.map → playground-Drz1qszs.js.map} +1 -1
  72. package/dist/{posthog-integration-DxRkw5DI.js → posthog-integration-mIQGBc1f.js} +882 -2512
  73. package/dist/posthog-integration-mIQGBc1f.js.map +1 -0
  74. package/dist/programs-Bv0_YHzE.js +3 -0
  75. package/dist/{programs-eTv2T2rs.js → programs-mxhRXwP-.js} +194 -88
  76. package/dist/programs-mxhRXwP-.js.map +1 -0
  77. package/dist/provisioning-BVsxSNve.js +2 -0
  78. package/dist/{provisioning-c5JJ30KX.js → provisioning-CHUjae9V.js} +4 -4
  79. package/dist/{provisioning-c5JJ30KX.js.map → provisioning-CHUjae9V.js.map} +1 -1
  80. package/dist/{queue-tools-10w8lhEs.js → queue-tools-PFG6H93Q.js} +4 -4
  81. package/dist/{queue-tools-10w8lhEs.js.map → queue-tools-PFG6H93Q.js.map} +1 -1
  82. package/dist/{results-C45prSKD.js → results-5yCKf2cI.js} +2 -2
  83. package/dist/{results-C45prSKD.js.map → results-5yCKf2cI.js.map} +1 -1
  84. package/dist/{run-jugssomh.js → run-D5TKJuSL.js} +10 -10
  85. package/dist/{run-jugssomh.js.map → run-D5TKJuSL.js.map} +1 -1
  86. package/dist/{run-CM24OgPF.js → run-W03b6jNM.js} +12 -12
  87. package/dist/{run-CM24OgPF.js.map → run-W03b6jNM.js.map} +1 -1
  88. package/dist/{run-tool-DZrZFoGq.js → run-tool-DTQF8mmI.js} +17 -17
  89. package/dist/{run-tool-DZrZFoGq.js.map → run-tool-DTQF8mmI.js.map} +1 -1
  90. package/dist/run-tool-DjERhu7G.js +2 -0
  91. package/dist/{runners-CoIYt0iI.js → runners-B_84kPyf.js} +11 -11
  92. package/dist/{runners-CoIYt0iI.js.map → runners-B_84kPyf.js.map} +1 -1
  93. package/dist/{security-ppK2MwCn.js → security-pApVawYb.js} +5 -5
  94. package/dist/{security-ppK2MwCn.js.map → security-pApVawYb.js.map} +1 -1
  95. package/dist/{setup-utils-B5E1rXLa.js → setup-utils-CQY8PHjR.js} +3 -3
  96. package/dist/{setup-utils-B5E1rXLa.js.map → setup-utils-CQY8PHjR.js.map} +1 -1
  97. package/dist/{start-playground-DujiUNFT.js → start-playground-DAw1GbLt.js} +8 -8
  98. package/dist/{start-playground-DujiUNFT.js.map → start-playground-DAw1GbLt.js.map} +1 -1
  99. package/dist/{subagent-BdDWA8j5.js → subagent-YLGSvgoZ.js} +4 -4
  100. package/dist/subagent-YLGSvgoZ.js.map +1 -0
  101. package/dist/{task-Y8lWoDE7.js → task-3hRSovmK.js} +19 -20
  102. package/dist/task-3hRSovmK.js.map +1 -0
  103. package/dist/{tasks-BfJhVky2.js → tasks-C03QbXTx.js} +1 -1
  104. package/dist/{tasks-BfJhVky2.js.map → tasks-C03QbXTx.js.map} +1 -1
  105. package/dist/{telemetry-Cwqf0iur.js → telemetry-BPBzqsb_.js} +2 -2
  106. package/dist/{telemetry-Cwqf0iur.js.map → telemetry-BPBzqsb_.js.map} +1 -1
  107. package/dist/{terminal-BLL-X6YP.js → terminal-DZsYFbm9.js} +92 -63
  108. package/dist/terminal-DZsYFbm9.js.map +1 -0
  109. package/dist/{tools-C8k90ISV.js → tools-D_AtrhbO.js} +15 -15
  110. package/dist/{tools-C8k90ISV.js.map → tools-D_AtrhbO.js.map} +1 -1
  111. package/dist/{tools-g42e5kJz.js → tools-DyzRi_dQ.js} +8 -8
  112. package/dist/{tools-g42e5kJz.js.map → tools-DyzRi_dQ.js.map} +1 -1
  113. package/dist/{tui-CBnRWnrm.js → tui-CRp0iCuT.js} +5 -5
  114. package/dist/{tui-CBnRWnrm.js.map → tui-CRp0iCuT.js.map} +1 -1
  115. package/dist/{tui-BPWJ-J-k.js → tui-UI6ctBQJ.js} +1 -1
  116. package/dist/{upload-step-CFoxwd5t.js → upload-step-DrYXnpzI.js} +3 -3
  117. package/dist/{upload-step-CFoxwd5t.js.map → upload-step-DrYXnpzI.js.map} +1 -1
  118. package/dist/{urls-BTZhm5eA.js → urls-WeqfBLP4.js} +2 -2
  119. package/dist/{urls-BTZhm5eA.js.map → urls-WeqfBLP4.js.map} +1 -1
  120. package/dist/{wizard-abort-HtpiSsQO.js → wizard-abort-BwEefm6P.js} +5 -5
  121. package/dist/{wizard-abort-HtpiSsQO.js.map → wizard-abort-BwEefm6P.js.map} +1 -1
  122. package/dist/yara-hooks-MDtFbuoM.js +2 -0
  123. package/dist/{yara-hooks-BPYhN9WM.js → yara-hooks-aDbdsitJ.js} +41 -5
  124. package/dist/{yara-hooks-BPYhN9WM.js.map → yara-hooks-aDbdsitJ.js.map} +1 -1
  125. package/package.json +9 -7
  126. package/dist/agent-interface-fcDXqb-_.js.map +0 -1
  127. package/dist/agent-prompt-loader-F9gGPMt0.js.map +0 -1
  128. package/dist/analytics-C9QnGvuc.js +0 -2
  129. package/dist/analytics-GQv--mQm.js.map +0 -1
  130. package/dist/api-CsXfZKkq.js +0 -2
  131. package/dist/api-key-login-CDSxsYtP.js +0 -2
  132. package/dist/bounded-fs-B31zq2ir.js.map +0 -1
  133. package/dist/debug-BTUMatFI.js.map +0 -1
  134. package/dist/headless-DZ3mq07K.js +0 -3
  135. package/dist/package-json-Dblip2_X.js +0 -2
  136. package/dist/pi-2pYz2OY3.js.map +0 -1
  137. package/dist/posthog-integration-DxRkw5DI.js.map +0 -1
  138. package/dist/programs-D2793-PK.js +0 -3
  139. package/dist/programs-eTv2T2rs.js.map +0 -1
  140. package/dist/provisioning-CtQ0GEHa.js +0 -2
  141. package/dist/run-tool-BZFgFb_2.js +0 -2
  142. package/dist/subagent-BdDWA8j5.js.map +0 -1
  143. package/dist/task-Y8lWoDE7.js.map +0 -1
  144. package/dist/terminal-BLL-X6YP.js.map +0 -1
  145. package/dist/yara-hooks-BwhEDXIt.js +0 -2
@@ -1 +0,0 @@
1
- {"version":3,"file":"programs-eTv2T2rs.js","names":["fs","path","config","abortUnsupportedPlatform","config","config","config","DOCS_URL","config","SETUP_REPORT_FILE","fs","audit","eventsAudit","REPORT_FILE","DOCS_URL","config","config","config","config","REPORT_FILE","DOCS_URL","config","VARIANT_DISPLAY_NAME","config","abortUnsupportedPlatform","config","config","auditPrograms","posthogIntegration","revenueAnalytics","warehouseSource","sourceMaps","errorTracking","featureFlags","webAnalyticsDoctor","migration","selfDriving","agentSkill","mcpAnalytics","replayVision","aiObservability","metrics","randomUUID","writeFileSync","appendFileSync","isAbsolute","resolve","isUUID","resolve","validVersion"],"sources":["../src/shared/headless-mode.ts","../src/shared/install-cli-steering/index.ts","../src/programs/shared/skill-program.ts","../src/programs/mcp-analytics/index.ts","../src/programs/replay-vision/scopes.ts","../src/programs/replay-vision/index.ts","../src/programs/ai-observability/index.ts","../src/programs/metrics/index.ts","../src/shared/health-checks/readiness.ts","../src/programs/audit/detect.ts","../src/programs/session/audit-checks.ts","../src/programs/audit/seed.ts","../src/programs/audit/events/seed.ts","../src/programs/audit/events/config.ts","../src/shared/utils/file-watcher.ts","../src/programs/audit/ledger-watcher.ts","../src/programs/audit/index.ts","../src/shared/oauth-scopes.ts","../src/programs/web-analytics-doctor/detect.ts","../src/programs/web-analytics-doctor/index.ts","../src/programs/migration/index.ts","../src/programs/revenue-analytics/detect.ts","../src/programs/revenue-analytics/index.ts","../src/programs/warehouse-source/detect.ts","../src/programs/warehouse-source/index.ts","../src/programs/self-driving/detect.ts","../src/programs/self-driving/prompt.ts","../src/programs/self-driving/step-keys.ts","../src/programs/self-driving/detect-agentic.ts","../src/programs/self-driving/pricing.ts","../src/programs/self-driving/scopes.ts","../src/programs/self-driving/index.ts","../src/programs/error-tracking-upload-source-maps/prompt.ts","../src/programs/error-tracking-upload-source-maps/detect.ts","../src/programs/shared/posthog-cli-preinstall.ts","../src/programs/error-tracking-upload-source-maps/detect-agentic.ts","../src/programs/error-tracking-upload-source-maps/index.ts","../src/programs/error-tracking/detect-agentic.ts","../src/programs/error-tracking/index.ts","../src/programs/feature-flags/scopes.ts","../src/programs/feature-flags/index.ts","../src/programs/agent-skill/scopes.ts","../src/programs/agent-skill/index.ts","../src/programs/program-registry.ts","../src/programs/detect-map.ts","../src/programs/detect-program.ts","../src/shared/utils/oauth-errors.ts","../src/programs/oauth/tokens.ts","../src/programs/credentials.ts","../src/programs/login.ts","../src/shared/task-status.ts","../src/programs/session/session-store.ts","../src/programs/run-program.ts","../src/programs/session/wizard-session.ts","../src/programs/session/interaction.ts","../src/programs/session/task-stream/destinations/file.ts","../src/programs/session/task-stream/destinations/posthog.ts","../src/programs/session/task-stream/wizard-run-sync.ts","../src/programs/session/task-stream/event-plan-watcher.ts","../src/programs/session/task-stream/audit-areas.ts","../src/programs/session/task-stream/task-stream-push.ts","../src/programs/api-key-login.ts"],"sourcesContent":["/**\n * Headless mode — the published-build, non-interactive run path.\n *\n * The headless flag and `--ci` both drive a non-interactive install\n * (session.ci === true), but through dedicated entry points so they can\n * diverge: runHeadlessInstall → runWizardHeadless for headless,\n * runCIInstall → runWizardCI for `--ci`. Both delegate to one shared pipeline\n * (runNonInteractive) today; the only forks are the api-key prefixes accepted\n * (ci-install) and the analytics build tag (the mode passed to the runner).\n *\n * The flag is deliberately named `--headless-DONOTUSE-EXPERIMENTAL` and hidden\n * from `--help`: the contract is still unstable and subject to breaking\n * changes, so it must not be advertised or relied on by external callers. This\n * module is the single source of truth for the flag's name and detection — keep\n * the scary name out of every other file so a future rename is one edit here.\n */\n\nimport type { Options } from 'yargs';\n\nimport { HEADLESS_FLAG } from '@env';\n\n/**\n * The yargs option declaration for the headless flag. Commands opt in so that\n * unsupported commands reject headless execution instead of starting it.\n */\nexport const headlessOption: Record<string, Options> = {\n [HEADLESS_FLAG]: {\n default: false,\n // EXPERIMENTAL + UNSTABLE: the non-interactive published-build run path.\n // Declared unconditionally (unlike --ci) so it works in the shipped\n // package, but hidden and intentionally ugly-named — the contract may\n // break without notice, so it must not be advertised.\n describe:\n 'EXPERIMENTAL — do not use. Unstable, subject to breaking changes.',\n type: 'boolean',\n hidden: true,\n },\n};\n\n/**\n * Read the headless signal off a parsed argv / options bag. yargs always sets\n * the value under the declared key (`HEADLESS_FLAG`), so reading that key is\n * reliable regardless of camel-case expansion.\n */\nexport function isHeadless(options: Record<string, unknown>): boolean {\n return options[HEADLESS_FLAG] === true;\n}\n\n// `--region` only means something non-interactively (API-key auth has no OAuth\n// token response to read `posthog_region` from), so only headless-capable\n// commands declare it.\nexport const regionOption: Record<string, Options> = {\n region: {\n describe: 'PostHog cloud region\\nenv: POSTHOG_WIZARD_REGION',\n choices: ['us', 'eu'],\n type: 'string',\n hidden: true,\n },\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 { logToFile } 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 logToFile(`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 logToFile(`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 * 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 '../program-step';\nimport type { AbortCase } from '@agent/types';\nimport type { ProgramRun } from '../program-run';\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 reportFile: opts.reportFile,\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","import type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { createSkillProgram } from '../shared/skill-program';\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`), Python ' +\n '(`posthog.mcp`, shipped inside the `posthog` package), Go ' +\n '(`posthogmcpsdk`, for servers built on the official go-sdk), and Ruby ' +\n '(`PostHog::MCP`, shipped inside the `posthog-ruby` gem, experimental). ' +\n \"This project doesn't look like any of them, so there's nothing to \" +\n '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: /^go mcp server is not built on the official go-sdk$/i,\n message: 'Go MCP server not built on the official go-sdk',\n body:\n 'The Go SDK instruments servers built on the official ' +\n '`github.com/modelcontextprotocol/go-sdk`. This server uses another ' +\n 'library, so there is no one-line setup. See ' +\n 'https://posthog.com/docs/mcp-analytics/installation/go for the supported setup.',\n },\n {\n match: /^go toolchain older than 1\\.25$/i,\n message: 'Go toolchain older than 1.25',\n body:\n '`posthogmcpsdk` needs Go 1.25 or later, and the installed toolchain ' +\n \"couldn't switch to a newer one. Upgrade Go, then run the command again.\",\n },\n {\n match: /^ruby mcp server is not built on the official mcp gem$/i,\n message: 'Ruby MCP server not built on the official mcp gem',\n body:\n 'The Ruby SDK instruments servers built on the official `mcp` gem, or ' +\n 'a custom dispatcher through `PostHog::MCP::Client`. This server uses ' +\n 'another library. See https://posthog.com/docs/mcp-analytics/installation/ruby ' +\n 'for the supported setups.',\n },\n {\n match: /^ruby older than 3\\.0$/i,\n message: 'Ruby older than 3.0',\n body:\n '`PostHog::MCP` needs Ruby 3.0 or later. Upgrade Ruby, then run the ' +\n 'command again.',\n },\n {\n match: /^ruby mcp gem older than 1\\.4$/i,\n message: 'Ruby mcp gem older than 1.4',\n body:\n '`PostHog::MCP.instrument` needs the `mcp` gem at 1.4 or later. The ' +\n \"wizard doesn't change your `mcp` version. Upgrade it, then run the \" +\n 'command again.',\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 for its\n * language (TypeScript, Python, Go, or Ruby) so it reports `$mcp_*` analytics about\n * itself. This is the opposite of `wizard mcp add` (which installs the\n * PostHog MCP *server* into a coding 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 config = 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 language and server ' +\n 'style, install the PostHog MCP SDK for that language (`@posthog/mcp` for ' +\n 'TypeScript, `posthog` for Python, `posthogmcpsdk` for Go, `posthog-ruby` ' +\n 'for Ruby), wrap the server ' +\n '(or use the custom-dispatcher client where the skill says so), wire the ' +\n 'project API key and host, and verify. ' +\n 'Make only additive changes — do not alter tool behavior. For a Ruby ' +\n 'server, the report must open by saying the Ruby SDK is experimental and ' +\n 'not officially supported. 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","/**\n * Extra scopes the replay-vision program needs on top of `WIZARD_OAUTH_SCOPES`.\n * The same set self-driving's step 6c uses, narrowed to just this flow:\n * • replay_scanner:read / replay_scanner:write — the scanner tasks list the\n * team's existing scanners and create the ones scoped to the product's key\n * flows (`vision-scanners-list` / `-create`, plus the advisory\n * `vision-scanners-estimate-create` / `vision-quota-retrieve`). The scope\n * OBJECT is `replay_scanner`; `vision-scanners-*` are MCP tool names.\n * • session_recording:read — the scanner API pairs it with\n * `replay_scanner:*`, since a scanner's config indirectly exposes\n * recording contents. Configuring a scanner fails without it.\n * • product_enablement:write — the enable-replay task's server half turns on\n * Session Replay (`products-enable`) so there are recordings to scan.\n *\n * Without these the PostHog MCP omits the tools from the catalog it serves\n * this token, every scanner task takes its \"tool unknown\" skip path, and the\n * run reports success having created nothing (run 69afc6f8).\n *\n * No OAuth-ceiling edit needed — all are unprivileged public scope objects\n * covered by the apps' `@default` sentinel, and self-driving already requests\n * every one of them.\n */\nexport const REPLAY_VISION_SCOPE_ADDITIONS = [\n 'session_recording:read',\n 'product_enablement:write',\n 'replay_scanner:read',\n 'replay_scanner:write',\n] as const;\n","import type { AbortCase } from '@agent/types';\nimport {\n Harness,\n Integration,\n Sequence,\n GPT6_SOL_MODEL,\n REPLAY_VISION_SUPPORTED,\n} from '@shared/constants';\nimport { detectFramework } from '../detection/framework';\nimport { gatherFrameworkContext } from '../detection/context';\nimport { noteDetectedFramework } from '../detection/detected-framework';\nimport { scopeInstallDirToProject } from '../detection/project-scope';\nimport type { CiRunnerContext } from '../runner-context';\nimport { FRAMEWORK_REGISTRY } from '../frameworks/registry';\nimport { createSkillProgram } from '../shared/skill-program';\nimport { detectPostHogIntegration } from '../detection/integration';\nimport type { ProgramConfig, ProgramReadyContext } from '../program-step';\nimport type { ProgramSession } from '../program-session';\nimport { analytics } from '@utils/analytics';\nimport { ProgramAbort } from '../program-abort';\nimport { ErrorCodes } from '@shared/errors';\nimport { REPLAY_VISION_SCOPE_ADDITIONS } from './scopes.js';\nimport { abortNoFrameworkDetected } from '../shared/abort-no-framework';\n\nconst REPLAY_VISION_REPORT_FILE = 'posthog-replay-vision-report.md';\n\nfunction abortUnsupportedPlatform(integration: Integration): never {\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.\n analytics.wizardCapture('replay-vision unsupported platform', {\n integration,\n });\n throw new ProgramAbort({\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\nconst NO_FRAMEWORK_GUIDANCE = {\n message: 'Could not find a Replay Vision compatible framework',\n docsLabel: 'Supported replay platforms:',\n docsUrl: 'https://posthog.com/docs/session-replay',\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 */\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).\nasync function detectReplayVisionProject(\n ctx: ProgramReadyContext,\n): Promise<void> {\n const integration = await detectFramework(ctx.session.installDir);\n if (!integration) {\n // Stop before skill preflight, matching the CI path below.\n abortNoFrameworkDetected(NO_FRAMEWORK_GUIDANCE);\n }\n if (!REPLAY_VISION_SUPPORTED.has(integration)) {\n abortUnsupportedPlatform(integration);\n }\n await detectPostHogIntegration(ctx);\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 * - `onReady` detection, 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 `onReady`.\n */\nexport const config: ProgramConfig = {\n ...base,\n // Orchestrator on pi; the flow's `model_pi`/`effort_pi` pin each stage, this model is the fallback.\n binding: {\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n model: GPT6_SOL_MODEL,\n },\n agentFlow: 'replay-vision',\n onReady: detectReplayVisionProject,\n oauthScopeAdditions: REPLAY_VISION_SCOPE_ADDITIONS,\n\n ciPreRun: async (\n session: ProgramSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n abortNoFrameworkDetected(NO_FRAMEWORK_GUIDANCE);\n }\n if (!REPLAY_VISION_SUPPORTED.has(integration)) {\n abortUnsupportedPlatform(integration);\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 noteDetectedFramework(session, frameworkConfig, context, runner.log);\n },\n};\n","import { Harness, Sequence, GPT5_6_TERRA_MODEL } from '@shared/constants';\nimport type { ProgramConfig } from '../program-step';\nimport { headlessOption, regionOption } from '@shared/headless-mode';\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 config: ProgramConfig = {\n binding: {\n sequence: Sequence.linear,\n harness: Harness.pi,\n model: GPT5_6_TERRA_MODEL,\n thinkingLevel: 'high',\n },\n command: 'ai-observability',\n description: 'Add PostHog AI Observability to your LLM calls',\n id: 'ai-observability',\n cliOptions: { ...headlessOption, ...regionOption },\n reportFile: AI_OBSERVABILITY_REPORT_FILE,\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 { Harness, Sequence, DEFAULT_AGENT_MODEL } from '@shared/constants';\nimport type { ProgramConfig } from '../program-step';\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 config: ProgramConfig = {\n command: 'metrics',\n description: 'Add PostHog application metrics to your project',\n id: 'metrics',\n // Orchestrator on pi. The binding routes only; every stage's model and\n // effort are pinned context-mill side in the flow's frontmatter\n // (`model_pi`/`effort_pi`: terra seed, sol tasks, luna report).\n binding: {\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n model: DEFAULT_AGENT_MODEL,\n },\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 handoffNotebook: true,\n reportFile: METRICS_REPORT_FILE,\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","import {\n ServiceHealthStatus,\n type AllServicesHealth,\n type BaseHealthResult,\n type HealthCheckKey,\n} from './types';\nimport { checkSkillsOriginHealth } from './endpoints';\nimport { logToFile } from '@utils/debug';\n\n// ---------------------------------------------------------------------------\n// Service labels (used in human-readable reason strings)\n// ---------------------------------------------------------------------------\n\nexport const SERVICE_LABELS: Record<HealthCheckKey, string> = {\n skillsOrigin: 'Skills download',\n};\n\n// ---------------------------------------------------------------------------\n// Readiness config\n// ---------------------------------------------------------------------------\n\nexport interface WizardReadinessConfig {\n /** Services where status=Down blocks the run (readiness=No). */\n downBlocksRun: HealthCheckKey[];\n /** Services where status=Degraded (or worse) blocks the run (readiness=No). */\n degradedBlocksRun?: HealthCheckKey[];\n}\n\n// Skills gate startup; gateway readiness is checked against the minted URL.\nexport const DEFAULT_WIZARD_READINESS_CONFIG: WizardReadinessConfig = {\n downBlocksRun: ['skillsOrigin'],\n};\n\nexport const SIGNUP_WIZARD_READINESS_CONFIG = DEFAULT_WIZARD_READINESS_CONFIG;\n\n// ---------------------------------------------------------------------------\n// Aggregate check\n// ---------------------------------------------------------------------------\n\nexport async function checkAllExternalServices(): Promise<AllServicesHealth> {\n return { skillsOrigin: await checkSkillsOriginHealth() };\n}\n\n// ---------------------------------------------------------------------------\n// Wizard readiness evaluation\n// ---------------------------------------------------------------------------\n\nexport enum WizardReadiness {\n Yes = 'yes',\n No = 'no',\n YesWithWarnings = 'yes_with_warnings',\n}\n\nexport interface WizardReadinessResult {\n decision: WizardReadiness;\n health: AllServicesHealth;\n reasons: string[];\n}\n\nfunction describeResult(label: string, h: BaseHealthResult): string {\n const parts = [`${label}: ${h.status}`];\n if (h.rawIndicator) parts.push(`indicator=${h.rawIndicator}`);\n if (h.error) parts.push(h.error);\n return parts.join(' — ');\n}\n\n// Each probe can take up to one base timeout + two retries with the\n// 500ms / 2000ms backoffs in endpoints.ts (worst case ~17.5s for a\n// network failure that exhausts retries). Probes run in parallel so\n// the aggregate ceiling is one probe, not the sum.\nconst READINESS_TIMEOUT_MS = 20_000;\n\nexport async function evaluateWizardReadiness(\n config: WizardReadinessConfig = DEFAULT_WIZARD_READINESS_CONFIG,\n): Promise<WizardReadinessResult> {\n try {\n const health = await Promise.race([\n checkAllExternalServices(),\n new Promise<AllServicesHealth>((resolve) =>\n setTimeout(\n () => resolve(allUnknown('Health check timed out')),\n READINESS_TIMEOUT_MS,\n ),\n ),\n ]);\n\n const blockingKeys = getBlockingServiceKeys(health, config);\n const reasons = blockingKeys.map((key) =>\n describeResult(SERVICE_LABELS[key], health[key]),\n );\n if (blockingKeys.length > 0) {\n const blockingDetails = blockingKeys.map((key) => {\n const h = health[key];\n return `${key} (${h.status}${h.error ? ` — ${h.error}` : ''})`;\n });\n logToFile(`[health-checks] blocked by: ${blockingDetails.join(', ')}`);\n return { decision: WizardReadiness.No, health, reasons };\n }\n\n return { decision: WizardReadiness.Yes, health, reasons };\n } catch (err) {\n logToFile(\n `[health-checks] error: ${err instanceof Error ? err.message : err}`,\n );\n // Health checks must never block the wizard run\n return {\n decision: WizardReadiness.Yes,\n health: allUnknown('Unexpected error'),\n reasons: [],\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Blocking service detection\n// ---------------------------------------------------------------------------\n\n// Report only dependencies that prevent this run from starting.\nexport function getBlockingServiceKeys(\n health: AllServicesHealth,\n config: WizardReadinessConfig = DEFAULT_WIZARD_READINESS_CONFIG,\n): HealthCheckKey[] {\n return (Object.keys(health) as HealthCheckKey[]).filter((key) => {\n const result = health[key];\n if (\n config.downBlocksRun.includes(key) &&\n (result.status === ServiceHealthStatus.Down ||\n result.status === ServiceHealthStatus.NoConnection)\n ) {\n return true;\n }\n if (\n (config.degradedBlocksRun ?? []).includes(key) &&\n result.status !== ServiceHealthStatus.Healthy\n ) {\n return true;\n }\n return false;\n });\n}\n\n/** Build an AllServicesHealth where every service is Degraded with the given error. */\nfunction allUnknown(error: string): AllServicesHealth {\n const base: BaseHealthResult = {\n status: ServiceHealthStatus.Degraded,\n error,\n };\n return { skillsOrigin: base };\n}\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 type { AuditCheck } from '@shared/audit-ledger';\nimport type { ProgramSession } from '../program-session';\n\nexport const AUDIT_CHECKS_KEY = 'auditChecks';\n\nexport function getAuditChecks(session: ProgramSession): AuditCheck[] {\n const raw = session.frameworkContext[AUDIT_CHECKS_KEY];\n return Array.isArray(raw) ? (raw as AuditCheck[]) : [];\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 type { AuditCheck } from '../types.js';\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 '../../program-step';\nimport type { ProgramRun } from '../../program-run';\nimport type { ProgramSession } from '../../program-session';\nimport { OutroKind } from '@shared/outro';\nimport { SPINNER_MESSAGE } from '../../framework-config';\nimport { isUsingTypeScript } from '@utils/setup-utils';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { AUDIT_CHECKS_FILE, AUDIT_CHECKS_KEY } from '../types.js';\nimport { seedAuditLedger } from '../seed.js';\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 config: ProgramConfig = {\n description: 'Audit PostHog event tracking in this project',\n id: 'events-audit',\n skillId: 'events-audit',\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: ProgramSession): 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","/**\n * JSON file watcher shared by runner and UI machinery.\n *\n * `fs.watch` alone is unreliable for atomic-rename writes, so the watcher\n * pairs it with a continuous mtime-polled re-read. The poll catches missed\n * events; the watch keeps latency low when it does fire.\n */\n\nimport * as fs from 'fs';\nimport { basename, dirname } from 'node:path';\nimport { logToFile } from '@utils/debug';\n\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_ATTACH_RETRY_INTERVAL_MS = 1000;\nconst DEFAULT_WATCH_DEBOUNCE_MS = 25;\n\nexport interface FileWatcherHandle {\n refresh(): void;\n stop(): void;\n}\n\nexport interface FileWatcherOptions {\n /** ms between mtime checks once the file exists. */\n pollIntervalMs?: number;\n /** ms between attach attempts while waiting for the file to appear. */\n attachRetryIntervalMs?: number;\n /** ms to coalesce duplicate filesystem events. */\n watchDebounceMs?: number;\n /** Ignore the file that exists when the watcher starts until it changes. */\n ignoreInitialFile?: boolean;\n /** Refuse to read files larger than this many bytes. */\n maxFileSizeBytes?: number;\n}\n\n/** Watch `path` for JSON updates and call `onUpdate(parsed)` whenever the\n * file's mtime changes and the contents are valid JSON. Caller must invoke\n * `handle.stop()` to release the watcher. */\nexport function startFileWatcher(\n path: string,\n onUpdate: (parsed: unknown) => void,\n options: FileWatcherOptions = {},\n): FileWatcherHandle {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const attachRetryIntervalMs =\n options.attachRetryIntervalMs ?? DEFAULT_ATTACH_RETRY_INTERVAL_MS;\n const watchDebounceMs = options.watchDebounceMs ?? DEFAULT_WATCH_DEBOUNCE_MS;\n\n const watchers: fs.FSWatcher[] = [];\n const intervals: Array<ReturnType<typeof setInterval>> = [];\n const targetDir = dirname(path);\n const targetName = basename(path);\n let lastMtimeMs = 0;\n let ignoredInitialSignature: string | null = null;\n let watchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n let lastReadErrorSignature: string | null = null;\n let stopped = false;\n\n const signature = (stat: fs.Stats): string =>\n `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;\n\n if (options.ignoreInitialFile) {\n try {\n const stat = fs.lstatSync(path);\n if (stat.isFile() && !stat.isSymbolicLink()) {\n ignoredInitialSignature = signature(stat);\n }\n } catch {\n // No initial file to ignore.\n }\n }\n\n const logReadError = (errorSignature: string, message: string) => {\n if (lastReadErrorSignature === errorSignature) return;\n lastReadErrorSignature = errorSignature;\n logToFile(`[file-watcher] ${message}: ${path}`);\n };\n\n const read = (force = false) => {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(path);\n } catch {\n return;\n }\n\n if (!stat.isFile() || stat.isSymbolicLink()) return;\n const fileSignature = signature(stat);\n\n try {\n if (ignoredInitialSignature) {\n if (fileSignature === ignoredInitialSignature) return;\n ignoredInitialSignature = null;\n }\n if (\n options.maxFileSizeBytes !== undefined &&\n stat.size > options.maxFileSizeBytes\n ) {\n logReadError(\n `oversized:${fileSignature}`,\n `refusing oversized JSON file (${stat.size} bytes, limit ${options.maxFileSizeBytes} bytes)`,\n );\n return;\n }\n if (!force && stat.mtimeMs === lastMtimeMs) return;\n lastMtimeMs = stat.mtimeMs;\n const parsed: unknown = JSON.parse(fs.readFileSync(path, 'utf-8'));\n lastReadErrorSignature = null;\n onUpdate(parsed);\n } catch (error) {\n logReadError(\n `invalid:${fileSignature}:${String(error)}`,\n `could not read valid JSON (${String(error)})`,\n );\n }\n };\n\n const scheduleRead = () => {\n if (stopped) return;\n if (watchDebounceTimer) clearTimeout(watchDebounceTimer);\n watchDebounceTimer = setTimeout(() => {\n watchDebounceTimer = null;\n read(true);\n }, watchDebounceMs);\n };\n\n const attachWatch = () => {\n watchers.push(\n fs.watch(targetDir, (_eventType, filename) => {\n if (filename == null || filename.toString() === targetName) {\n scheduleRead();\n }\n }),\n );\n };\n\n intervals.push(setInterval(() => read(), pollIntervalMs));\n\n try {\n attachWatch();\n // Defer the initial callback until the caller has received the handle, so\n // callbacks that stop their watcher cannot race handle assignment.\n queueMicrotask(() => {\n if (!stopped) read(true);\n });\n } catch {\n // Parent directory does not exist yet. Polling still covers a later file;\n // retry attaching the low-latency directory watcher until it appears.\n const attachInterval = setInterval(() => {\n try {\n fs.accessSync(targetDir);\n clearInterval(attachInterval);\n const idx = intervals.indexOf(attachInterval);\n if (idx >= 0) intervals.splice(idx, 1);\n attachWatch();\n read(true);\n } catch {\n // Still waiting.\n }\n }, attachRetryIntervalMs);\n intervals.push(attachInterval);\n }\n\n return {\n refresh() {\n if (stopped) return;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n read(true);\n },\n stop() {\n stopped = true;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n for (const watcher of watchers) watcher.close();\n for (const interval of intervals) clearInterval(interval);\n },\n };\n}\n","/**\n * Mirrors the agent's `.posthog-audit-checks.json` into the session, so the TUI\n * screens and the task stream read one value. `runProgram` owns the\n * lifecycle and removes the file at run end, so every path gets it — including\n * the e2e host, which builds no task stream.\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport type { RunnerContext } from '../runner-context';\nimport {\n startFileWatcher,\n type FileWatcherHandle,\n type FileWatcherOptions,\n} from '@utils/file-watcher';\nimport { logToFile } from '@utils/debug';\nimport { AUDIT_CHECKS_KEY, coerceAuditChecks } from './types';\n\nconst MAX_LEDGER_FILE_BYTES = 256 * 1024;\n\nexport function startAuditLedgerWatcher(\n installDir: string,\n file: string,\n runner: Pick<RunnerContext, 'setFrameworkContext'>,\n options: FileWatcherOptions = {},\n): FileWatcherHandle {\n const target = path.join(installDir, file);\n logToFile(`[audit-ledger] watching ${target}`);\n\n return startFileWatcher(\n target,\n (parsed) =>\n runner.setFrameworkContext(AUDIT_CHECKS_KEY, coerceAuditChecks(parsed)),\n {\n // A ledger an earlier run left behind stays ignored until this run writes.\n ignoreInitialFile: true,\n maxFileSizeBytes: MAX_LEDGER_FILE_BYTES,\n ...options,\n },\n );\n}\n\n/** The program declared the ledger, so it removes it, agent `rm` step or not. */\nexport function removeAuditLedger(installDir: string, file: string): void {\n const target = path.join(installDir, file);\n try {\n fs.rmSync(target, { force: true });\n } catch (error) {\n logToFile(`[audit-ledger] could not remove ${target}: ${String(error)}`);\n }\n}\n","import { createSkillProgram } from '../shared/skill-program';\nimport type { ProgramConfig } from '../program-step';\nimport type { ProgramRun } from '../program-run';\nimport type { RunnerContext } from '../runner-context';\nimport type { ProgramSession } from '../program-session';\nimport { OutroKind } from '@shared/outro';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { headlessOption, regionOption } from '@shared/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';\nimport { config as eventsAudit } from './events/config.js';\n\nconst seedBeforeAuditRun = (session: ProgramSession): void => {\n seedAuditLedger(session.installDir);\n session.frameworkContext[AUDIT_CHECKS_KEY] = AUDIT_SEED_CHECKS;\n};\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: ProgramSession,\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 // The session store lays any URL the agent emitted during the run over\n // this outro, so dashboardUrl/notebookUrl may stay undefined here.\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\nconst audit: ProgramConfig = {\n ...baseConfig,\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/** Registers `audit` and `events-audit`, which has no entry of its own. */\nexport const configs = [\n audit,\n eventsAudit,\n] as const satisfies readonly ProgramConfig[];\n\nexport {\n AUDIT_CHECKS_FILE,\n AUDIT_CHECKS_KEY,\n AUDIT_REPORT_FILE,\n getAuditChecks,\n type AuditCheck,\n type AuditStatus,\n} from './types.js';\nexport {\n removeAuditLedger,\n startAuditLedgerWatcher,\n} from './ledger-watcher.js';\n/** The checks every audit run starts from. */\nexport { AUDIT_SEED_CHECKS } from './seed.js';\n","// OAuth scope sets shared by programs and tools: a base set widened by additions.\n\n/**\n * Extra scope the Connect-Slack step needs on top of `WIZARD_OAUTH_SCOPES`.\n *\n * The step polls `/api/projects/:id/integrations/` (`fetchSlackConnected`)\n * to render the already-connected variant and to flip live once the user\n * completes the Slack OAuth step in the browser. Without `integration:read`\n * the first poll 403s, the screen stops polling, and an already-connected\n * project is nagged with the connect nudge. Used by the `wizard slack` tool,\n * where the step is the whole flow.\n */\nexport const CONNECT_SLACK_SCOPE_ADDITIONS = ['integration:read'] as const;\n\n/** `base` then `additions`, deduped, base first. */\nexport function withScopeAdditions(\n base: readonly string[],\n additions: readonly string[] | undefined,\n): readonly string[] {\n if (!additions || additions.length === 0) {\n return base;\n }\n return [...new Set([...base, ...additions])];\n}\n","import { existsSync, statSync } from 'fs';\nimport type { ProgramSession } from '../program-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { findPackageJsons } from '../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: ProgramSession,\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 { ProgramConfig } from '../program-step';\nimport { createSkillProgram } from '../shared/skill-program.js';\nimport {\n detectWebAnalyticsPrerequisites,\n WEB_ANALYTICS_ABORT_CASES,\n} from './detect.js';\n\nconst REPORT_FILE = 'posthog-web-analytics-report.md';\nconst DOCS_URL = 'https://posthog.com/docs/web-analytics';\n\nexport const config: 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 onReady: (ctx) =>\n detectWebAnalyticsPrerequisites(ctx.session, ctx.setFrameworkContext),\n parentCommand: 'audit',\n};\n\nexport {\n detectWebAnalyticsPrerequisites,\n WEB_ANALYTICS_ABORT_CASES,\n type WebAnalyticsDetectError,\n} from './detect.js';\n","import type { ProgramConfig } from '../program-step';\nimport type { AbortCase } from '@agent/types';\nimport { WIZARD_TOOL_NAMES } from '@agent';\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 this config directly).\nconst DEFAULT_MIGRATE_SKILL_ID = 'migrate-statsig';\n\nexport const config: ProgramConfig = {\n command: 'migrate',\n description: 'Migrate to PostHog from another analytics provider',\n id: 'migration',\n skillId: DEFAULT_MIGRATE_SKILL_ID,\n reportFile: MIGRATION_REPORT_FILE,\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","/**\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 { ProgramSession } from '../program-session';\nimport type { AbortCase } from '@agent/types';\nimport { findPackageJsons } from '../shared/package-scanning';\n\nexport {\n findPackageJsons,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type PackageMatch,\n} from '../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: ProgramSession,\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","import type { ProgramConfig } from '../program-step';\nimport { WIZARD_TOOL_NAMES } from '@agent';\nimport { detectRevenuePrerequisites, REVENUE_ABORT_CASES } from './detect.js';\n\nexport const config: ProgramConfig = {\n command: 'revenue-analytics',\n description: 'Set up PostHog for Revenue Analytics',\n id: 'revenue-analytics-setup',\n skillId: 'revenue-analytics-setup',\n onReady: (ctx) =>\n detectRevenuePrerequisites(ctx.session, ctx.setFrameworkContext),\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 {\n detectRevenuePrerequisites,\n POSTHOG_SDKS,\n STRIPE_SDKS,\n type RevenueDetectError,\n} from './detect.js';\n","/**\n * Warehouse-source program detection step.\n *\n * Thin adapter over `detectWarehouseSources` that writes results into\n * frameworkContext for the intro screen, plus the `[ABORT]` cases the\n * data-warehouse-source-setup skill can emit.\n */\n\nimport { existsSync, statSync } from 'fs';\nimport { analytics } from '@utils/analytics';\nimport type { ProgramSession } from '../program-session';\nimport type { AbortCase } from '@agent/types';\nimport {\n detectWarehouseSources,\n DETECTED_WAREHOUSE_SOURCES_KEY,\n} from '../warehouse-sources/detect';\n\n/** Structured detection errors rendered by the intro screen. */\nexport type WarehouseDetectError =\n | {\n kind: 'bad-directory';\n path: string;\n reason: 'missing' | 'not-dir' | 'unreadable';\n }\n | { kind: 'no-sources' };\n\n/** `[ABORT] <reason>` cases the skill can emit. */\nexport const WAREHOUSE_ABORT_CASES: AbortCase[] = [\n {\n // Skill emits: [ABORT] No data source detected\n // Tolerant of plural (\"sources\") and a trailing period.\n match: /^no data sources? detected\\.?$/i,\n message: 'No data source detected',\n body:\n 'The agent could not confirm a data warehouse source to connect. ' +\n 'Run this command from a project that uses a supported source ' +\n '(a database, Stripe, etc.).',\n docsUrl: 'https://posthog.com/docs/data-warehouse',\n },\n {\n // Skill emits: [ABORT] Source creation failed\n match: /^source creation failed\\.?$/i,\n message: 'Source creation failed',\n body:\n 'PostHog could not create the data warehouse source with the ' +\n 'credentials provided. Double-check the connection details and try ' +\n 'again, or set the source up directly in the PostHog app.',\n docsUrl: 'https://posthog.com/docs/data-warehouse',\n },\n];\n\n/**\n * Scan `session.installDir` for warehouse-source signals. Writes the detected\n * sources (or a `detectError`) into frameworkContext for the intro screen.\n */\nexport function detectWarehousePrerequisites(\n session: ProgramSession,\n setFrameworkContext: (key: string, value: unknown) => void,\n): void {\n const fail = (error: WarehouseDetectError) =>\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 (error) {\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'detectWarehousePrerequisites.stat', path: installDir },\n );\n fail({ kind: 'bad-directory', path: installDir, reason: 'unreadable' });\n return;\n }\n\n const sources = detectWarehouseSources(installDir);\n\n if (sources.length === 0) {\n fail({ kind: 'no-sources' });\n return;\n }\n\n // Tag every subsequent event (agent started/completed, setup wizard\n // finished, …) with what was detected — without this the analytics can't\n // say which source types a run attempted, only that a run happened.\n analytics.setTag(\n 'warehouse_source_kinds',\n sources.map((s) => s.kind).join(','),\n );\n analytics.setTag(\n 'warehouse_source_modes',\n sources.map((s) => `${s.kind}:${s.mode}`).join(','),\n );\n analytics.setTag('warehouse_source_count', sources.length);\n\n setFrameworkContext(DETECTED_WAREHOUSE_SOURCES_KEY, sources);\n}\n","import type { ProgramConfig } from '../program-step';\nimport type { ProgramRun } from '../program-run';\nimport type { ProgramSession } from '../program-session';\nimport { LONGER_ASK_TIMEOUT_MS } from '@shared/ask-policy';\nimport { WAREHOUSE_SOURCE_SCOPE_ADDITIONS } from '../oauth/program-scopes';\nimport {\n detectWarehousePrerequisites,\n WAREHOUSE_ABORT_CASES,\n} from './detect.js';\nimport { getDetectedWarehouseSources } from '../warehouse-sources/detect';\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: ProgramSession): 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 config: ProgramConfig = {\n command: 'warehouse',\n description: 'Detect and connect Data Warehouse sources',\n id: 'warehouse-source',\n skillId: 'data-warehouse-source-setup',\n onReady: (ctx) =>\n detectWarehousePrerequisites(ctx.session, ctx.setFrameworkContext),\n oauthScopeAdditions: WAREHOUSE_SOURCE_SCOPE_ADDITIONS,\n // No health-check screen in the TUI flow; the run skips the readiness check.\n healthCheck: false,\n reportFile: 'posthog-warehouse-report.md',\n allowedTools: ['Agent'],\n run: (session: ProgramSession): 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 {\n detectWarehousePrerequisites,\n WAREHOUSE_ABORT_CASES,\n type WarehouseDetectError,\n} from './detect.js';\nexport {\n getDetectedWarehouseSources,\n DETECTED_WAREHOUSE_SOURCES_KEY,\n} from '../warehouse-sources/detect';\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 { ProgramSession } from '../program-session';\nimport type { AbortCase } from '@agent/types';\nimport { ErrorCodes } from '@shared/errors';\nimport { detectWarehouseSources } from '../warehouse-sources/detect';\nimport type { DetectedSource } from '../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: ProgramSession,\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: ProgramSession,\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","import { AgentSignals } from '@agent';\nimport type { PromptContext } from '@agent/types';\nimport type { DetectedSource } from '../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 * 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 DetectProgress,\n} from '../detection/agentic';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '../detection/project-scope';\nimport { gatherFrameworkContext } from '../detection/context';\nimport { noteDetectedFramework } from '../detection/detected-framework';\nimport type { Integration } from '@shared/constants';\nimport type { ProgramSession } from '../program-session';\nimport type { RunnerContext } from '../runner-context';\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: ProgramSession,\n onEvent?: DetectEvent,\n onProgress?: DetectProgress,\n): Promise<IntegrationDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'self-driving',\n onEvent,\n onProgress,\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: ProgramSession,\n log: RunnerContext['log'],\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 noteDetectedFramework(session, frameworkConfig, context, log);\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","/**\n * Extra scopes the self-driving program needs on top of\n * `WIZARD_OAUTH_SCOPES`. All consumed by the PostHog MCP tools the\n * agent drives during the run:\n * • task:read / task:write — the signal source config API\n * (`inbox-source-configs-*`) is permissioned under the generic\n * `task` scope object, NOT a signals-specific one. Unrelated to\n * the Tasks product.\n * • integration:read — `integrations-list`, to check whether the\n * team already has a GitHub integration and to verify the connect\n * flow completed.\n * • signal_scout:read / signal_scout:write — list, sync, and tune\n * the Signals scout troop (`signals-scout-config-*`).\n * • session_recording:read / survey:read / error_tracking:read —\n * server-side product-usage probes (`query-session-recordings-list`,\n * `survey-list`, `error-issue-list`). Product usage is a\n * project-level fact (often instrumented in another repo or via\n * the snippet), so the agent asks the server instead of inferring\n * only from the local setup report. All three are read-only and\n * already in the wizard OAuth app's production scope ceiling (the\n * mcp-tutorial program requests them).\n * • external_data_source:read / external_data_source:write — the\n * connected-tools step creates the GitHub Issues / Linear warehouse\n * sources directly (`external-data-sources-create`) and verifies\n * what's actually connected (`external-data-sources-list`) instead\n * of taking the user's word for it.\n * • llm_skill:read / llm_skill:write — the custom-scouts step\n * (skill step 6b): read the seeded `authoring-signals-scouts`\n * guide and canonical scout bodies (`llma-skill-get` /\n * `llma-skill-file-get`) and author the user-approved custom\n * `signals-scout-*` skills (`llma-skill-create`). Canonical scout\n * bodies are never edited.\n * • product_enablement:write — the \"Enable products\" step turns on\n * Session Replay / Error Tracking / Support so their sources have\n * data to read (`products-enable`). A purpose-built scope: the\n * server owns each enable recipe, so this can flip the product\n * toggles without the far broader `project:write`.\n * • replay_scanner:read / replay_scanner:write — the Replay Vision\n * scanners step (skill step 6c) lists the team's existing scanners\n * and creates the `emits_signals` ones whose findings land in the\n * inbox (`vision-scanners-list` / `-create` / `-update`, and the\n * advisory `vision-scanners-estimate-create` / `vision-quota-retrieve`).\n * The scope OBJECT is `replay_scanner` — the `vision-scanners-*`\n * names are MCP tool names, not scopes. Configuring a scanner also\n * requires `session_recording:read` (the API pairs the two, since a\n * scanner's config indirectly exposes recording contents); that one\n * is already in this list for the step-2 usage probes.\n *\n * No OAuth-ceiling edit is needed for any scope here: they are all normal\n * public (unprivileged, non-internal, non-hidden) scope objects, and the\n * live wizard apps' ceiling is the `@default` sentinel, which resolves to\n * every such scope (`UNPRIVILEGED_SCOPES`) and auto-tracks new ones. Only a\n * privileged/internal/hidden object (e.g. `llm_gateway:*`) would need a\n * manual per-app edit. See README → \"OAuth app scope ceiling\".\n */\nexport const SELF_DRIVING_SCOPE_ADDITIONS = [\n 'task:read',\n 'task:write',\n 'integration:read',\n 'signal_scout:read',\n 'signal_scout:write',\n 'session_recording:read',\n 'survey:read',\n 'error_tracking:read',\n 'external_data_source:read',\n 'external_data_source:write',\n 'llm_skill:read',\n 'llm_skill:write',\n 'product_enablement:write',\n 'replay_scanner:read',\n 'replay_scanner:write',\n] as const;\n","import { join } from 'path';\nimport { access, rm } from 'node:fs/promises';\nimport type { ProgramConfig } from '../program-step';\nimport type { ProgramRun } from '../program-run';\nimport { createSkillProgram } from '../shared/skill-program.js';\nimport { OutroKind } from '@shared/outro';\nimport type { ProgramSession } from '../program-session';\nimport {\n detectSelfDrivingPrerequisites,\n getSelfDrivingDetectedTools,\n SELF_DRIVING_ABORT_CASES,\n SELF_DRIVING_INTEGRATE_PATH_KEY,\n} from './detect.js';\nimport { buildSelfDrivingPrompt } from './prompt.js';\nimport { resolveSelfDrivingStepKey } from './step-keys.js';\nimport { resolveProjectDir } from '../detection/agentic';\nimport { prepSelfDrivingIntegration } from './detect-agentic.js';\nimport { NO_DEFAULT_LIMIT, PRICE_PER_PR_USD, PRICING_LONG } from './pricing.js';\nimport { SELF_DRIVING_SCOPE_ADDITIONS } from './scopes.js';\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: ProgramSession): string =>\n resolveProjectDir(\n session.installDir,\n session.frameworkContext[SELF_DRIVING_INTEGRATE_PATH_KEY],\n );\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: ProgramSession): 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 config: 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 onReady: (ctx) =>\n detectSelfDrivingPrerequisites(ctx.session, ctx.setFrameworkContext),\n oauthScopeAdditions: SELF_DRIVING_SCOPE_ADDITIONS,\n // Integrate path: posthog-integration's agent runs composed, in the picked\n // project's dir, with that project's framework context gathered first.\n runSteps: {\n 'integrate-run': {\n runProgramId: 'posthog-integration',\n onRunPrep: prepSelfDrivingIntegration,\n targetDir: integrationDir,\n },\n },\n run: buildRun,\n};\n\nexport {\n detectSelfDrivingPrerequisites,\n GITHUB_REQUIRED_BODY,\n GITHUB_REQUIRED_MESSAGE,\n POSTHOG_PRESENT_KEY,\n SELF_DRIVING_ABORT_CASES,\n SELF_DRIVING_INTEGRATE_PATH_KEY,\n type SelfDrivingDetectError,\n} from './detect.js';\n\nexport { NO_DEFAULT_LIMIT, PRICING_LONG, PRICING_SHORT } from './pricing.js';\nexport {\n detectSelfDrivingIntegrationProjects,\n type IntegrationDetectionReport,\n type IntegrationProject,\n} from './detect-agentic.js';\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 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 { ProgramSession } from '../program-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: Pick<ProgramSession, 'installDir'>,\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 * 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 '../runner-context';\nimport { installOrUpdatePostHogCli } from '@shared/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","/**\n * Source-maps adapter over the generic agentic detector\n * (`@programs/detection/agentic`). The detector itself is product-knowledge-free;\n * this file supplies the source-maps targets (the automatable skill variants),\n * maps the result back to variants, and classifies each project as\n * instrumentable or not. The screen renders the result and the run step\n * instruments the chosen project.\n */\n\nimport { existsSync, readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport { IGNORED_DIRS, readProjectFile } from '@utils/bounded-fs';\nimport {\n detectProjectsWithAgent,\n coerceAgenticReport,\n type DetectTarget,\n type AgenticDetectionReport,\n type DetectEvent,\n type DetectProgress,\n} from '../detection/agentic';\nimport type { ProgramSession } from '../program-session';\nimport {\n VARIANT_DISPLAY_NAME,\n AUTOMATABLE_VARIANTS,\n MANUAL_SDK_INSTALL,\n RUST_SDK_CRATE,\n GO_SDK_MODULE,\n type SkillVariant,\n} from './detect.js';\n\nexport type { DetectEvent };\n\n/** One project, classified for source-map upload. */\nexport type DetectedProject = {\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 supported source-maps variant when it matches one, else null. */\n variant: SkillVariant | null;\n /** Whether a PostHog SDK is already installed in this project. */\n hasPostHog: boolean;\n /** variant != null && hasPostHog — source-map upload can be wired up here. */\n instrumentable: boolean;\n /** Why the project can't be instrumented (only when !instrumentable). */\n reason?: string;\n};\n\nexport type DetectionReport = {\n repoType: 'monorepo' | 'single';\n projects: DetectedProject[];\n};\n\n/**\n * Variant precedence for the agentic picker (most specific first). The detector\n * keeps the EARLIEST matching target. React Native and Flutter both outrank\n * Android and iOS: their repos carry `android/` and `ios/` folders with real\n * Gradle and Xcode manifests inside them, and must not be misclassified as\n * plain native apps. JS ordering mirrors `pickJsVariant` in detect.ts:\n * opinionated frameworks → bundlers → bare React → Node → generic web.\n */\nconst VARIANT_PRECEDENCE: readonly SkillVariant[] = [\n 'react-native',\n 'flutter',\n 'android',\n 'ios',\n 'nextjs',\n 'nuxt',\n 'angular',\n 'vite',\n 'webpack',\n 'rollup',\n 'react',\n 'node',\n // Native binaries: only chosen when no JS target matches the project, but\n // ahead of the generic web fallback so a go.mod / Cargo.toml project\n // resolves to its debug-symbols variant instead of `web`. Deliberate\n // tradeoff for the same-directory mixed case: a Go/Rust project with a\n // tooling-only package.json (common) beats a root-level JS app sharing a\n // directory with go.mod / Cargo.toml (rare — frontends usually live in a\n // subdirectory, which classifies as its own project).\n 'go',\n 'rust',\n 'web',\n];\n\nconst precedenceRank = (v: SkillVariant): number => {\n const i = VARIANT_PRECEDENCE.indexOf(v);\n return i === -1 ? Number.MAX_SAFE_INTEGER : i;\n};\n\n/** Source-map detection targets, ordered by VARIANT_PRECEDENCE. */\nexport const SOURCE_MAPS_TARGETS: DetectTarget[] = [...AUTOMATABLE_VARIANTS]\n .sort((a, b) => precedenceRank(a) - precedenceRank(b))\n .map((v) => ({ id: v, name: VARIANT_DISPLAY_NAME[v] }));\n\n// JS variants co-occur in one package.json (react+vite, node+rollup), so the\n// enumeration's priority winner may override the agent's pick among them.\n// Mobile/native stacks are mutually exclusive and stay pick-wins.\nconst JS_RERANK_VARIANTS: readonly SkillVariant[] = [\n 'nextjs',\n 'nuxt',\n 'angular',\n 'vite',\n 'webpack',\n 'rollup',\n 'react',\n 'node',\n 'web',\n];\n\n/** Comment-stripped substring check for the Rust SDK in one manifest. */\nfunction manifestMentionsSdk(manifestPath: string): boolean {\n const content = readProjectFile(manifestPath);\n return (\n content != null &&\n content.split('\\n').some((line) => {\n const code = line.split('#', 1)[0];\n return code.includes(RUST_SDK_CRATE);\n })\n );\n}\n\n/**\n * A workspace root is often a virtual manifest (`[workspace]` only, no\n * `[dependencies]`) — the SDK then lives in a member crate's manifest, so a\n * miss on the root must fall through to a bounded walk over nested\n * manifests. Depth 3 covers the common `crates/<name>/` layouts.\n */\nfunction anyNestedManifestMentionsSdk(dir: string, depth: number): boolean {\n if (depth > 3) return false;\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name.startsWith('.')) continue;\n if (IGNORED_DIRS.has(entry.name) || entry.name === 'target') continue;\n const child = join(dir, entry.name);\n if (manifestMentionsSdk(join(child, 'Cargo.toml'))) return true;\n if (anyNestedManifestMentionsSdk(child, depth + 1)) return true;\n }\n return false;\n}\n\n/**\n * Checks a project's Cargo manifests for the Rust SDK. The agentic detector\n * reports a single `hasPostHog` boolean for ANY PostHog dependency in the\n * project — for `rust` that could be satisfied by an unrelated JS SDK in the\n * same directory, so the deterministic manifest read is authoritative.\n * Comment-stripped substring matching, not TOML parsing: it still catches\n * renamed (`package = \"posthog-rs\"`) and workspace-inherited deps, while a\n * commented-out dependency no longer counts. Known miss: selecting a\n * workspace MEMBER whose dep is aliased at the workspace root\n * (`posthog.workspace = true` with the `package = \"posthog-rs\"` mapping only\n * in the root manifest, which sits above the member) — accepted; resolving\n * that needs full workspace metadata. Exported for testing.\n */\nexport function rustSdkVerifier(\n installDir: string,\n): (projectPath: string) => boolean {\n return (projectPath) => {\n const dir =\n projectPath === '.' ? installDir : join(installDir, projectPath);\n return (\n manifestMentionsSdk(join(dir, 'Cargo.toml')) ||\n anyNestedManifestMentionsSdk(dir, 0)\n );\n };\n}\n\n/**\n * True when go.mod actively `require`s the SDK — single-line requires and\n * `require ( … )` blocks only, so a `module` declaration, `replace`, or\n * `exclude` mention of the path never counts.\n */\nfunction goModRequiresSdk(content: string): boolean {\n let inBlock: 'require' | 'other' | null = null;\n for (const raw of content.split('\\n')) {\n const code = raw.split('//', 1)[0].trim();\n if (code === '') continue;\n if (inBlock != null) {\n if (code === ')') {\n inBlock = null;\n } else if (inBlock === 'require' && code.includes(GO_SDK_MODULE)) {\n return true;\n }\n continue;\n }\n if (code.endsWith('(')) {\n inBlock = code.startsWith('require') ? 'require' : 'other';\n continue;\n }\n if (code.startsWith('require ') && code.includes(GO_SDK_MODULE)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Checks a project's go.mod for the Go SDK — the same authoritative override\n * as `rustSdkVerifier`, for the same reason. Only `require` directives count;\n * no multi-module walk — unlike Cargo workspaces, a Go module's requirements\n * always live in the selected module's own go.mod. Exported for testing.\n */\nexport function goSdkVerifier(\n installDir: string,\n): (projectPath: string) => boolean {\n return (projectPath) => {\n const dir =\n projectPath === '.' ? installDir : join(installDir, projectPath);\n const content = readProjectFile(join(dir, 'go.mod'));\n return content != null && goModRequiresSdk(content);\n };\n}\n\nfunction isAutomatableVariant(value: string | null): value is SkillVariant {\n return value !== null && AUTOMATABLE_VARIANTS.includes(value as SkillVariant);\n}\n\nexport const FLUTTER_NO_BUILD_TARGET_REASON =\n 'Flutter project has no web, android or ios target — nothing to upload symbols for';\n\nexport const BARE_REACT_NATIVE_REASON =\n 'Bare React Native (without Expo) is not supported — source-map upload needs the Expo build pipeline';\n\n/**\n * Filesystem probes the classifier needs on top of what the agent reports.\n * Injected so `coerceReport` stays testable without touching disk. A missing\n * probe answers `false`, which blocks the variant it guards.\n */\nexport type ProjectProbes = {\n /**\n * Flutter: does the project have at least one platform target the wizard can\n * wire uploads into — web (dart2js source maps), android (R8 mapping files)\n * or ios (dSYMs)?\n */\n hasBuildTarget: (path: string) => boolean;\n /** React Native: is the `expo` package installed in the project? */\n isExpoProject: (path: string) => boolean;\n /**\n * Rust: does the project's Cargo manifest tree carry the posthog-rs crate?\n * Unlike the gate probes above, absence means \"trust the agent's\n * `hasPostHog`\", not `false` — the probe REPLACES the agent's boolean when\n * present, because any unrelated PostHog dependency can satisfy it.\n */\n verifyRustSdk?: (path: string) => boolean;\n /** Go: does the project's go.mod require posthog-go? Same semantics as `verifyRustSdk`. */\n verifyGoSdk?: (path: string) => boolean;\n};\n\nconst NO_PROBES: ProjectProbes = {\n hasBuildTarget: () => false,\n isExpoProject: () => false,\n};\n\n/**\n * True when the Flutter project at `projectPath` has at least one platform\n * target. `flutter create` scaffolds a directory per enabled platform, so their\n * absence means this is a pure Dart package or plugin — it produces no app\n * build, and there is nothing to upload.\n */\nfunction projectHasBuildTarget(\n installDir: string,\n projectPath: string,\n): boolean {\n const root = join(installDir, projectPath);\n return (\n existsSync(join(root, 'web', 'index.html')) ||\n existsSync(join(root, 'android')) ||\n existsSync(join(root, 'ios'))\n );\n}\n\n/** True when the project at `projectPath` has the `expo` package installed. */\nfunction projectHasExpo(installDir: string, projectPath: string): boolean {\n try {\n const pkg = JSON.parse(\n readFileSync(join(installDir, projectPath, 'package.json'), 'utf-8'),\n ) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n return (\n pkg.dependencies?.['expo'] != null ||\n pkg.devDependencies?.['expo'] != null\n );\n } catch {\n return false;\n }\n}\n\n/** Classify one agent-detected project for source-map upload. */\nfunction classifyProject(\n p: AgenticDetectionReport['projects'][number],\n probes: ProjectProbes,\n): DetectedProject {\n // For the native-binary variants the deterministic manifest read overrides\n // the agent's single hasPostHog boolean, which any unrelated PostHog\n // dependency can satisfy.\n const sdkVerifier =\n p.targetId === 'rust'\n ? probes.verifyRustSdk\n : p.targetId === 'go'\n ? probes.verifyGoSdk\n : undefined;\n const hasPostHog = sdkVerifier ? sdkVerifier(p.path) : p.hasPostHog;\n const base = {\n path: p.path,\n framework: p.framework,\n hasPostHog,\n };\n\n // A React Native or Flutter labelled project only counts when it resolved to\n // its own target — the nested Gradle and Xcode manifests inside those repos\n // must not claim android or ios.\n const namesForeignNativePlatform =\n (/\\b(?:react[\\s-]*native|expo)\\b/i.test(p.framework) &&\n p.targetId !== 'react-native') ||\n (/\\bflutter\\b/i.test(p.framework) && p.targetId !== 'flutter');\n if (!isAutomatableVariant(p.targetId) || namesForeignNativePlatform) {\n return {\n ...base,\n variant: null,\n instrumentable: false,\n reason: \"Source-map upload isn't supported for this stack yet\",\n };\n }\n\n // A Flutter package or plugin has no platform directories and never produces\n // an app build, so there are no symbols to upload for it.\n if (p.targetId === 'flutter' && !probes.hasBuildTarget(p.path)) {\n return {\n ...base,\n variant: null,\n instrumentable: false,\n reason: FLUTTER_NO_BUILD_TARGET_REASON,\n };\n }\n\n // Bare React Native (no expo package) is not supported: its Metro pipeline\n // can't inject chunk IDs, so uploads would never resolve.\n if (p.targetId === 'react-native' && !probes.isExpoProject(p.path)) {\n return {\n ...base,\n variant: null,\n instrumentable: false,\n reason: BARE_REACT_NATIVE_REASON,\n };\n }\n\n if (!hasPostHog) {\n // The wizard's default flow can't install the Go/Rust SDKs, so don't\n // point users at it for those stacks.\n const install =\n MANUAL_SDK_INSTALL[p.targetId] ?? 'run `npx @posthog/wizard` first';\n return {\n ...base,\n variant: p.targetId,\n instrumentable: false,\n reason: `No PostHog SDK installed yet — ${install}`,\n };\n }\n\n return { ...base, variant: p.targetId, instrumentable: true };\n}\n\n/** Map a generic detection report into source-maps projects. */\nfunction toSourceMapsReport(\n report: AgenticDetectionReport,\n probes: ProjectProbes,\n): DetectionReport {\n return {\n repoType: report.repoType,\n projects: report.projects.map((p) => classifyProject(p, probes)),\n };\n}\n\n/**\n * Validate the agent's raw JSON into a source-maps detection report. Exported\n * for testing — clamps projects the wizard cannot wire up to\n * non-instrumentable. Probes left out of `probes` answer `false`, so every\n * Flutter project is treated as target-less and every React Native project as\n * bare (both blocked).\n */\nexport function coerceReport(\n parsed: unknown,\n probes: Partial<ProjectProbes> = {},\n): DetectionReport {\n return toSourceMapsReport(\n coerceAgenticReport(\n parsed,\n SOURCE_MAPS_TARGETS.map((target) => target.id),\n ),\n { ...NO_PROBES, ...probes },\n );\n}\n\n/** Run the Haiku detector over the repo and classify projects for source maps. */\nexport async function detectSourceMapsProjects(\n session: ProgramSession,\n onEvent?: DetectEvent,\n onProgress?: DetectProgress,\n): Promise<DetectionReport> {\n const report = await detectProjectsWithAgent(session, {\n targets: SOURCE_MAPS_TARGETS,\n programId: 'error-tracking-upload-source-maps',\n purpose: 'set up PostHog Error Tracking source-map upload',\n rerankIds: JS_RERANK_VARIANTS,\n onEvent,\n onProgress,\n });\n return toSourceMapsReport(report, {\n hasBuildTarget: (path) => projectHasBuildTarget(session.installDir, path),\n isExpoProject: (path) => projectHasExpo(session.installDir, path),\n verifyRustSdk: rustSdkVerifier(session.installDir),\n verifyGoSdk: goSdkVerifier(session.installDir),\n });\n}\n","import type { ProgramConfig } from '../program-step';\nimport type { ProgramRun } from '../program-run';\nimport type { ProgramSession } from '../program-session';\nimport { OutroKind } from '@shared/outro';\nimport type { RunnerContext } from '../runner-context';\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 { preinstallPostHogCliOnce } from '../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 config: ProgramConfig = {\n command: 'upload-source-maps',\n description: 'Upload source maps to PostHog Error Tracking',\n id: 'error-tracking-upload-source-maps',\n // No health-check screen in the TUI flow; the run skips the readiness check.\n healthCheck: false,\n reportFile: REPORT_FILE,\n requires: ['posthog-integration'],\n\n run: (\n _session: ProgramSession,\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 {\n detectSourceMapsPrerequisites,\n SOURCE_MAPS_ABORT_CASES,\n SOURCE_MAPS_CONTEXT_KEYS,\n VARIANT_DISPLAY_NAME,\n VARIANTS_REQUIRING_POSTHOG_CLI,\n MANUAL_SDK_VARIANTS,\n type SkillVariant,\n type SourceMapsDetectError,\n} from './detect.js';\n\nexport {\n detectSourceMapsProjects,\n type DetectedProject,\n type DetectionReport,\n} from './detect-agentic.js';\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 type DetectProgress,\n} from '../detection/agentic';\nimport { gatherFrameworkContext } from '../detection/context';\nimport { noteDetectedFramework } from '../detection/detected-framework';\nimport {\n detectIntegrationProjects,\n toIntegrationCandidates,\n} from '../detection/project-scope';\nimport type { ProgramSession } from '../program-session';\nimport type { RunnerContext } from '../runner-context';\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: ProgramSession,\n onEvent?: DetectEvent,\n onProgress?: DetectProgress,\n): Promise<ErrorTrackingDetectionReport> {\n const report = await detectIntegrationProjects(session, {\n programId: 'error-tracking',\n recommend: true,\n onEvent,\n onProgress,\n });\n return toErrorTrackingReport(report);\n}\n\n/** The run's working directory: the picked project, else the repo root. */\nexport function errorTrackingProjectDir(session: ProgramSession): 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: ProgramSession,\n log: RunnerContext['log'],\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 noteDetectedFramework(session, frameworkConfig, context, log);\n}\n","import {\n Harness,\n Integration,\n Sequence,\n DEFAULT_AGENT_MODEL,\n} from '@shared/constants';\nimport { detectFramework } from '../detection/framework';\nimport { scopeInstallDirToProject } from '../detection/project-scope';\nimport { FRAMEWORK_REGISTRY } from '../frameworks/registry';\nimport type { ProgramRun } from '../program-run';\nimport {\n ERROR_TRACKING_UNSUPPORTED,\n errorTrackingProjectDir,\n gatherErrorTrackingContext,\n} from './detect-agentic';\nimport type { ProgramConfig } from '../program-step';\nimport type { ProgramSession } from '../program-session';\nimport type { CiRunnerContext, RunnerContext } from '../runner-context';\nimport { preinstallPostHogCliOnce } from '../shared/posthog-cli-preinstall';\nimport { analytics } from '@utils/analytics';\nimport { ProgramAbort } from '../program-abort';\nimport { ErrorCodes } from '@shared/errors';\nimport { abortNoFrameworkDetected } from '../shared/abort-no-framework';\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\nfunction abortUnsupportedPlatform(integration: Integration): never {\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 throw new ProgramAbort({\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 * Prepare the run once the project is known: pre-install posthog-cli when the\n * framework's symbol upload shells out to it, then gather the framework\n * context. See `preinstallPostHogCliOnce` for the once-per-process guard and\n * the warn-don't-fail handling.\n */\nasync function prepareErrorTrackingRun(\n session: ProgramSession,\n log: RunnerContext['log'],\n): Promise<void> {\n const { integration } = session;\n if (integration && SYMBOL_UPLOAD_CLI_FRAMEWORKS.has(integration)) {\n preinstallPostHogCliOnce(\n 'error tracking posthog-cli preinstall failed',\n { integration },\n log,\n );\n }\n await gatherErrorTrackingContext(session, log);\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\n4. On Python, Ruby, and PHP, link each production deploy to its release:\n install the matching \"error-tracking-link-releases\" skill variant\n (\\`python\\`, \\`ruby\\`, or \\`php\\`) and follow it. Change only the\n production deploy path; local runs, tests, and pull-request builds stay\n as they are.\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, or, on Python, Ruby and PHP, the `link-releases`\n * task that resolves a release in the production deploy.\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 * - The user picks the project after auth, which sets the framework preflight\n * needs and the directory the run is scoped to (`runSteps.run.targetDir`).\n * - The run step's `onRunPrep` runs after the pick (`ciPreRun` headless), so\n * the posthog-cli pre-install, which the agent cannot do (warlock blocks\n * \\`npm install -g\\`), waits for the project.\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 config: ProgramConfig = {\n // Orchestrator on pi, like metrics. The binding routes only; every stage's\n // model and effort are pinned context-mill side in the flow's frontmatter\n // (`model_pi`/`effort_pi`: terra seed, install and init, sol tasks, luna report).\n binding: {\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n model: DEFAULT_AGENT_MODEL,\n },\n command: 'error-tracking',\n description:\n 'Set up PostHog error tracking, source-map upload and release linking included',\n id: 'error-tracking',\n agentFlow: 'error-tracking',\n reportFile: ERROR_TRACKING_REPORT_FILE,\n // The run is scoped to the project the user picks after login.\n runSteps: {\n run: {\n targetDir: errorTrackingProjectDir,\n onRunPrep: prepareErrorTrackingRun,\n },\n },\n\n run: ERROR_TRACKING_RUN,\n\n ciPreRun: async (\n session: ProgramSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n abortNoFrameworkDetected();\n }\n if (ERROR_TRACKING_UNSUPPORTED.has(integration)) {\n abortUnsupportedPlatform(integration);\n }\n session.integration = integration;\n analytics.setTag('integration', integration);\n session.frameworkConfig = FRAMEWORK_REGISTRY[integration];\n session.skillId = integration;\n\n await prepareErrorTrackingRun(session, runner.log);\n },\n};\n\nexport {\n ERROR_TRACKING_PROJECT_PATH_KEY,\n detectErrorTrackingProjects,\n type ErrorTrackingDetectionReport,\n type ErrorTrackingProject,\n} from './detect-agentic.js';\n","export const FEATURE_FLAGS_SCOPE_ADDITIONS = [\n 'feature_flag:read',\n 'feature_flag:write',\n] as const;\n","import { headlessOption, regionOption } from '@shared/headless-mode';\nimport {\n Harness,\n Integration,\n Sequence,\n DEFAULT_AGENT_MODEL,\n} from '@shared/constants';\nimport { ErrorCodes } from '@shared/errors';\nimport { analytics } from '@utils/analytics';\nimport { detectPostHogIntegration } from '../detection/integration.js';\nimport { detectFramework } from '../detection/framework';\nimport { gatherFrameworkContext } from '../detection/context';\nimport { noteDetectedFramework } from '../detection/detected-framework';\nimport { scopeInstallDirToProject } from '../detection/project-scope';\nimport { FRAMEWORK_REGISTRY } from '../frameworks/registry';\nimport { ProgramAbort } from '../program-abort';\nimport type { ProgramConfig } from '../program-step';\nimport type { ProgramSession } from '../program-session';\nimport type { CiRunnerContext } from '../runner-context';\nimport { abortNoFrameworkDetected } from '../shared/abort-no-framework';\nimport { FEATURE_FLAGS_SCOPE_ADDITIONS } from './scopes.js';\n\nconst FEATURE_FLAGS_REPORT_FILE = 'posthog-feature-flags-report.md';\nconst FEATURE_FLAGS_DOCS_URL = 'https://posthog.com/docs/feature-flags';\n\nexport const FEATURE_FLAGS_STEP_SKILL_ID = 'integration-v2-feature-flags-step';\n\n/** KMP skill variants carry no `framework` tag, so orchestrator preflight aborts every KMP run. */\nexport const FEATURE_FLAGS_UNSUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.kmp,\n]);\n\nfunction abortUnsupportedPlatform(integration: Integration): never {\n const name = FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration;\n analytics.wizardCapture('feature flags unsupported platform', {\n integration,\n });\n throw new ProgramAbort({\n code: ErrorCodes.DetectUnsupportedPlatform,\n message:\n `The wizard cannot set up feature flags for ${name} projects yet.\\n\\n` +\n `Set them up manually:\\n ${FEATURE_FLAGS_DOCS_URL}`,\n });\n}\n\nexport const config: ProgramConfig = {\n command: 'feature-flags',\n description: 'Set up example PostHog feature flags',\n id: 'feature-flags',\n binding: {\n sequence: Sequence.orchestrator,\n harness: Harness.pi,\n model: DEFAULT_AGENT_MODEL,\n },\n agentFlow: 'feature-flags',\n handoffNotebook: true,\n // The intro blocks Continue until a supported framework is detected.\n onReady: (ctx) => detectPostHogIntegration(ctx),\n oauthScopeAdditions: FEATURE_FLAGS_SCOPE_ADDITIONS,\n cliOptions: { ...headlessOption, ...regionOption },\n run: {\n integrationLabel: 'feature-flags',\n successMessage: 'Feature flags wired in!',\n reportFile: FEATURE_FLAGS_REPORT_FILE,\n docsUrl: FEATURE_FLAGS_DOCS_URL,\n spinnerMessage: 'Setting up your first feature flags...',\n estimatedDurationMinutes: 5,\n buildOutroNextSteps: () => ({\n heading: 'Next steps',\n items: [\n 'Open your app and load the page or endpoint that evaluates each flag',\n \"Find the evaluation on each flag's page in PostHog (it can take a few minutes to show up)\",\n \"Turn the flags on when you're ready\",\n ],\n }),\n },\n\n // Headless `onReady`, as in posthog-integration, with the framework as the skill id.\n ciPreRun: async (\n session: ProgramSession,\n runner: CiRunnerContext,\n ): Promise<void> => {\n await scopeInstallDirToProject(session, runner);\n\n const integration = await detectFramework(session.installDir);\n if (!integration) {\n abortNoFrameworkDetected();\n }\n if (FEATURE_FLAGS_UNSUPPORTED.has(integration)) {\n abortUnsupportedPlatform(integration);\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 noteDetectedFramework(session, frameworkConfig, context, runner.log);\n },\n};\n","/**\n * Extra scopes the agent-skill program needs on top of `WIZARD_OAUTH_SCOPES`.\n *\n * Skills under this program (e.g. `creating-product-tours`) create feature\n * flags during the install flow. PostHog's consent grants exactly the scope\n * strings requested — `:write` does not imply `:read` — so listing existing\n * flags to avoid key collisions needs `feature_flag:read` explicitly.\n * `property_definition:read` lets the agent discover person properties when\n * building flag rollout filters instead of having to ask the user verbatim.\n */\nexport const AGENT_SKILL_SCOPE_ADDITIONS = [\n 'feature_flag:read',\n 'feature_flag:write',\n 'property_definition:read',\n] as const;\n","import type { ProgramConfig } from '../program-step.js';\nimport { POSTHOG_DOCS_URL } from '@shared/constants.js';\nimport { AGENT_SKILL_SCOPE_ADDITIONS } from './scopes.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 runProgram fails\n// with \"has no run configuration\" and the skill never executes — so we derive\n// generic run metadata from the resolved skill id at run time.\nexport const config: ProgramConfig = {\n id: 'agent-skill',\n description: 'Run an arbitrary context-mill skill',\n allowedTools: ['Agent'],\n oauthScopeAdditions: AGENT_SKILL_SCOPE_ADDITIONS,\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 // The integration skills' conclude step mirrors the report into a notebook.\n handoffNotebook: skillId.startsWith('integration-'),\n });\n },\n};\n","/**\n * Central registry of all wizard programs. A program is an agent run; a\n * command that runs no agent is a tool, in `src/tools`. To add one, follow\n * \"Add a program\" in README.md in this folder, then import it here.\n */\n\nimport type { ProgramConfig, ProgramId } from './program-step.js';\nimport {\n WIZARD_OAUTH_SCOPES,\n WIZARD_PROVISIONING_SCOPES,\n} from '@shared/constants';\nimport { withScopeAdditions } from '@shared/oauth-scopes';\nimport { config as posthogIntegration } from '@programs/posthog-integration';\nimport { config as mcpAnalytics } from '@programs/mcp-analytics';\nimport { config as replayVision } from '@programs/replay-vision';\nimport { config as aiObservability } from '@programs/ai-observability';\nimport { config as metrics } from '@programs/metrics';\nimport { configs as auditPrograms } from '@programs/audit';\nimport { config as webAnalyticsDoctor } from '@programs/web-analytics-doctor';\nimport { config as migration } from '@programs/migration';\nimport { config as revenueAnalytics } from '@programs/revenue-analytics';\nimport { config as warehouseSource } from '@programs/warehouse-source';\nimport { config as selfDriving } from '@programs/self-driving';\nimport { config as sourceMaps } from '@programs/error-tracking-upload-source-maps';\nimport { config as errorTracking } from '@programs/error-tracking';\nimport { config as featureFlags } from '@programs/feature-flags';\nimport { config as agentSkill } from '@programs/agent-skill';\n\nconst [audit, eventsAudit] = auditPrograms;\n\n/** Every program entry, one line each. */\nexport const PROGRAM_REGISTRY = [\n posthogIntegration,\n revenueAnalytics,\n warehouseSource,\n sourceMaps,\n errorTracking,\n featureFlags,\n ...auditPrograms,\n webAnalyticsDoctor,\n migration,\n selfDriving,\n agentSkill,\n mcpAnalytics,\n replayVision,\n aiObservability,\n metrics,\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 that config's `id` under a friendly name for call sites.\n */\nexport const Program = {\n PostHogIntegration: posthogIntegration.id,\n RevenueAnalyticsSetup: revenueAnalytics.id,\n WarehouseSource: warehouseSource.id,\n ErrorTrackingUploadSourceMaps: sourceMaps.id,\n ErrorTracking: errorTracking.id,\n FeatureFlags: featureFlags.id,\n Migration: migration.id,\n Audit: audit.id,\n EventsAudit: eventsAudit.id,\n WebAnalyticsDoctor: webAnalyticsDoctor.id,\n SelfDriving: selfDriving.id,\n AgentSkill: agentSkill.id,\n McpAnalytics: mcpAnalytics.id,\n ReplayVision: replayVision.id,\n AiObservability: aiObservability.id,\n Metrics: metrics.id,\n} as const;\n\n/**\n * Look up a program config by its id. Callers pass ids from `Program` or\n * from a registered config.\n */\nexport function getProgramConfig(id: ProgramId): ProgramConfig {\n const config = PROGRAM_REGISTRY.find((c) => c.id === id);\n if (!config) throw new Error(`Unknown program id \"${id}\"`);\n return config;\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 a command. Nested ones go through its parent. */\nexport function getCommandPath(config: {\n command: string;\n parentCommand?: string;\n}): string {\n return config.parentCommand\n ? `${config.parentCommand} ${config.command}`\n : config.command;\n}\n\n/** The program with this id, or undefined for a tool's id or none. */\nexport function findProgramConfig(\n programId: ProgramId | null | undefined,\n): ProgramConfig | undefined {\n return programId\n ? PROGRAM_REGISTRY.find((c) => c.id === programId)\n : undefined;\n}\n\n/**\n * The OAuth scopes a program's login asks for: `WIZARD_OAUTH_SCOPES` plus\n * the program's `oauthScopeAdditions`. A missing or unknown id gets the base\n * set unchanged.\n */\nexport function getOAuthScopesForProgram(\n programId: ProgramId | null | undefined,\n): readonly string[] {\n return withScopeAdditions(\n WIZARD_OAUTH_SCOPES,\n findProgramConfig(programId)?.oauthScopeAdditions,\n );\n}\n\n/**\n * The scopes for the signup provisioning path. Same shape as\n * `getOAuthScopesForProgram`, on `WIZARD_PROVISIONING_SCOPES`, so a\n * program's extra scopes only reach tokens provisioned for that program.\n */\nexport function getProvisioningScopesForProgram(\n programId: ProgramId | null | undefined,\n): readonly string[] {\n return withScopeAdditions(\n WIZARD_PROVISIONING_SCOPES,\n findProgramConfig(programId)?.oauthScopeAdditions,\n );\n}\n","import { ErrorCodes, type ErrorCode } from '@shared/errors';\nimport type { RevenueDetectError } from './revenue-analytics/detect';\nimport type { SelfDrivingDetectError } from './self-driving/detect';\nimport type { SourceMapsDetectError } from './error-tracking-upload-source-maps/detect';\nimport type { WarehouseDetectError } from './warehouse-source/detect';\nimport type { WebAnalyticsDetectError } from './web-analytics-doctor/detect';\n\n/**\n * Every `kind` a program detect step can write into\n * `frameworkContext.detectError`, assembled from the programs' own unions.\n * Type-only imports, so this adds no runtime edge from the catalog to the\n * programs (same shape as `skill-map.ts` keying on `InstallSkillResult`).\n *\n * The point is the compile error: add a kind to any program's `DetectError`\n * and `DETECT_CODES` stops type-checking until it gets a code.\n */\nexport type DetectErrorKind =\n | RevenueDetectError['kind']\n | SelfDrivingDetectError['kind']\n | SourceMapsDetectError['kind']\n | WarehouseDetectError['kind']\n | WebAnalyticsDetectError['kind'];\n\nconst DETECT_CODES: Record<DetectErrorKind, ErrorCode> = {\n 'bad-directory': ErrorCodes.DetectBadDirectory,\n 'unsupported-platform': ErrorCodes.DetectUnsupportedPlatform,\n 'no-project-files': ErrorCodes.DetectNoProjectFiles,\n 'no-sources': ErrorCodes.DetectNoSources,\n 'no-package-json': ErrorCodes.DetectNoPackageJson,\n 'no-sdks': ErrorCodes.DetectNoSdks,\n 'missing-stripe': ErrorCodes.DetectMissingStripe,\n // Three kinds, one failure class: the program needs a PostHog SDK and the\n // project has none. Hosts that care which program asked read `detail.kind`.\n 'no-posthog-sdk': ErrorCodes.DetectNoPosthogSdk,\n 'no-posthog': ErrorCodes.DetectNoPosthogSdk,\n 'missing-posthog': ErrorCodes.DetectNoPosthogSdk,\n};\n\n/**\n * `kind` arrives as a bare string — `frameworkContext.detectError` is untyped\n * storage — so the lookup can still miss. It falls back to a detect-group code\n * with `retry: 'no'`, never to `InternalUnhandled`: an unrecognized precondition\n * failure is still a precondition failure, and telling a sandbox to retry one\n * costs it the whole budget.\n */\nexport function detectErrorCode(kind: string): ErrorCode {\n return DETECT_CODES[kind as DetectErrorKind] ?? ErrorCodes.DetectUnclassified;\n}\n","/** A program's detection, written into the session store before its run. */\nimport type { RunResult } from '@agent/types';\nimport { POSTHOG_DOCS_URL } from '@shared/constants';\nimport { ErrorCodes } from '@shared/errors';\nimport { WizardError } from '@shared/errors/wizard-error';\nimport { detectErrorCode } from './detect-map';\nimport type { ProgramConfig } from './program-step';\nimport type { CiRunnerContext } from './runner-context';\nimport type { WizardSession } from './session/wizard-session';\nimport type { SessionStore } from './session/session-store';\n\n/**\n * Scan the project for `config`: a CI session runs the program's `ciPreRun`\n * when it has one, anything else its `onReady`. Marks detection complete.\n */\nexport async function detectProgram(\n config: ProgramConfig,\n store: SessionStore,\n runner: CiRunnerContext,\n): Promise<void> {\n const ciPreRun = config.ciPreRun;\n if (store.session.ci && ciPreRun) {\n await store.edit((draft) =>\n ciPreRun(draft, {\n ...runner,\n // The login lands in the store; the draft needs it too, or a scan of it sees none.\n authenticate: async (programId) => {\n await runner.authenticate(programId);\n const { credentials, apiProject, apiUser, roleAtOrganization } =\n store.session;\n Object.assign(draft, {\n credentials,\n apiProject,\n apiUser,\n roleAtOrganization,\n });\n },\n }),\n );\n } else {\n await config.onReady?.(store.readyContext());\n }\n store.setDetectionComplete();\n}\n\n/** What detection found that stops the run: an unsupported version or an unmet prerequisite. */\nexport function detectionFailure(\n config: ProgramConfig,\n session: WizardSession,\n): NonNullable<RunResult['failure']> | null {\n if (session.unsupportedVersion) {\n const { current, minimum, docsUrl } = session.unsupportedVersion;\n const code = ErrorCodes.DetectUnsupportedVersion;\n return {\n code,\n message:\n `Detected framework version ${current} is not supported. ` +\n `Minimum supported version is ${minimum}.\\n\\nSee ${docsUrl}`,\n error: new WizardError(\n `${config.id} unsupported framework version`,\n { integration: config.id, current, minimum },\n code,\n ),\n };\n }\n const detectError = session.frameworkContext.detectError as\n | { kind: string; [key: string]: unknown }\n | undefined;\n if (!detectError) return null;\n const code = detectErrorCode(detectError.kind);\n const docsUrl =\n (typeof config.run === 'object' ? config.run.docsUrl : undefined) ??\n POSTHOG_DOCS_URL;\n return {\n code,\n message: `Prerequisites not met: ${detectError.kind}\\n\\nSee ${docsUrl}`,\n // `kind` stays in the detail: several kinds share one code.\n detail: { ...detectError },\n error: new WizardError(\n `${config.id} prerequisites failed`,\n { integration: config.id, detect_error_kind: detectError.kind },\n code,\n ),\n };\n}\n","/**\n * OAuth failure presentation — the structured error the flow throws and the\n * user-facing messaging built from it.\n *\n * `oauth.ts` owns the flow machinery (callback server, token exchange, port\n * retries); this module owns what a failure *means*: the RFC 6749 error\n * fields (`error`, `error_description`, `error_uri`), per-code remediation\n * copy, and safe rendering for the browser callback page. Everything here is\n * pure so the copy can be unit-tested without the HTTP machinery.\n */\n\nimport { z } from 'zod';\nimport {\n ISSUES_URL,\n POSTHOG_STATUS_PAGE_URL,\n WIZARD_CONTACT_EMAIL,\n} from '@shared/constants';\n\n/**\n * A structured OAuth failure (RFC 6749 §4.1.2.1 authorize errors, §5.2 token\n * errors). Thrown by both the authorize callback and the token exchange so\n * the flow's error handler keys remediation copy and analytics fingerprints\n * off `code` instead of re-parsing `message`.\n *\n * The message stays exactly `OAuth error: <code>` — the analytics fingerprint\n * scheme (`wizard_oauth_<code>`) and the legacy message-prefix parsing both\n * depend on that shape. The description/uri ride along as properties.\n *\n * NAMING: never bind an instance to a variable or property whose name\n * contains \"oauth\" (use `flowError`, `callbackError`, ...). CodeQL's\n * clear-text-logging heuristic classifies any `*oauth*`-named variable read\n * as password data, and these errors are deliberately shown to the user, so\n * such a binding flags every log statement downstream of it (CWE-312).\n */\nexport class OAuthError extends Error {\n readonly code: string;\n /** The server's human-readable explanation (`error_description`), if sent. */\n readonly description?: string;\n /** The server's \"more info\" link (`error_uri`), if sent. */\n readonly uri?: string;\n\n constructor(code: string, description?: string, uri?: string) {\n super(`OAuth error: ${code}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n this.uri = uri;\n }\n}\n\n/**\n * Sanitize an OAuth error param before logging or rendering it: these arrive\n * on a query string (or a token-endpoint body) that anything able to hit the\n * local callback port can populate. Strips non-printable/non-ASCII characters\n * and caps the length. Returns undefined when nothing printable remains.\n */\nexport function sanitizeOAuthParam(\n value: string | null | undefined,\n maxLength = 200,\n): string | undefined {\n if (!value) return undefined;\n const cleaned = value\n .replace(/[^\\x20-\\x7e]/g, '')\n .slice(0, maxLength)\n .trim();\n return cleaned || undefined;\n}\n\n/** Description sentences get more room than bare codes/URIs. */\nconst MAX_DESCRIPTION_LENGTH = 500;\n\nexport function escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\nconst OAuthErrorBodySchema = z.object({\n error: z.string().min(1),\n error_description: z.string().optional(),\n error_uri: z.string().optional(),\n});\n\n/**\n * Build an `OAuthError` from an authorize-callback's query params.\n * Sanitizes every field — the callback URL is attacker-reachable input.\n */\nexport function oauthErrorFromCallbackParams(\n params: URLSearchParams,\n): OAuthError {\n return new OAuthError(\n sanitizeOAuthParam(params.get('error')) ?? 'unknown',\n sanitizeOAuthParam(params.get('error_description'), MAX_DESCRIPTION_LENGTH),\n sanitizeOAuthParam(params.get('error_uri')),\n );\n}\n\n/**\n * Build an `OAuthError` from a token-endpoint JSON error body (RFC 6749\n * §5.2). Returns null when the body isn't an OAuth error shape (HTML error\n * pages, proxy bodies, network failures) — the caller rethrows the raw\n * error in that case.\n */\nexport function oauthErrorFromTokenBody(data: unknown): OAuthError | null {\n const parsed = OAuthErrorBodySchema.safeParse(data);\n if (!parsed.success) return null;\n return new OAuthError(\n sanitizeOAuthParam(parsed.data.error) ?? 'unknown',\n sanitizeOAuthParam(parsed.data.error_description, MAX_DESCRIPTION_LENGTH),\n sanitizeOAuthParam(parsed.data.error_uri),\n );\n}\n\n/**\n * The error paragraphs for the browser callback page. The constant framing\n * (title, styles, \"return to your terminal\") stays in `oauth.ts`; this owns\n * the part that varies by error, with everything HTML-escaped since the\n * values come straight off the callback query string.\n */\nexport function buildCallbackErrorHtml(error: OAuthError): string {\n if (error.code === 'access_denied') {\n return '<p>Authorization cancelled.</p>';\n }\n const paragraphs = [\n `<p>Authorization failed (${escapeHtml(error.code)}).</p>`,\n ];\n if (error.description) {\n paragraphs.push(`<p>${escapeHtml(error.description)}</p>`);\n }\n if (error.uri) {\n paragraphs.push(`<p>More info: ${escapeHtml(error.uri)}</p>`);\n }\n return paragraphs.join('\\n');\n}\n\ninterface FailureMessageParams {\n error: Error;\n /** Scopes the flow requested (base set + program additions). */\n requestedScopes: readonly string[];\n clientId: string;\n /** OAuth server origin the flow targeted. */\n oauthUrl: string;\n /**\n * True when the flow targets a dev-seeded stack (IS_DEV build or a pinned\n * `--base-url`) — selects the \"fix your local OAuth app\" remediation over\n * the production-runbook one. Same condition that selects the dev client ID.\n */\n isDevStack: boolean;\n}\n\n/**\n * Per-code headline + \"what to do\" remediation for the terminal failure\n * message. Codes not listed fall through to a generic rendering of the\n * error. `access_denied` and the flow timeout never reach this — they keep\n * their dedicated handling in `performOAuthFlow`.\n */\nfunction headlineAndRemediation(params: FailureMessageParams): {\n headline: string;\n whatToDo?: string;\n} {\n const { error, oauthUrl, isDevStack } = params;\n if (!(error instanceof OAuthError)) {\n return { headline: error.message };\n }\n\n switch (error.code) {\n case 'invalid_scope':\n return {\n headline:\n 'PostHog rejected one or more of the requested scopes (invalid_scope).',\n whatToDo: isDevStack\n ? 'The wizard OAuth app on your local stack allows fewer scopes than the wizard requests (its OAuthApplication.scopes ceiling). Re-run your local wizard OAuth app setup so it allows every scope listed below, or add the missing ones in Django admin, then try again.'\n : `The wizard OAuth app's server-side scope ceiling (OAuthApplication.scopes) is missing scopes the wizard requests. This needs a fix on the PostHog side, so email ${WIZARD_CONTACT_EMAIL} with the details below (PostHog staff: the \"scope-ceiling-invalid-scope\" runbook in PostHog/runbooks has the fix). Re-running the wizard will not help until the ceiling is updated.`,\n };\n case 'invalid_client':\n return {\n headline: `PostHog at ${oauthUrl} does not recognize the wizard's OAuth client (invalid_client).`,\n whatToDo:\n 'The client ID below is not registered on the target instance. If you pointed the wizard at a local or self-hosted stack (--base-url), seed its wizard OAuth app first; otherwise re-run without --base-url to use PostHog Cloud.',\n };\n case 'invalid_grant':\n return {\n headline:\n 'The login code was rejected (invalid_grant): it expired or was already used.',\n whatToDo:\n 'Re-run the wizard and complete the browser login promptly. Login codes expire after 5 minutes and can only be used once.',\n };\n case 'server_error':\n return {\n headline:\n \"PostHog's OAuth server hit an internal error (server_error).\",\n whatToDo: `This is usually transient. Wait a moment and re-run the wizard. If it keeps failing, check ${POSTHOG_STATUS_PAGE_URL}.`,\n };\n case 'temporarily_unavailable':\n return {\n headline:\n \"PostHog's OAuth server is temporarily unavailable (temporarily_unavailable).\",\n whatToDo: `This is usually transient. Wait a moment and re-run the wizard. If it keeps failing, check ${POSTHOG_STATUS_PAGE_URL}.`,\n };\n default:\n return {\n headline: `PostHog returned an OAuth error: ${error.code}.`,\n whatToDo:\n 'Re-run the wizard. If the error persists, include the details below when asking for help.',\n };\n }\n}\n\n/**\n * Terminal message for a failed OAuth flow. Always ends with the request\n * context (target host, client ID, requested scopes) so a pasted bug report\n * is actionable without follow-up questions, plus how to get help: the\n * support email (Zendesk-backed) and the issues link.\n */\nexport function buildOAuthFailureMessage(params: FailureMessageParams): string {\n const { error, requestedScopes, clientId, oauthUrl } = params;\n const flowError = error instanceof OAuthError ? error : null;\n const { headline, whatToDo } = headlineAndRemediation(params);\n\n const sections = [`Authorization failed: ${headline}`];\n if (flowError?.description) {\n sections.push(`The server said: ${flowError.description}`);\n }\n if (flowError?.uri) {\n sections.push(`More info: ${flowError.uri}`);\n }\n if (whatToDo) {\n sections.push(`What to do: ${whatToDo}`);\n }\n sections.push(\n [\n 'Details for bug reports:',\n ` target host: ${oauthUrl}`,\n ` client ID: ${clientId}`,\n ` requested scopes: ${requestedScopes.join(' ')}`,\n ].join('\\n'),\n );\n sections.push(\n `Need help? Email ${WIZARD_CONTACT_EMAIL} with the details above, or create an issue:\\n${ISSUES_URL}`,\n );\n return sections.join('\\n\\n');\n}\n","/** The wizard's OAuth token shapes, scope checks and the refresh grant a program's login rotates with. */\nimport axios from 'axios';\nimport { z } from 'zod';\nimport {\n POSTHOG_DEV_CLIENT_ID,\n POSTHOG_PROXY_CLIENT_ID,\n WIZARD_USER_AGENT,\n} from '@shared/constants';\nimport { logToFile } from '@utils/debug';\nimport { oauthErrorFromTokenBody } from '@utils/oauth-errors';\nimport { getOAuthUrl, resolveBaseUrl } from '@utils/urls';\n\nexport const OAuthTokenResponseSchema = z.object({\n access_token: z.string(),\n expires_in: z.number(),\n token_type: z.string(),\n scope: z.string(),\n refresh_token: z.string().optional(),\n scoped_teams: z.array(z.number()).optional(),\n scoped_organizations: z.array(z.string()).optional(),\n // Sent by PostHog Cloud (and passed through the oauth.posthog.com proxy); absent on\n // self-hosted. `.catch(undefined)` so an unrecognized value degrades to the probe\n // fallback instead of failing the whole login.\n posthog_region: z.enum(['us', 'eu']).optional().catch(undefined),\n posthog_base_url: z.string().optional().catch(undefined),\n});\n\nexport type OAuthTokenResponse = z.infer<typeof OAuthTokenResponseSchema>;\n\nexport function parseOAuthScopes(scope: string): string[] {\n return scope.split(/\\s+/).filter(Boolean);\n}\n\n/**\n * Requested scopes the grant came back without.\n *\n * A token can legitimately carry fewer scopes than the wizard asked for: the\n * consent screen lets the user deselect any scope the OAuth app doesn't mark\n * required, and anything outside the app's ceiling is clamped server-side.\n * Neither path is an error — `/oauth/token` just returns a narrower `scope`.\n * Diff it at login, where the gap is fixable, rather than letting the run\n * discover it as a permission failure on some API call minutes in.\n */\nexport function missingOAuthScopes(\n requested: readonly string[],\n grantedScope: string,\n): string[] {\n const granted = new Set(parseOAuthScopes(grantedScope));\n return requested.filter((scope) => !granted.has(scope));\n}\n\n/**\n * OAuth client ID for the current target. A pinned base URL (`--base-url`, or\n * IS_DEV's implicit localhost) means we're talking to a dev-seeded stack, which\n * registers the dev client; prod uses the proxy client.\n *\n * TODO: this assumes any pinned base URL is a dev-seeded instance that\n * registers POSTHOG_DEV_CLIENT_ID. If we ever point `--base-url` at a non-dev\n * instance with its own OAuth app, make the client ID configurable (e.g. a\n * `--oauth-client-id` flag) instead of always falling back to the dev client.\n */\nexport function getOAuthClientId(baseUrl?: string): string {\n return resolveBaseUrl(baseUrl)\n ? POSTHOG_DEV_CLIENT_ID\n : POSTHOG_PROXY_CLIENT_ID;\n}\n\n// Refresh-token grant (RFC 6749 §6); the server rotates, so callers must store the returned refresh_token.\nexport async function refreshAccessToken(\n refreshToken: string,\n baseUrl?: string,\n clientId?: string,\n): Promise<OAuthTokenResponse> {\n const oauthUrl = getOAuthUrl(baseUrl);\n logToFile(`[oauth] refreshing access token at ${oauthUrl}/oauth/token`);\n try {\n const response = await axios.post(\n `${oauthUrl}/oauth/token`,\n {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n // The grant only refreshes under its minting app — provisioning signups pass their regional client.\n client_id: clientId ?? getOAuthClientId(baseUrl),\n },\n {\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': WIZARD_USER_AGENT,\n },\n timeout: 30_000,\n },\n );\n const token = OAuthTokenResponseSchema.parse(response.data);\n logToFile('[oauth] access token refreshed');\n return token;\n } catch (e) {\n logToFile(\n '[oauth] token refresh failed:',\n e instanceof Error ? e.message : e,\n );\n const refreshError = axios.isAxiosError(e)\n ? oauthErrorFromTokenBody(e.response?.data)\n : null;\n throw refreshError ?? e;\n }\n}\n","/** Resolved credentials runProgram passes to one agent run, and the grant that rotates them. */\n\nimport type { ApiProject, ApiUser, Credentials } from '@shared/api';\nimport { WIZARD_OAUTH_SCOPES } from '@shared/constants';\nimport { markGrantRevoked } from '@shared/auth-session-state';\nimport { missingOAuthScopes, refreshAccessToken } from './oauth/tokens';\nimport { OAuthError } from '@utils/oauth-errors';\nimport { analytics } from '@utils/analytics';\nimport { logToFile } from '@utils/debug';\n\nexport type ResolvedProgramCredentials = {\n posthog: Credentials; // token, project API key, project ID and host\n project: ApiProject | null; // the project, when known\n apiUser: ApiUser | null; // the user, when known\n roleAtOrganization?: string | null; // the user's role, for role-tailored copy; else the user's own\n};\n\n/** The caller authenticates once per scope; the signal aborts with the invocation. */\nexport type CredentialsProvider = {\n resolve(\n programId: string,\n context: { signal: AbortSignal },\n ): Promise<ResolvedProgramCredentials>;\n};\n\n/**\n * Grants the token endpoint refuses permanently. A dead grant means the login\n * is gone, not that the network blipped, so only these mark the session.\n */\nconst DEAD_GRANT_CODES = new Set(['invalid_grant', 'invalid_client']);\n\n// One refresh-token grant. A failure returns the same credentials: the old token may still have minutes of life.\nexport async function rotateCredentials(\n credentials: Credentials,\n baseUrl: string | undefined,\n): Promise<Credentials> {\n if (!credentials.refreshToken) return credentials;\n try {\n const token = await refreshAccessToken(\n credentials.refreshToken,\n baseUrl,\n credentials.oauthClientId,\n );\n return {\n ...credentials,\n accessToken: token.access_token,\n missingScopes: missingOAuthScopes(\n [...WIZARD_OAUTH_SCOPES, ...(credentials.missingScopes ?? [])],\n token.scope,\n ),\n // Rotation: keep the returned refresh token or the old one stops working.\n refreshToken: token.refresh_token ?? credentials.refreshToken,\n expiresAt: Date.now() + token.expires_in * 1000,\n };\n } catch (error) {\n // A dead grant is recorded but not thrown. If a 401 does follow, the auth-error screen can name the cause.\n if (error instanceof OAuthError && DEAD_GRANT_CODES.has(error.code)) {\n markGrantRevoked();\n analytics.wizardCapture('auth session expired', { reason: error.code });\n }\n logToFile(\n '[oauth] token refresh failed, continuing with the existing token:',\n error instanceof Error ? error.message : error,\n );\n return credentials;\n }\n}\n","/** The invocation's login: the caller's, the store's, or a provider's, recorded in the store. */\nimport { analytics, groupsFromUser } from '@utils/analytics';\nimport type {\n CredentialsProvider,\n ResolvedProgramCredentials,\n} from './credentials';\nimport type { SessionStore } from './session/session-store';\n\n/**\n * Log in for `programId` once per store: a login the caller holds wins, then\n * the store's, then `provider`'s. A new login is written to the store, and the\n * user is identified so feature flags can target them.\n */\nexport async function logIn(\n programId: string,\n store: SessionStore,\n options: {\n credentials?: ResolvedProgramCredentials;\n provider?: CredentialsProvider;\n signal: AbortSignal;\n },\n): Promise<ResolvedProgramCredentials> {\n const held = store.session.credentials;\n let login: ResolvedProgramCredentials | undefined =\n options.credentials ??\n (held\n ? {\n posthog: held,\n project: store.session.apiProject,\n apiUser: store.session.apiUser,\n roleAtOrganization: store.session.roleAtOrganization,\n }\n : undefined);\n if (!login && options.provider) {\n login = await options.provider.resolve(programId, {\n signal: options.signal,\n });\n }\n if (!login) {\n throw new Error(`Credentials are required to run ${programId}.`);\n }\n // A dev or test `--ci` run carries a pre-issued gateway token on its login.\n const gateway = store.session.ciGateway;\n if (gateway && !login.posthog.gateway) {\n login = { ...login, posthog: { ...login.posthog, gateway } };\n }\n if (login.posthog !== held) {\n store.setLogin({\n posthog: login.posthog,\n project: login.project,\n apiUser: login.apiUser,\n roleAtOrganization: login.roleAtOrganization,\n });\n }\n if (login.apiUser) analytics.identifyUser(login.apiUser);\n analytics.setGroups(\n groupsFromUser(login.apiUser, login.posthog.host.apiHost),\n );\n return login;\n}\n","/** The status of one task in the run's task list. */\nexport enum TaskStatus {\n Pending = 'pending',\n InProgress = 'in_progress',\n Completed = 'completed',\n Skipped = 'skipped',\n Failed = 'failed',\n}\n\nexport function isTaskStatus(value: string): value is TaskStatus {\n return (Object.values(TaskStatus) as string[]).includes(value);\n}\n","/**\n * SessionStore — the session and run state every host shares.\n *\n * The TUI and headless each build one, and an embedder may build its own;\n * `runProgram` reads the session from it and writes detection, the login,\n * readiness and the run's progress back. Every write goes through a setter, so\n * a subscriber sees each change and nobody holds a stale copy. The TUI layers\n * its display state, screens and gates on top and reacts to this store's\n * changes.\n */\n\nimport { atom, map, type MapStore } from 'nanostores';\nimport type {\n AgentProgress,\n AskAnswers,\n PendingQuestion,\n TaskNotice,\n} from '@agent/types';\nimport type { Credentials } from '@shared/api';\nimport type { DiscoveredFeature } from '@shared/discovered-feature';\nimport {\n WizardReadiness,\n getBlockingServiceKeys,\n type WizardReadinessResult,\n} from '@shared/health-checks/readiness';\nimport { OutroKind, type OutroData } from '@shared/outro';\nimport { RunPhase, ScanConsent } from '@shared/run-state';\nimport { appendStatus } from '@shared/status-history';\nimport { TaskStatus, isTaskStatus } from '@shared/task-status';\nimport { analytics } from '@utils/analytics';\nimport { logToFile } from '@utils/debug';\nimport { reportWarehouseSourcesDetected } from '../detection/integration';\nimport type { ProgramReadyContext } from '../program-step';\nimport type { WizardSession } from './wizard-session';\n\nexport interface TaskItem {\n id?: string;\n source?: string;\n sourceStatus?: string;\n label: string;\n activeForm?: string;\n status: TaskStatus;\n /** Legacy compat */\n done: boolean;\n}\n\nexport interface PlannedEvent {\n name: string;\n description: string;\n}\n\n/** A login the store records: what a credentials provider resolved. */\nexport type SessionLogin = {\n posthog: Credentials;\n project: WizardSession['apiProject'];\n apiUser: WizardSession['apiUser'];\n roleAtOrganization?: string | null;\n};\n\n// Capture blocked skill downloads once per readiness result.\nfunction captureHealthCheckBlocked(result: WizardReadinessResult): void {\n try {\n const health = result.health;\n const blockingKeys = getBlockingServiceKeys(health);\n const attempts = health.skillsOrigin.rawIndicator?.match(/attempts=(\\d+)/);\n const retriesUsed = Math.max(0, attempts ? Number(attempts[1]) - 1 : 0);\n\n analytics.wizardCapture('health check blocked', {\n decision: 'skills-origin-down',\n blocking_keys: blockingKeys,\n retries_used: retriesUsed,\n });\n } catch (err) {\n logToFile(\n `[health-checks] failed to capture analytics: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n}\n\nexport class SessionStore {\n private readonly $session: MapStore<WizardSession>;\n private readonly $statusMessages = atom<string[]>([]);\n private readonly $tasks = atom<TaskItem[]>([]);\n private readonly $eventPlan = atom<PlannedEvent[]>([]);\n private readonly $handoffText = atom<string | null>(null);\n private readonly $version = atom(0);\n private batchDepth = 0;\n private batchDirty = false;\n\n private noteAskProgressFn: (() => void) | null = null;\n private resolvePendingQuestionFn: ((answers: AskAnswers) => void) | null =\n null;\n private resolveTaskNoticeFn: ((keep: boolean) => void) | null = null;\n\n constructor(session: WizardSession) {\n this.$session = map<WizardSession>(session);\n }\n\n // ── Reads ─────────────────────────────────────────────────────────\n\n get session(): WizardSession {\n return this.$session.get();\n }\n\n /** Replace the whole session, e.g. once the host has built it from its launch values. */\n set session(value: WizardSession) {\n this.$session.set(value);\n this.emitChange();\n }\n\n get statusMessages(): string[] {\n return this.$statusMessages.get();\n }\n\n get tasks(): TaskItem[] {\n return this.$tasks.get();\n }\n\n get eventPlan(): PlannedEvent[] {\n return this.$eventPlan.get();\n }\n\n get handoffText(): string | null {\n return this.$handoffText.get();\n }\n\n getFrameworkContext(key: string): unknown {\n return this.session.frameworkContext[key];\n }\n\n // ── Change notification ───────────────────────────────────────────\n\n /** Called after every change, with no initial call. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void {\n return this.$version.listen(() => listener());\n }\n\n getVersion(): number {\n return this.$version.get();\n }\n\n private emitChange(): void {\n if (this.batchDepth > 0) {\n this.batchDirty = true;\n return;\n }\n this.$version.set(this.$version.get() + 1);\n }\n\n /** Make several writes, then notify once. */\n batch(writes: () => void): void {\n this.batchDepth += 1;\n try {\n writes();\n } finally {\n this.batchDepth -= 1;\n if (this.batchDepth === 0 && this.batchDirty) {\n this.batchDirty = false;\n this.emitChange();\n }\n }\n }\n\n // ── Session writes ────────────────────────────────────────────────\n\n /** Write several session fields, then notify once. */\n update(patch: Partial<WizardSession>): void {\n for (const [key, value] of Object.entries(patch)) {\n this.$session.setKey(key as never, value as never);\n }\n this.emitChange();\n }\n\n private set<K extends keyof WizardSession>(\n key: K,\n value: WizardSession[K],\n ): void {\n this.$session.setKey(key, value as never);\n this.emitChange();\n }\n\n /**\n * Hand `work` a copy of the session, then write back what it changed. For\n * program code that writes to the session object it is given, such as\n * `ciPreRun` and a `run` function. Framework-context keys merge into the\n * live context, so writes made meanwhile through a setter are kept.\n */\n async edit<T>(work: (draft: WizardSession) => Promise<T> | T): Promise<T> {\n const before = this.session;\n const draft: WizardSession = {\n ...before,\n frameworkContext: { ...before.frameworkContext },\n };\n try {\n return await work(draft);\n } finally {\n const patch: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(draft)) {\n if (key === 'frameworkContext') continue;\n if (value !== before[key as keyof WizardSession]) {\n patch[key] = value;\n }\n }\n const context = Object.entries(draft.frameworkContext).filter(\n ([key, value]) => before.frameworkContext[key] !== value,\n );\n if (context.length > 0) {\n patch.frameworkContext = {\n ...this.session.frameworkContext,\n ...Object.fromEntries(context),\n };\n }\n if (Object.keys(patch).length > 0)\n this.update(patch as Partial<WizardSession>);\n }\n }\n\n setRunPhase(phase: RunPhase): void {\n this.$session.setKey('runPhase', phase);\n analytics.setTag('run_phase', phase);\n this.emitChange();\n }\n\n /** Record a login: the credentials, the project, the user and their role. The login's host reports `auth complete`. */\n setLogin(login: SessionLogin): void {\n if (login.posthog.projectId) {\n analytics.setTag('project_id', login.posthog.projectId);\n }\n this.update({\n credentials: login.posthog,\n apiProject: login.project,\n apiUser: login.apiUser,\n roleAtOrganization:\n login.roleAtOrganization ?? login.apiUser?.role_at_organization ?? null,\n });\n }\n\n setCredentials(credentials: WizardSession['credentials']): void {\n this.$session.setKey('credentials', credentials);\n if (credentials?.projectId) {\n analytics.setTag('project_id', credentials.projectId);\n }\n analytics.wizardCapture('auth complete', {\n project_id: credentials?.projectId,\n });\n this.emitChange();\n }\n\n /** Post-refresh credential swap: no `auth complete`, the user logged in once. */\n setAccessToken(credentials: WizardSession['credentials']): void {\n this.set('credentials', credentials);\n }\n\n setRoleAtOrganization(role: string | null): void {\n this.set('roleAtOrganization', role);\n }\n\n setApiUser(user: WizardSession['apiUser']): void {\n this.set('apiUser', user);\n }\n\n setFrameworkConfig(\n integration: WizardSession['integration'],\n config: WizardSession['frameworkConfig'],\n ): void {\n this.$session.setKey('integration', integration);\n this.$session.setKey('frameworkConfig', config);\n this.$session.setKey('unsupportedVersion', null);\n if (integration) analytics.setTag('integration', integration);\n this.emitChange();\n }\n\n setFrameworkContext(key: string, value: unknown): void {\n this.set('frameworkContext', {\n ...this.session.frameworkContext,\n [key]: value,\n });\n }\n\n setDetectionComplete(): void {\n this.set('detectionComplete', true);\n }\n\n setDetectedFramework(label: string): void {\n this.$session.setKey('detectedFrameworkLabel', label);\n analytics.setTag('detected_framework', label);\n this.emitChange();\n }\n\n setPosthogSdkDetected(detected: boolean): void {\n this.set('posthogSdkDetected', detected);\n }\n\n setSkillId(skillId: string | null): void {\n this.set('skillId', skillId);\n }\n\n setUnsupportedVersion(\n info: NonNullable<WizardSession['unsupportedVersion']>,\n ): void {\n this.set('unsupportedVersion', info);\n }\n\n addDiscoveredFeature(feature: DiscoveredFeature): void {\n if (this.session.discoveredFeatures.includes(feature)) return;\n this.set('discoveredFeatures', [\n ...this.session.discoveredFeatures,\n feature,\n ]);\n }\n\n /**\n * Sharing is on: either the user turned it back on in the panel, or they\n * pressed Continue without ever touching it. Both are reversible until\n * completeSetup() resolves the intro gate and reports.\n */\n grantSharing(): void {\n this.set('scanConsent', ScanConsent.Granted);\n }\n\n /**\n * Sharing is off. Suppresses reporting only — local detection still ran and\n * the results stay in the session, so the outro suggestion and the warehouse\n * task are unaffected; see `scanConsent` on `WizardSession`.\n *\n * Deliberately does not report. The panel's toggle can come back here, so\n * marking the run reported would strand a user who turns sharing off and\n * then on again. completeSetup() owns the single report.\n */\n declineSharing(): void {\n this.set('scanConsent', ScanConsent.Declined);\n }\n\n /**\n * reportWarehouseSourcesDetected() is the single place scan results turn\n * into telemetry; this just supplies its idempotency flag via the normal\n * setter path (never mutate session directly). A no-op once\n * `warehouseSourcesReported` is set, or for any program that never\n * populated a warehouse-scan result in the first place.\n */\n markWarehouseSourcesReportedIfNeeded(): void {\n if (reportWarehouseSourcesDetected(this.session)) {\n this.set('warehouseSourcesReported', true);\n }\n }\n\n setAiSdkStampReported(): void {\n if (this.session.aiSdkStampReported) return;\n this.set('aiSdkStampReported', true);\n }\n\n setReadinessResult(result: WizardReadinessResult | null): void {\n this.$session.setKey('readinessResult', result);\n if (result && result.decision === WizardReadiness.No) {\n captureHealthCheckBlocked(result);\n }\n this.emitChange();\n }\n\n setOutroData(data: OutroData): void {\n this.set('outroData', data);\n }\n\n setDashboardUrl(url: string): void {\n logToFile(`store.setDashboardUrl: ${url}`);\n this.set('dashboardUrl', url);\n }\n\n setNotebookUrl(url: string): void {\n logToFile(`store.setNotebookUrl: ${url}`);\n this.set('notebookUrl', url);\n }\n\n // ── Run progress ──────────────────────────────────────────────────\n\n pushStatus(message: string): void {\n const msgs = this.$statusMessages.get();\n const next = appendStatus(msgs, message);\n if (next === msgs) return;\n this.$statusMessages.set(next);\n this.emitChange();\n }\n\n setTasks(tasks: TaskItem[]): void {\n this.$tasks.set(tasks);\n this.emitChange();\n }\n\n updateTask(index: number, done: boolean): void {\n const tasks = this.$tasks.get();\n if (!tasks[index]) return;\n const updated = [...tasks];\n updated[index] = {\n ...updated[index],\n done,\n status: done ? TaskStatus.Completed : TaskStatus.Pending,\n };\n this.$tasks.set(updated);\n this.emitChange();\n }\n\n /** Replace the live tasks with the agent's list, keeping finished ones from other sources. */\n syncTodos(\n todos: Array<{\n id?: string;\n source?: string;\n content: string;\n status: string;\n activeForm?: string;\n }>,\n ): void {\n const incoming = todos.map((t) => {\n const status = isTaskStatus(t.status) ? t.status : TaskStatus.Pending;\n return {\n id: t.id,\n source: t.source,\n sourceStatus: isTaskStatus(t.status) ? undefined : t.status,\n label: t.content,\n activeForm: t.activeForm,\n status,\n done: status === TaskStatus.Completed,\n };\n });\n const incomingLabels = new Set(incoming.map((t) => t.label));\n const sources = new Set(todos.map((t) => t.source));\n const retained = this.$tasks\n .get()\n .filter(\n (t) =>\n (t.status === TaskStatus.Completed ||\n t.status === TaskStatus.Failed ||\n t.status === TaskStatus.Skipped) &&\n (t.source ? !sources.has(t.source) : !incomingLabels.has(t.label)),\n );\n this.$tasks.set([...retained, ...incoming]);\n this.emitChange();\n }\n\n setEventPlan(events: PlannedEvent[]): void {\n this.$eventPlan.set(events);\n this.emitChange();\n }\n\n /** No-op on identical text: a change here means a network push downstream. */\n setHandoffText(text: string): void {\n if (this.$handoffText.get() === text) return;\n logToFile(`store.setHandoffText: ${text.length} chars`);\n this.$handoffText.set(text);\n this.emitChange();\n }\n\n // ── Questions and notices the agent waits on ──────────────────────\n\n /**\n * Hold the agent's wizard_ask request until an answerer resolves it: the\n * TUI's WizardAsk screen, or an embedder's answerer.\n *\n * Only one request is in flight at a time — calling this while a request\n * is already pending throws.\n */\n requestQuestion(\n question: PendingQuestion,\n onAnswer?: () => void,\n ): Promise<AskAnswers> {\n if (this.resolvePendingQuestionFn) {\n throw new Error(\n 'requestQuestion called while another wizard_ask request is pending',\n );\n }\n const answered = new Promise<AskAnswers>((resolve) => {\n this.resolvePendingQuestionFn = resolve;\n });\n this.noteAskProgressFn = onAnswer ?? null;\n this.set('pendingQuestion', question);\n analytics.wizardCapture('wizard_ask shown', {\n source: question.source,\n question_count: question.questions.length,\n kinds: question.questions.map((q) => q.kind),\n });\n return answered;\n }\n\n /**\n * Report that the user answered one question of the in-flight request and\n * another is coming — the ask bridge's timeout heartbeat.\n */\n noteAskProgress(): void {\n this.noteAskProgressFn?.();\n }\n\n /**\n * Resolve the in-flight wizard_ask request with the user's answers.\n * Answers flow back to the agent as the tool result.\n */\n resolvePendingQuestion(answers: AskAnswers): void {\n const resolve = this.resolvePendingQuestionFn;\n this.resolvePendingQuestionFn = null;\n this.noteAskProgressFn = null;\n this.set('pendingQuestion', null);\n resolve?.(answers);\n }\n\n /**\n * Cancel the in-flight wizard_ask request — the bridge sends a sentinel\n * answer (\"__cancelled__\") so the skill can decide how to handle it.\n */\n cancelPendingQuestion(): void {\n const pending = this.session.pendingQuestion;\n if (!pending) return;\n const cancelled: AskAnswers = {};\n for (const q of pending.questions) cancelled[q.id] = '__cancelled__';\n this.resolvePendingQuestion(cancelled);\n }\n\n /**\n * Show an optional step's notice and return whether to keep that step.\n * Asked before the step runs, so nobody is surprised by a prompt mid-run.\n */\n showTaskNotice(notice: TaskNotice): Promise<boolean> {\n const answered = new Promise<boolean>((resolve) => {\n this.resolveTaskNoticeFn = resolve;\n });\n this.set('taskNotice', notice);\n return answered;\n }\n\n /** Resolve the notice, keeping (`true`) or skipping (`false`) the step. */\n resolveTaskNotice(keep: boolean): void {\n const resolve = this.resolveTaskNoticeFn;\n this.resolveTaskNoticeFn = null;\n this.set('taskNotice', null);\n resolve?.(keep);\n }\n\n // ── Views for program hooks ───────────────────────────────────────\n\n /** The writes a program's `onReady` detection makes, through this store. */\n readyContext(): ProgramReadyContext {\n const read = (): WizardSession => this.session;\n return {\n get session() {\n return read();\n },\n setFrameworkContext: (k, v) => this.setFrameworkContext(k, v),\n setFrameworkConfig: (i, c) =>\n this.setFrameworkConfig(\n i as WizardSession['integration'],\n c as WizardSession['frameworkConfig'],\n ),\n setDetectedFramework: (l) => this.setDetectedFramework(l),\n setPosthogSdkDetected: (d) => this.setPosthogSdkDetected(d),\n setSkillId: (id) => this.setSkillId(id),\n setUnsupportedVersion: (info) =>\n this.setUnsupportedVersion(\n info as NonNullable<WizardSession['unsupportedVersion']>,\n ),\n addDiscoveredFeature: (f) => this.addDiscoveredFeature(f),\n setDetectionComplete: () => this.setDetectionComplete(),\n };\n }\n}\n\n/** Record an agent progress event that is run state; display-only events change nothing here. */\nexport function applyAgentProgress(\n store: SessionStore,\n event: AgentProgress,\n): void {\n switch (event.kind) {\n case 'lifecycle':\n if (event.phase === 'started') {\n store.setRunPhase(RunPhase.Running);\n return;\n }\n store.batch(() => {\n if (!store.session.outroData) {\n store.setOutroData({\n kind: OutroKind.Success,\n message: event.message,\n });\n }\n if (store.session.runPhase === RunPhase.Running) {\n store.setRunPhase(RunPhase.Completed);\n }\n });\n return;\n case 'status':\n store.pushStatus(event.message);\n return;\n case 'tasks':\n store.syncTodos(\n event.tasks.map((t) => ({\n id: t.id,\n source: t.source,\n content: t.content,\n status: t.status,\n activeForm: t.activeForm,\n })),\n );\n return;\n case 'url':\n if (event.which === 'dashboard') store.setDashboardUrl(event.url);\n else store.setNotebookUrl(event.url);\n return;\n case 'handoff':\n store.setHandoffText(event.text);\n return;\n case 'completion': {\n // A link the agent printed during the run wins over the outro's own.\n const { dashboardUrl, notebookUrl } = store.session;\n store.setOutroData({\n ...event.outro,\n dashboardUrl: dashboardUrl ?? event.outro.dashboardUrl ?? undefined,\n notebookUrl: notebookUrl ?? event.outro.notebookUrl ?? undefined,\n });\n return;\n }\n case 'binding': // display-only: the agent reports its pick, no host reads it\n case 'spinner':\n case 'log':\n case 'stage':\n case 'usage':\n case 'finalCost':\n case 'authError':\n case 'activity':\n return;\n default: {\n const unhandled: never = event;\n throw new Error(`Unhandled agent progress: ${JSON.stringify(unhandled)}`);\n }\n }\n}\n","/** Run a registered program on a session store the caller owns. It never exits the process. */\nimport path from 'path';\nimport { randomUUID } from 'crypto';\nimport { DEFAULT_BINDING, runAgent, RunOutcome } from '@agent';\nimport type {\n AgentProgress,\n AgentRunDefinition,\n RunHooks,\n RunInput,\n RunResult,\n} from '@agent/types';\nimport { getSkillsBaseUrl, type Integration } from '@shared/constants';\nimport { classifyRunFailure, ErrorCodes, type ErrorCode } from '@shared/errors';\nimport {\n backupAndFixClaudeSettings,\n checkAllSettingsConflicts,\n classifySettingsConflicts,\n restoreClaudeSettings,\n} from '@shared/claude-settings';\nimport {\n evaluateWizardReadiness,\n getBlockingServiceKeys,\n SERVICE_LABELS,\n SIGNUP_WIZARD_READINESS_CONFIG,\n WizardReadiness,\n} from '@shared/health-checks/readiness';\nimport { OutroKind } from '@shared/outro';\nimport { mayReportScanResults, RunPhase } from '@shared/run-state';\nimport {\n configureOAuthSession,\n currentCredentials,\n} from '@shared/oauth-session';\nimport { analytics } from '@utils/analytics';\nimport { registerCleanup } from '@utils/cleanup';\nimport { logToFile } from '@utils/debug';\nimport { removeAuditLedger, startAuditLedgerWatcher } from '@programs/audit';\nimport { FRAMEWORK_REGISTRY } from './frameworks/registry';\nimport { findProgramConfig, getProgramConfig } from './program-registry';\nimport { ProgramAbort } from './program-abort';\nimport { detectionFailure, detectProgram } from './detect-program';\nimport {\n rotateCredentials,\n type ResolvedProgramCredentials,\n} from './credentials';\nimport { stampAiSdkDetected } from './detection/ai-sdk-stamp';\nimport { logIn } from './login';\nimport { getDetectedWarehouseSources } from './warehouse-sources/detect';\nimport { applyAgentProgress, type SessionStore } from './session/session-store';\nimport type { WizardSession } from './session/wizard-session';\nimport type { ProgramConfig, ProgramRunStep } from './program-step';\nimport type { ProgramRun } from './program-run';\nimport type { ProgramSession } from './program-session';\nimport type { CiRunnerContext, RunnerContext } from './runner-context';\nimport type {\n ProgramDiagnostic,\n ProgramInput,\n ProgramOptions,\n ProgramProgress,\n ProgramRunOutcome,\n ProgramStep,\n WizardFlagSnapshot,\n} from './program-input';\n\n/** The session's frameworkContext slot holding an orchestrated run's final task outcomes. */\nexport const TASK_OUTCOMES_KEY = 'orchestrator-task-outcomes';\n\n/** Handed to the caller's capabilities when it supplied no signal. */\nconst NEVER_ABORTED = new AbortController().signal;\n\nconst MAX_DIAGNOSTICS = 10;\n\ntype Failure = NonNullable<RunResult['failure']>;\n\n/** Fields that carry functions, class instances or the caller's store; everything else is data. */\nconst KEPT_BY_REFERENCE = [\n 'store',\n 'config',\n 'credentials',\n] as const satisfies readonly (keyof ProgramInput)[];\n\n/** Copy the caller's input, so a later caller write cannot reach the run or its hooks. */\nfunction snapshotProgramInput(input: ProgramInput): ProgramInput {\n const data: Partial<ProgramInput> = { ...input };\n for (const key of KEPT_BY_REFERENCE) delete data[key];\n return { ...input, ...structuredClone(data) };\n}\n\n/** One agent run of the invocation: a composed sub-run from `runSteps`, or the program's own. */\ntype PlannedRun = {\n stepId: string; // the host's step: a `runSteps` key, or `run`\n config: ProgramConfig; // the program whose agent runs\n runStep?: ProgramRunStep; // the run's directory and prep\n own: boolean; // the program's own run, not a composed one\n};\n\n/**\n * Run a registered program: detection, readiness, login, the host's gates,\n * then each agent run in order. Every write lands in `input.store`, and the\n * store ends settled even when the call rejects.\n */\nexport async function runProgram(\n programId: string,\n callerInput: ProgramInput,\n options: ProgramOptions = {},\n): Promise<ProgramRunOutcome> {\n try {\n return await invokeProgram(programId, callerInput, options);\n } catch (error) {\n recordThrown(callerInput.store, error);\n throw error;\n }\n}\n\nasync function invokeProgram(\n programId: string,\n callerInput: ProgramInput,\n options: ProgramOptions,\n): Promise<ProgramRunOutcome> {\n const input = snapshotProgramInput(callerInput);\n const { store } = input;\n // An unregistered id runs on input.config alone.\n const config = {\n ...findProgramConfig(programId),\n ...input.config,\n id: programId,\n } as ProgramConfig;\n const signal = options.signal ?? NEVER_ABORTED;\n const runId = input.runId ?? randomUUID();\n const progress = createProgress(options.onProgress);\n const runResults: RunResult[] = [];\n const artifacts: ProgramRunOutcome['artifacts'] = {};\n\n const settle = (\n outcome: RunOutcome,\n failure?: Failure,\n ): ProgramRunOutcome => {\n // A caller cancel through the signal is not a run failure, so no host shows a failure screen for it.\n if (failure && signal.aborted) recordCancel(store, failure);\n else if (failure) recordFailure(store, failure);\n else recordSuccess(store);\n // A throwing progress handler is kept as a diagnostic, so log it.\n const diagnostics = progress.diagnostics();\n for (const diagnostic of diagnostics) {\n logToFile(\n `[agent-runner] progress diagnostic (${diagnostic.eventKind} run=${diagnostic.runId}): ${diagnostic.message}`,\n );\n }\n return {\n programId,\n outcome,\n runResults,\n artifacts,\n diagnostics,\n ...(failure && { failure }),\n };\n };\n const fail = (\n message: string,\n code: ErrorCode = ErrorCodes.InternalUnhandled,\n ) => settle(RunOutcome.Failed, { code, message });\n const abort = (message: string) =>\n settle(RunOutcome.Aborted, { code: ErrorCodes.AgentAbort, message });\n const cancelled = () => abort('Run cancelled by the caller.');\n\n if (signal.aborted) return cancelled();\n if (!config.run)\n return fail(`Program \"${programId}\" has no run configuration.`);\n\n /** Await a caller capability; a caller abort wins over its answer. */\n const park = <T>(work: Promise<T>): Promise<T> => parkOn(signal, work);\n // Log lines and the spinner from program code arrive as the program's own run's progress.\n const emit = (event: AgentProgress) => progress.deliver(runId, event);\n const runner = storeRunner(store, emit);\n const login = () =>\n park(\n logIn(programId, store, {\n credentials: input.credentials,\n provider: options.credentials,\n signal,\n }),\n );\n\n try {\n if (!store.session.detectionComplete) {\n await detectProgram(config, store, {\n log: runner.log,\n authenticate: async () => {\n await login();\n },\n onProgress: emit,\n } satisfies CiRunnerContext);\n }\n } catch (error) {\n // A decided stop ends the run cancelled, as main's wizardAbort({ code, message }) did.\n if (error instanceof ProgramAbort) {\n return settle(RunOutcome.Aborted, {\n code: error.code,\n message: error.message,\n });\n }\n if (signal.aborted) return cancelled();\n // A crash keeps its error, so the host reports its stack.\n const { code, message } = classifyRunFailure(error);\n return settle(RunOutcome.Crashed, {\n code,\n message,\n error: error instanceof Error ? error : new Error(message),\n });\n }\n const blocked = detectionFailure(config, store.session);\n if (blocked) return settle(RunOutcome.Failed, blocked);\n\n // Started before the run resolves: an audit seeds the ledger from inside its recipe.\n const installDir = store.session.installDir;\n const ledgerFile = config.auditLedgerFile;\n const ledger = ledgerFile\n ? startAuditLedgerWatcher(installDir, ledgerFile, runner)\n : null;\n let ledgerReleased = false;\n const releaseLedger = () => {\n if (ledgerReleased || !ledgerFile) return;\n ledgerReleased = true;\n // Read a last write the watch debounce hasn't picked up before stopping.\n ledger?.refresh();\n ledger?.stop();\n removeAuditLedger(installDir, ledgerFile);\n };\n const unregisterLedger = ledger ? registerCleanup(releaseLedger) : undefined;\n let plainSettings: { backedUp: boolean } | undefined;\n try {\n let run: AgentRunDefinition;\n try {\n run = await resolveRun(config, store, runner);\n } catch (error) {\n if (error instanceof ProgramAbort) return fail(error.message, error.code);\n throw error;\n }\n store.setSkillId(run.skillId ?? run.integrationLabel);\n\n const plannedRuns = planRuns(config, options);\n const outage = await checkReadiness(programId, config, store, {\n workflow: options.workflow,\n park,\n signal,\n emit,\n });\n if (outage) return settle(RunOutcome.Failed, outage);\n\n analytics.wizardCapture('agent started', {\n integration: run.integrationLabel,\n program_id: programId,\n skill_id: run.skillId ?? null,\n });\n\n // Everything before the first agent run: a rejection fails the run, unless the caller aborted.\n let credentials: ResolvedProgramCredentials;\n let flags: WizardFlagSnapshot = {\n flags: { ...input.wizardFlags },\n payloads: { ...input.wizardFlagPayloads },\n };\n try {\n // A plain run's directory is known now, so its settings are checked before login. A scoped or composed run checks its own after its prep.\n if (plannedRuns.some((planned) => !planned.runStep)) {\n const settings = await checkSettingsConflicts(programId, installDir, {\n workflow: options.workflow,\n park,\n signal,\n });\n if ('failure' in settings) {\n return settle(RunOutcome.Failed, settings.failure);\n }\n plainSettings = settings;\n }\n credentials = await login();\n if (!store.session.aiSdkStampReported) {\n store.setAiSdkStampReported();\n stampAiSdkDetected({\n apiUser: credentials.apiUser,\n discoveredFeatures: store.session.discoveredFeatures,\n warehouseSources: getDetectedWarehouseSources(store.session),\n mayReportScanResults: mayReportScanResults(store.session),\n });\n }\n if (needsAiApproval(store.session, credentials)) {\n if (!options.workflow) {\n return fail(\n 'AI processing approval is required before this program can run.',\n );\n }\n const approved = await park(\n options.workflow.confirmStep(\n { kind: 'ai-approval', programId, installDir },\n { signal },\n ),\n );\n if (!approved) return abort('AI processing approval declined.');\n }\n if (!input.wizardFlags && options.featureFlags) {\n flags = await park(options.featureFlags());\n }\n } catch (error) {\n if (signal.aborted) return cancelled();\n if (error instanceof ProgramAbort) return fail(error.message, error.code);\n return fail(error instanceof Error ? error.message : String(error));\n }\n\n // Every rotation of this login, before or during a run, lands in the store.\n configureOAuthSession(credentials.posthog, {\n rotate: (held) => rotateCredentials(held, store.session.baseUrl),\n onRefreshed: (refreshed) => {\n const current = store.session.credentials;\n // A refresh replaces only the token fields; the login keeps its host.\n store.setAccessToken(\n current\n ? {\n ...current,\n accessToken: refreshed.accessToken,\n refreshToken: refreshed.refreshToken,\n expiresAt: refreshed.expiresAt,\n }\n : refreshed,\n );\n },\n });\n\n /** One agent run in its own directory; a settled outcome when it can't start. */\n const runOne = async (\n planned: PlannedRun,\n ownRun: AgentRunDefinition | undefined,\n checked: { backedUp: boolean } | undefined,\n ): Promise<RunResult | ProgramRunOutcome> => {\n // A scoped run works on its own copy of the session; its writes don't leak into later runs.\n const scoped = planned.runStep\n ? await scopeSession(planned.runStep, store.session, runner)\n : null;\n if (\n scoped?.detectedFrameworkLabel &&\n scoped.detectedFrameworkLabel !== store.session.detectedFrameworkLabel\n ) {\n store.setDetectedFramework(scoped.detectedFrameworkLabel);\n }\n const session = (): WizardSession => scoped ?? store.session;\n const runDir = session().installDir;\n const agentRun =\n ownRun ?? (await resolveRun(planned.config, store, runner, scoped));\n if (!ownRun) {\n analytics.wizardCapture('agent started', {\n integration: agentRun.integrationLabel,\n program_id: planned.config.id,\n skill_id: agentRun.skillId ?? null,\n });\n }\n\n const settings =\n checked ??\n (await checkSettingsConflicts(programId, runDir, {\n workflow: options.workflow,\n park,\n signal,\n }));\n if ('failure' in settings) {\n return settle(RunOutcome.Failed, settings.failure);\n }\n try {\n // Not parked: a rotation spends the old refresh token, so the new one is kept before a cancel returns.\n const posthog = await currentCredentials(credentials.posthog);\n if (signal.aborted) return cancelled();\n\n if (planned.own) {\n artifacts.reportFile = path.resolve(runDir, agentRun.reportFile);\n }\n const agentRunId = planned.own ? runId : randomUUID();\n const observer = progress.beginRun(\n agentRunId,\n planned.own ? undefined : planned.stepId,\n );\n const current = session();\n const result = await runAgent(\n {\n programId: planned.config.id,\n run: agentRun,\n composed: planned.own ? input.composed ?? false : true,\n // The agent resolves CLI, then flag, then this binding, and reports the pick.\n routing: {\n binding: planned.config.binding ?? DEFAULT_BINDING,\n overrides: {\n harness: current.harness,\n sequence: current.sequence,\n model: current.model,\n },\n },\n skillsBaseUrl: getSkillsBaseUrl(),\n wizardFlags: { ...flags.flags },\n wizardFlagPayloads: { ...flags.payloads },\n allowedTools: planned.config.allowedTools,\n disallowedTools: planned.config.disallowedTools,\n handoffNotebook:\n agentRun.handoffNotebook ?? planned.config.handoffNotebook,\n agentFlow: planned.config.agentFlow,\n excludedTaskTypes: planned.config.excludedTaskTypes,\n seedTasks: planned.config.seedTasks\n ? () => planned.config.seedTasks!(session())\n : undefined,\n hooks: sessionHooks(agentRun, session, (outcomes) => {\n if (scoped) scoped.frameworkContext[TASK_OUTCOMES_KEY] = outcomes;\n else store.setFrameworkContext(TASK_OUTCOMES_KEY, outcomes);\n }),\n },\n {\n installDir: runDir,\n credentials: posthog,\n project: credentials.project,\n apiUser: credentials.apiUser,\n skillId: agentRun.skillId ?? agentRun.integrationLabel,\n integration: current.integration,\n frameworkDocsUrl: frameworkDocsUrl(current),\n flags: runFlags(current),\n host: {\n projectId: current.projectId,\n apiKey: current.apiKey,\n baseUrl: current.baseUrl,\n region: current.region,\n email: current.email,\n },\n },\n {\n interaction: options.interaction,\n onProgress: (event) =>\n observer.onEvent(event, () => applyAgentProgress(store, event)),\n signal: options.signal,\n },\n );\n observer.finish();\n return result;\n } finally {\n // The run neutralized its directory's settings; put them back whatever happened.\n if (settings.backedUp) restoreClaudeSettings(runDir);\n }\n };\n\n for (const planned of plannedRuns) {\n const step: Extract<ProgramStep, { kind: 'run' }> = {\n kind: 'run',\n stepId: planned.stepId,\n programId: planned.config.id,\n };\n let result: RunResult | ProgramRunOutcome;\n try {\n if (options.workflow) {\n const go = await park(options.workflow.confirmStep(step, { signal }));\n if (!go) continue;\n }\n result = await runOne(\n planned,\n planned.own ? run : undefined,\n planned.runStep ? undefined : plainSettings,\n );\n } catch (error) {\n if (signal.aborted) return cancelled();\n if (error instanceof ProgramAbort)\n return fail(error.message, error.code);\n throw error;\n }\n if ('programId' in result) return result;\n runResults.push(result);\n options.workflow?.finishStep?.(step, result);\n if (result.outcome !== RunOutcome.Success) {\n return settle(result.outcome, result.failure);\n }\n }\n return settle(RunOutcome.Success);\n } finally {\n // A plain run's settings were neutralized before login; put them back if the run never started.\n if (plainSettings?.backedUp) restoreClaudeSettings(installDir);\n releaseLedger();\n unregisterLedger?.();\n }\n}\n\n/** The program's own run, and before it every composed sub-run a host workflow can confirm. */\nfunction planRuns(\n config: ProgramConfig,\n options: ProgramOptions,\n): PlannedRun[] {\n const own: PlannedRun = { stepId: 'run', config, own: true };\n // Run steps are the host's flow: with no workflow to confirm them, only the program's own run runs.\n if (!options.workflow) return [own];\n const composed: PlannedRun[] = [];\n for (const [stepId, runStep] of Object.entries(config.runSteps ?? {})) {\n if (runStep.runProgramId) {\n composed.push({\n stepId,\n config: getProgramConfig(runStep.runProgramId),\n runStep,\n own: false,\n });\n } else {\n own.stepId = stepId;\n own.runStep = runStep;\n }\n }\n return [...composed, own];\n}\n\n/** A program's run definition. A `run` function writes to the session copy it is handed. */\nasync function resolveRun(\n config: ProgramConfig,\n store: SessionStore,\n runner: RunnerContext,\n scoped?: WizardSession | null,\n): Promise<AgentRunDefinition> {\n const run = config.run;\n if (!run) {\n throw new ProgramAbort({\n code: ErrorCodes.InternalUnhandled,\n message: `Program \"${config.id}\" has no run configuration.`,\n });\n }\n if (typeof run !== 'function') return run;\n if (scoped) return run(scoped, runner);\n return store.edit((draft) => run(draft, runner));\n}\n\n/** The session a scoped run works on: the step's directory and its own framework context, after any prep. */\nasync function scopeSession(\n runStep: ProgramRunStep,\n live: WizardSession,\n runner: RunnerContext,\n): Promise<WizardSession | null> {\n if (!runStep.targetDir && !runStep.onRunPrep) return null;\n const session: WizardSession = {\n ...live,\n installDir: runStep.targetDir ? runStep.targetDir(live) : live.installDir,\n frameworkContext: { ...live.frameworkContext },\n };\n if (runStep.onRunPrep) await runStep.onRunPrep(session, runner.log);\n return session;\n}\n\nfunction needsAiApproval(\n session: WizardSession,\n credentials: ResolvedProgramCredentials,\n): boolean {\n return (\n !session.ci &&\n !session.signup &&\n credentials.apiUser?.organization?.is_ai_data_processing_approved !== true\n );\n}\n\n/** Await `work`; an abort rejects at once, and wins over an answer that lands after it. */\nfunction parkOn<T>(signal: AbortSignal, work: Promise<T>): Promise<T> {\n if (signal.aborted) return Promise.reject(abortReason(signal));\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortReason(signal));\n signal.addEventListener('abort', onAbort, { once: true });\n work.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n if (signal.aborted) reject(abortReason(signal));\n else resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new Error('Run cancelled by the caller.');\n}\n\n/** The program's run effects, through the store and the invocation's progress. */\nfunction storeRunner(\n store: SessionStore,\n emit: (event: AgentProgress) => void,\n): RunnerContext {\n return {\n getFrameworkContext: (key) => store.getFrameworkContext(key),\n setFrameworkContext: (key, value) => store.setFrameworkContext(key, value),\n log: {\n info: (message) => emit({ kind: 'log', level: 'info', message }),\n warn: (message) => emit({ kind: 'log', level: 'warn', message }),\n },\n spinner: () => ({\n start: (message) => emit({ kind: 'spinner', action: 'start', message }),\n stop: (message) => emit({ kind: 'spinner', action: 'stop', message }),\n message: (message) =>\n emit({ kind: 'spinner', action: 'message', message }),\n }),\n };\n}\n\n/** The program's completion hooks, reading the run's session when they fire. */\nfunction sessionHooks(\n run: ProgramRun,\n session: () => ProgramSession,\n recordTaskOutcomes: NonNullable<RunHooks['recordTaskOutcomes']>,\n): RunHooks {\n return {\n postRun: run.postRun\n ? (creds) => run.postRun!(session(), creds)\n : undefined,\n buildOutroData: run.buildOutroData\n ? (creds) => run.buildOutroData!(session(), creds) ?? undefined\n : undefined,\n buildOutroNextSteps: run.buildOutroNextSteps\n ? (creds, completed) =>\n run.buildOutroNextSteps!(session(), creds, completed)\n : undefined,\n recordTaskOutcomes,\n };\n}\n\nfunction runFlags(session: WizardSession): RunInput['flags'] {\n return {\n ci: session.ci,\n signup: session.signup,\n debug: session.debug,\n e2eAsk: session.e2eAsk,\n localMcp: session.localMcp,\n captureAio: session.captureAio,\n benchmark: session.benchmark,\n yaraReport: session.yaraReport,\n };\n}\n\nfunction frameworkDocsUrl(session: ProgramSession): string | undefined {\n const framework = session.integration ?? session.skillId;\n return framework\n ? FRAMEWORK_REGISTRY[framework as Integration]?.metadata.docsUrl\n : undefined;\n}\n\n/** A settled success: a run the agent started ends completed; its outro came from the agent or is its caller's. */\nfunction recordSuccess(store: SessionStore): void {\n if (store.session.runPhase === RunPhase.Running) {\n store.setRunPhase(RunPhase.Completed);\n }\n}\n\n/** A rejection, recorded as its error outro before it reaches the caller; never masks the error. */\nfunction recordThrown(store: SessionStore, error: unknown): void {\n try {\n const { code, message } = classifyRunFailure(error);\n recordFailure(store, { code, message });\n } catch (recordError) {\n logToFile('[run-program] could not record the rejection:', recordError);\n }\n}\n\n/** A settled failure, recorded in the store: the error outro and the phase. */\nfunction recordFailure(store: SessionStore, failure: Failure): void {\n store.batch(() => {\n store.setOutroData(\n failure.outroData ?? {\n kind: OutroKind.Error,\n message: failure.message,\n errorCode: failure.code,\n ...(failure.detail && { errorDetail: failure.detail }),\n },\n );\n store.setRunPhase(RunPhase.Error);\n });\n}\n\n/** A settled caller cancel, recorded in the store: the cancel outro and the phase. */\nfunction recordCancel(store: SessionStore, failure: Failure): void {\n store.batch(() => {\n store.setOutroData({ kind: OutroKind.Cancel, message: failure.message });\n store.setRunPhase(RunPhase.Error);\n });\n}\n\ntype StepContext = {\n workflow: ProgramOptions['workflow'];\n park: <T>(work: Promise<T>) => Promise<T>;\n signal: AbortSignal;\n};\n\n/** Service health, once per invocation, unless the host already checked it. A failure stops the run. */\nasync function checkReadiness(\n programId: string,\n config: ProgramConfig,\n store: SessionStore,\n context: StepContext & { emit: (event: AgentProgress) => void },\n): Promise<Failure | null> {\n const known = store.session.readinessResult;\n if (known) {\n logToFile(\n `[agent-runner] readiness pre-computed by the host: decision=${known.decision} — skipping re-check`,\n );\n return null;\n }\n if (config.healthCheck === false) return null;\n logToFile('[agent-runner] evaluating wizard readiness');\n const readinessConfig = store.session.signup\n ? SIGNUP_WIZARD_READINESS_CONFIG\n : undefined;\n const readiness = await evaluateWizardReadiness(readinessConfig);\n logToFile(`[agent-runner] readiness=${readiness.decision}`);\n store.setReadinessResult(readiness);\n const warn = (message: string) =>\n context.emit({ kind: 'log', level: 'warn', message });\n if (readiness.decision === WizardReadiness.No) {\n const blockingLabels = getBlockingServiceKeys(\n readiness.health,\n readinessConfig,\n ).map((k) => `${SERVICE_LABELS[k]} (${readiness.health[k].status})`);\n logToFile(`[agent-runner] blocked by: ${blockingLabels.join(', ')}`);\n const go = context.workflow\n ? await context.park(\n context.workflow.confirmStep(\n {\n kind: 'service-outage',\n programId,\n installDir: store.session.installDir,\n readiness,\n },\n { signal: context.signal },\n ),\n )\n : true;\n if (!go) {\n return {\n code: ErrorCodes.EnvServiceOutage,\n message:\n 'Cannot start — external services are down:\\n' +\n blockingLabels.map((l) => ` - ${l}`).join('\\n') +\n '\\n\\nPlease try again later.',\n };\n }\n if (!context.workflow) {\n warn('Service health issues detected.');\n const blockingKeys = getBlockingServiceKeys(readiness.health);\n if (blockingKeys.length > 0) {\n warn('Blocking services:');\n for (const key of blockingKeys) {\n const { status, error } = readiness.health[key];\n warn(\n `✖ ${SERVICE_LABELS[key]}: ${status}${error ? ` — ${error}` : ''}`,\n );\n }\n }\n for (const reason of readiness.reasons) warn(reason);\n warn(\n 'Continuing anyway — health checks are advisory in non-interactive runs.',\n );\n }\n } else if (readiness.decision === WizardReadiness.YesWithWarnings) {\n warn('Service health warnings detected.');\n for (const reason of readiness.reasons) warn(reason);\n }\n return null;\n}\n\n/** Claude settings in the run's directory; `backedUp` when the run must restore them. */\nasync function checkSettingsConflicts(\n programId: string,\n installDir: string,\n context: StepContext,\n): Promise<{ backedUp: boolean } | { failure: Failure }> {\n const settingsConflicts = checkAllSettingsConflicts(installDir);\n logToFile(\n `[agent-runner] settings conflicts: ${\n settingsConflicts.length > 0\n ? settingsConflicts\n .map((c) => `${c.source}(${c.keys.join(',')})`)\n .join('; ')\n : 'none'\n }`,\n );\n if (settingsConflicts.length === 0) return { backedUp: false };\n\n for (const conflict of settingsConflicts) {\n const level = conflict.source === 'managed' ? 'org' : conflict.source;\n analytics.wizardCapture('settings conflict detected', {\n level,\n keys: conflict.keys,\n });\n }\n\n const { autoFix, failClosed, warnOnly } =\n classifySettingsConflicts(settingsConflicts);\n\n // User-global and project-local files are already neutralized — the agent\n // runs with settingSources:['project'], so the SDK never reads them. Record\n // it and move on; don't make the user act on a setting that can't bite.\n for (const conflict of warnOnly) {\n logToFile(\n `[agent-runner] settings conflict in ${conflict.source} (${conflict.path}) ` +\n `neutralized by settingSources:['project'] — not blocking`,\n );\n analytics.wizardCapture('settings conflict neutralized', {\n level: conflict.source,\n keys: conflict.keys,\n });\n }\n\n // Writable project settings.json — the SDK *does* read it, but it can be\n // backed up and removed for the run. Neutralize without prompting.\n let backedUp = false;\n let unfixable = failClosed;\n if (autoFix.length > 0) {\n backedUp = backupAndFixClaudeSettings(installDir);\n if (backedUp) {\n logToFile('[agent-runner] auto-neutralized writable settings conflict');\n analytics.wizardCapture('settings conflict auto-neutralized', {\n keys: autoFix.flatMap((c) => c.keys),\n });\n } else {\n // Couldn't remove it — don't run into the redirect; fail closed instead.\n logToFile(\n '[agent-runner] could not back up writable settings conflict — failing closed',\n );\n unfixable = [...failClosed, ...autoFix];\n }\n }\n if (unfixable.length === 0) return { backedUp };\n\n // What can't be neutralized (org-managed, or a writable file that failed to\n // back up) must be fixed by the user, through the host, or the run stops.\n const failure: Failure = {\n code: ErrorCodes.SettingsUnfixableConflict,\n message:\n 'Cannot start — a Claude settings file redirects the agent away ' +\n 'from the PostHog gateway and cannot be neutralized automatically:\\n' +\n unfixable\n .map((c) => ` - ${c.source} (${c.path}): ${c.keys.join(', ')}`)\n .join('\\n') +\n '\\n\\nRemove the conflicting keys and re-run the wizard.',\n };\n if (!context.workflow) return { failure };\n const resolved = await context.park(\n context.workflow.confirmStep(\n {\n kind: 'settings-conflict',\n programId,\n installDir,\n conflicts: unfixable,\n fix: () => {\n const fixed = backupAndFixClaudeSettings(installDir);\n backedUp ||= fixed;\n return fixed;\n },\n },\n { signal: context.signal },\n ),\n );\n if (!resolved) return { failure };\n logToFile('[agent-runner] settings override resolved');\n return { backedUp };\n}\n\n/** Progress to the caller's observer: copied, never awaited, and a throw kept as a diagnostic. */\nfunction createProgress(observer: ProgramOptions['onProgress']) {\n const diagnostics: ProgramDiagnostic[] = [];\n const record = (\n runId: string,\n eventKind: AgentProgress['kind'],\n error: unknown,\n ) => {\n diagnostics.push({\n runId,\n eventKind,\n message: error instanceof Error ? error.message : String(error),\n });\n if (diagnostics.length > MAX_DIAGNOSTICS) diagnostics.shift();\n };\n const deliver = (runId: string, event: AgentProgress, stepId?: string) => {\n if (!observer) return;\n const progress: ProgramProgress = {\n runId,\n ...(stepId !== undefined && { stepId }),\n event: structuredClone(event),\n };\n try {\n const delivery: unknown = observer(progress);\n if (\n delivery &&\n typeof (delivery as PromiseLike<unknown>).then === 'function'\n ) {\n void Promise.resolve(delivery).catch((error: unknown) =>\n record(runId, event.kind, error),\n );\n }\n } catch (error) {\n record(runId, event.kind, error);\n }\n };\n return {\n deliver: (runId: string, event: AgentProgress) => deliver(runId, event),\n /** One agent run's events: recorded as run state, then observed, until it finishes. */\n beginRun(runId: string, stepId?: string) {\n let finished = false;\n return {\n onEvent(event: AgentProgress, applyToStore: () => void) {\n if (finished) {\n record(runId, event.kind, 'progress after finish');\n return;\n }\n try {\n applyToStore();\n } catch (error) {\n record(runId, event.kind, error);\n }\n deliver(runId, event, stepId);\n },\n finish() {\n finished = true;\n },\n };\n },\n diagnostics: () => diagnostics.map((d) => ({ ...d })),\n };\n}\n","/**\n * WizardSession — the launch values and run state every host shares.\n *\n * A host fills the launch values from its arguments; detection, the login and\n * the run fill the rest, written through a `SessionStore`. The TUI keeps its\n * screens' own state in its store, beside the session; headless and embedders\n * have none.\n */\n\nimport { POSTHOG_LOCAL_URL, resolveLocalDev } from '@shared/local-dev';\nimport type { Harness, Integration, Sequence } from '@shared/constants';\nimport type { WizardReadinessResult } from '@shared/health-checks/readiness';\nimport type { ApiProject, GatewayCredential } from '@shared/api';\nimport type { CloudRegion } from '@utils/types';\nimport type { PendingQuestion, TaskNotice } from '@agent/types';\nimport { RunPhase, ScanConsent } from '@shared/run-state';\nimport type { ProgramSession } from '../program-session';\n\nfunction parseProjectIdArg(value: string | undefined): number | undefined {\n if (value === undefined || value === '') return undefined;\n const n = Number(value);\n return Number.isInteger(n) && n > 0 ? n : undefined;\n}\n\nexport interface WizardSession extends ProgramSession {\n /** A pre-issued gateway token a dev or test `--ci` run logs in with; the login puts it on the credentials. */\n ciGateway: GatewayCredential | null;\n email?: string;\n region?: CloudRegion;\n /**\n * Explicit PostHog base URL (`--base-url`). When set, it pins every PostHog\n * origin — API host, cloud/app URL, OAuth server — and `region` is ignored.\n * The runtime equivalent of the dev-build localhost routing; lets the shipped\n * wizard target a local/self-hosted stack. Threaded into the URL helpers in\n * `@utils/urls`. Empty/unset → region-based resolution.\n */\n baseUrl?: string;\n noTelemetry: boolean;\n\n /**\n * `--capture-aio`: mirror every wizard LLM call as an `$ai_generation` event\n * into the authenticated project's AI Observability tab. Dev/test builds\n * only — the flag is undeclared in published builds so this stays `false`\n * there. See `src/agent/aio-capture.ts`.\n */\n captureAio: boolean;\n\n /** `--sequence` override, read in `runProgram`. Wins over the orchestrator flag. */\n sequence?: Sequence;\n /** `--model` override (gateway id), read by `resolveHarness`. Wins over the binding's model. */\n model?: string;\n\n /** True once framework detection has run (whether it found something or not) */\n detectionComplete: boolean;\n\n /** Set when the detected framework version is too old for the wizard */\n unsupportedVersion: {\n current: string;\n minimum: string;\n docsUrl: string;\n } | null;\n /**\n * `role_at_organization` from `/api/users/@me/`. Null when the upstream\n * value is missing (older accounts, fresh signups before onboarding).\n * Drives role-tailored MCP prompt suggestions on the McpSuggestedPromptsScreen.\n *\n * Mirrors `apiUser?.role_at_organization` — kept as a top-level convenience\n * because it has dedicated UI semantics (role-tailored kits) and pre-dates\n * the broader `apiUser` plumbing.\n */\n roleAtOrganization: string | null;\n /**\n * Project payload resolved at authentication, kept so a second agent run in\n * the same invocation (e.g. self-driving's integration phase) reuses the\n * first login wholesale instead of re-authenticating. The resolved region\n * lives on `credentials.host.region`.\n */\n apiProject: ApiProject | null;\n runPhase: RunPhase;\n /** The service health check, once run: by the TUI's health screen, or by `runProgram`. */\n readinessResult: WizardReadinessResult | null;\n /** Copy for the task-notice modal, set while it is open. */\n taskNotice: TaskNotice | null;\n /** Active wizard_ask request, set by the bridge when the agent calls the tool. */\n pendingQuestion: PendingQuestion | null;\n}\n\n/** The launch values a host builds a session from. */\nexport type SessionArgs = {\n debug?: boolean;\n installDir?: string;\n ci?: boolean;\n signup?: boolean;\n /** Harness-only. Set by the e2e TUI host from `E2E_ASK`, never by a flag. */\n e2eAsk?: boolean;\n localDev?: boolean;\n localMcp?: boolean;\n localPosthog?: boolean;\n apiKey?: string;\n email?: string;\n region?: CloudRegion;\n baseUrl?: string;\n integration?: Integration;\n benchmark?: boolean;\n yaraReport?: boolean;\n projectId?: string;\n noTelemetry?: boolean;\n harness?: Harness;\n sequence?: Sequence;\n model?: string;\n captureAio?: boolean;\n};\n\n/** Build a WizardSession from CLI args, pre-populating whatever is known. */\nexport function buildSession(args: SessionArgs): WizardSession {\n const local = resolveLocalDev(args);\n return {\n debug: args.debug ?? false,\n installDir: args.installDir ?? process.cwd(),\n ci: args.ci ?? false,\n signup: args.signup ?? false,\n e2eAsk: args.e2eAsk ?? false,\n ciGateway: null,\n localMcp: local.localMcp,\n apiKey: args.apiKey,\n email: args.email,\n region: args.region,\n // `--local-posthog` is sugar over `--base-url`, which every downstream URL\n // helper already honours. An explicit `--base-url` is more specific, so it wins.\n baseUrl:\n args.baseUrl ?? (local.localPosthog ? POSTHOG_LOCAL_URL : undefined),\n benchmark: args.benchmark ?? false,\n yaraReport: args.yaraReport ?? false,\n projectId: parseProjectIdArg(args.projectId),\n noTelemetry: args.noTelemetry ?? false,\n captureAio: args.captureAio ?? false,\n harness: args.harness,\n sequence: args.sequence,\n model: args.model,\n // No screen can ask in a scripted CI run, so granting keeps CI's\n // telemetry as it was. --signup alone still provisions a brand-new\n // account headlessly, and that user has never seen the disclosure — a\n // headless `--ci --signup` run stays covered by the ci branch above.\n scanConsent: args.ci ? ScanConsent.Granted : ScanConsent.Undecided,\n warehouseSourcesReported: false,\n aiSdkStampReported: false,\n integration: args.integration ?? null,\n frameworkContext: {},\n typescript: false,\n detectedFrameworkLabel: null,\n posthogSdkDetected: false,\n detectionComplete: false,\n unsupportedVersion: null,\n\n runPhase: RunPhase.Idle,\n discoveredFeatures: [],\n credentials: null,\n roleAtOrganization: null,\n apiUser: null,\n apiProject: null,\n readinessResult: null,\n taskNotice: null,\n outroData: null,\n dashboardUrl: null,\n notebookUrl: null,\n skillId: null,\n frameworkConfig: null,\n pendingQuestion: null,\n };\n}\n","/** The agent's questions and notices, held in a session store until an answerer resolves them. */\nimport type { AgentInteraction } from '@agent/types';\nimport { logToFile } from '@utils/debug';\nimport type { SessionStore } from './session-store';\n\n/**\n * Hold each ask and notice in `store` until someone answers it: the TUI's\n * WizardAsk screen, or an embedder's answerer. An abort dismisses the open request.\n */\nexport function storeInteraction(store: SessionStore): AgentInteraction {\n return {\n ask: (question, { signal, onAnswer }) =>\n dismissOnAbort(store.requestQuestion(question, onAnswer), signal, () =>\n store.cancelPendingQuestion(),\n ),\n taskNotice: (notice, { signal }) =>\n dismissOnAbort(store.showTaskNotice(notice), signal, () =>\n store.resolveTaskNotice(false),\n ),\n };\n}\n\n/**\n * Dismiss one open request on abort; a settled one is left alone. A throw\n * inside an abort listener reaches no caller: Node rethrows it as an uncaught\n * exception, so a failed dismissal is logged here instead.\n */\nfunction dismissOnAbort<T>(\n open: Promise<T>,\n signal: AbortSignal,\n dismiss: () => void,\n): Promise<T> {\n const onAbort = () => {\n try {\n dismiss();\n } catch (error) {\n logToFile('[interaction] dismissing an aborted request failed', error);\n }\n };\n // An abort listener added to an already aborted signal never fires.\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n return open.finally(() => signal.removeEventListener('abort', onAbort));\n}\n","/**\n * Dumps every *attempted* sync to a local JSONL file, one line per push. A line\n * means the run published that payload, not that a backend accepted it — `--ci`\n * swaps the PostHog destination for this one, so a synthetic run never creates a\n * session row in a real project.\n *\n * Dev builds only: the payload holds the run's handoff report.\n */\n\nimport { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { IS_PRODUCTION_BUILD } from '@env';\nimport { WIZARD_TASK_STREAM_FILE } from '@utils/paths';\nimport { logToFile } from '@utils/debug';\nimport type {\n StreamEvent,\n TaskStreamDestination,\n TaskStreamUpdate,\n} from '../types';\n\nexport interface FileDestinationOptions {\n path: string;\n /** Override for tests. */\n now?: () => string;\n}\n\nexport class FileDestination implements TaskStreamDestination {\n readonly name = 'file';\n readonly path: string;\n\n private readonly now: () => string;\n private disabled = false;\n\n constructor(opts: FileDestinationOptions) {\n this.path = opts.path;\n this.now = opts.now ?? (() => new Date().toISOString());\n // Truncate up front: one run, one file.\n this.write(() => writeFileSync(this.path, '', { mode: 0o600 }));\n }\n\n send(event: StreamEvent, payload: TaskStreamUpdate): Promise<void> {\n if (!this.disabled) {\n const line = `${JSON.stringify({ at: this.now(), event, payload })}\\n`;\n this.write(() => appendFileSync(this.path, line, { mode: 0o600 }));\n }\n return Promise.resolve();\n }\n\n /** Never throws, never writes to stdio; the first failure ends the dump. */\n private write(op: () => void): void {\n try {\n mkdirSync(dirname(this.path), { recursive: true });\n op();\n } catch (err) {\n this.disabled = true;\n logToFile(\n `[task-stream] file destination disabled (${\n err instanceof Error ? err.message : String(err)\n }): ${this.path}`,\n );\n }\n }\n}\n\n/**\n * Resolve a `--task-stream-log` value, or null when the run did not ask for a\n * dump. `''` and `true` mean the default path — how `--ci` asks for one.\n */\nexport function createFileDestination(\n value: unknown,\n opts: { productionBuild?: boolean; cwd?: string } = {},\n): FileDestination | null {\n if (opts.productionBuild ?? IS_PRODUCTION_BUILD) return null;\n if (value === undefined || value === null || value === false) return null;\n\n const raw = typeof value === 'string' ? value.trim() : '';\n const path =\n raw === ''\n ? WIZARD_TASK_STREAM_FILE\n : isAbsolute(raw)\n ? raw\n : resolve(opts.cwd ?? process.cwd(), raw);\n\n logToFile(`[task-stream] file destination enabled: ${path}`);\n return new FileDestination({ path });\n}\n","/**\n * PostHog destination — pushes wizard run state to the PostHog backend\n * via `POST /api/projects/{project_id}/wizard/sessions/`.\n *\n * The endpoint is an upsert keyed by `(team, session_id)`: 201 means\n * the row was created, 200 means it was updated. Both are success.\n *\n * Failure handling is fail-silent: never throws to the caller, never\n * writes to stdout/stderr, never blocks the agent. Errors flow through\n * the optional `onError` callback for the wizard's debug log.\n *\n * Retry policy:\n * 5xx / network → exponential backoff base 500ms cap 8s, max 3 attempts\n * 429 → honour `Retry-After` (seconds), single retry\n * 401 / 403 → disable for the rest of the run, no further pushes\n * 400 → give up for this push, do not disable\n * other 4xx → give up for this push, do not disable\n */\n\nimport type {\n TaskStreamDestination,\n TaskStreamUpdate,\n StreamEvent,\n} from '../types';\nimport type { Credentials } from '@shared/api';\nimport { logToFile } from '@utils/debug';\n\nexport interface PostHogDestinationOptions {\n /**\n * Lazy credential resolver — called on every send. Returns null\n * before authentication has completed; in that case the send is a\n * no-op (no HTTP request).\n */\n getCredentials: () => Credentials | null;\n /** Receives every error for the wizard's internal logfile. */\n onError?: (err: Error) => void;\n /** Override for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Override for tests. Defaults to setTimeout. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nconst MAX_ATTEMPTS = 3;\nconst BASE_BACKOFF_MS = 500;\nconst MAX_BACKOFF_MS = 8000;\nconst DEFAULT_RETRY_AFTER_MS = 1000;\n// setTimeout silently clamps anything above 2^31-1 ms to fire\n// immediately, so any Retry-After-derived sleep must be capped.\nconst MAX_RETRY_AFTER_MS = 60_000;\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function parseRetryAfter(value: string | null): number {\n if (!value) return DEFAULT_RETRY_AFTER_MS;\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(Math.ceil(seconds * 1000), MAX_RETRY_AFTER_MS);\n }\n // HTTP-date form — best-effort.\n const date = Date.parse(value);\n if (Number.isFinite(date)) {\n return Math.min(Math.max(0, date - Date.now()), MAX_RETRY_AFTER_MS);\n }\n return DEFAULT_RETRY_AFTER_MS;\n}\n\n/**\n * Strip the internal-only `timestamp` field before sending. The\n * backend schema in the RFC does not define it.\n */\nfunction toWirePayload(\n payload: TaskStreamUpdate,\n): Omit<TaskStreamUpdate, 'timestamp'> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { timestamp: _unused, ...rest } = payload;\n return rest;\n}\n\nexport class PostHogDestination implements TaskStreamDestination {\n readonly name = 'posthog';\n\n private readonly getCredentials: () => Credentials | null;\n private readonly onError: (err: Error) => void;\n private readonly fetchImpl: typeof fetch;\n private readonly sleep: (ms: number) => Promise<void>;\n\n private disabled = false;\n\n constructor(opts: PostHogDestinationOptions) {\n this.getCredentials = opts.getCredentials;\n this.onError = opts.onError ?? (() => undefined);\n this.fetchImpl = opts.fetchImpl ?? ((...args) => fetch(...args));\n this.sleep = opts.sleep ?? defaultSleep;\n }\n\n async send(_event: StreamEvent, payload: TaskStreamUpdate): Promise<void> {\n if (this.disabled) return;\n const creds = this.getCredentials();\n if (!creds) return;\n\n await this.postWithRetry(creds, toWirePayload(payload));\n }\n\n private buildRequest(\n creds: Credentials,\n body: object,\n ): { url: string; init: Parameters<typeof fetch>[1] } {\n const url = `${creds.host.apiHost.replace(/\\/$/, '')}/api/projects/${\n creds.projectId\n }/wizard/sessions/`;\n return {\n url,\n init: {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${creds.accessToken}`,\n },\n body: JSON.stringify(body),\n },\n };\n }\n\n private async postWithRetry(creds: Credentials, body: object): Promise<void> {\n const { url, init } = this.buildRequest(creds, body);\n let attempt = 0;\n let backoff = BASE_BACKOFF_MS;\n let retriedAfter429 = false;\n\n while (attempt < MAX_ATTEMPTS) {\n attempt += 1;\n let response: Response;\n try {\n response = await this.fetchImpl(url, init);\n } catch (err) {\n if (attempt >= MAX_ATTEMPTS) {\n this.onError(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n await this.sleep(backoff);\n backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);\n continue;\n }\n\n if (response.ok) {\n logToFile(`[task-stream] wizard/sessions push ok: ${response.status}`);\n return;\n }\n\n const status = response.status;\n\n if (status === 401 || status === 403) {\n this.disabled = true;\n this.onError(new Error(`wizard/sessions auth failed: ${status}`));\n return;\n }\n\n if (status === 429) {\n if (retriedAfter429) {\n this.onError(new Error('wizard/sessions rate limited'));\n return;\n }\n retriedAfter429 = true;\n const wait = parseRetryAfter(response.headers.get('Retry-After'));\n await this.sleep(wait);\n // Don't count this against the 5xx attempt budget.\n attempt -= 1;\n continue;\n }\n\n if (status >= 500) {\n if (attempt >= MAX_ATTEMPTS) {\n this.onError(new Error(`wizard/sessions server error: ${status}`));\n return;\n }\n await this.sleep(backoff);\n backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);\n continue;\n }\n\n if (status === 400) {\n let detail = '';\n try {\n detail = await response.text();\n } catch {\n // ignore\n }\n this.onError(new Error(`wizard/sessions bad request (400): ${detail}`));\n return;\n }\n\n this.onError(new Error(`wizard/sessions unexpected status: ${status}`));\n return;\n }\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { basename, resolve } from 'node:path';\nimport { validate as isUUID } from 'uuid';\nimport { valid as validVersion } from 'semver';\nimport { VERSION } from '@shared/version';\nimport { RunPhase } from '@shared/run-state';\nimport { type Credentials } from '@shared/api';\nimport type { WizardSession } from '../wizard-session';\nimport {\n currentCredentials,\n rotateAfterUnauthorized,\n type UnauthorizedRecovery,\n} from '@shared/oauth-session';\nimport { logToFile } from '@utils/debug';\nimport { parseRetryAfter } from './destinations/posthog';\n\nexport type TaskStreamOutcome = 'completed' | 'failed' | 'cancelled';\ntype RunTask = {\n name: string;\n status: 'created' | 'running' | 'completed' | 'failed';\n};\n// The task fields a snapshot reads, local so task-stream stays off the TUI store.\ntype TaskItem = {\n id?: string;\n source?: string;\n sourceStatus?: string;\n label: string;\n status: string;\n};\ntype Options = {\n mode: 'local' | 'cloud';\n programId: string;\n assignedId?: string;\n getSession: () => WizardSession;\n fetchImpl?: typeof fetch;\n onError?: (message: string) => void;\n};\n\nconst STATUS: Record<string, RunTask['status']> = {\n pending: 'created',\n in_progress: 'running',\n completed: 'completed',\n failed: 'failed',\n skipped: 'completed',\n};\n\nexport class RunTaskNames {\n private names = new Map<string, string>();\n private used = new Set<string>();\n\n snapshot(items: readonly TaskItem[]): RunTask[] {\n if (items.length > 100) throw new Error('task limit exceeded (100)');\n const occurrences = new Map<string, number>();\n const ids = new Set<string>();\n return items.map((item) => {\n const subject = item.label.trim();\n if (!subject) throw new Error('blank task name');\n const rawStatus = item.sourceStatus ?? item.status;\n const status = STATUS[rawStatus];\n if (!Object.hasOwn(STATUS, rawStatus))\n throw new Error('invalid task status');\n const occurrence = (occurrences.get(subject) ?? 0) + 1;\n occurrences.set(subject, occurrence);\n const id = item.id\n ? JSON.stringify([item.source, item.id])\n : JSON.stringify([subject, occurrence]);\n if (ids.has(id)) throw new Error('duplicate task identity');\n ids.add(id);\n let name = this.names.get(id);\n if (!name) {\n name = subject.slice(0, 255);\n for (let n = 2; this.used.has(name); n++) {\n const suffix = ` (${n})`;\n name = subject.slice(0, 255 - suffix.length) + suffix;\n }\n this.names.set(id, name);\n this.used.add(name);\n }\n return { name, status };\n });\n }\n}\n\nexport function createWizardRunSync(\n options: Omit<Options, 'mode'> & {\n mode: 'local' | 'headless' | 'ci';\n noTelemetry: boolean;\n },\n): WizardRunSync | undefined {\n if (options.noTelemetry || options.mode === 'ci') return;\n if (options.mode === 'headless' && options.assignedId === undefined) return;\n return new WizardRunSync({\n ...options,\n mode: options.assignedId !== undefined ? 'cloud' : 'local',\n });\n}\n\nexport class WizardRunSync {\n private readonly names = new RunTaskNames();\n private readonly tasksAbort = new AbortController();\n private readonly reported = new Set<string>();\n private context?: {\n apiHost: string;\n projectId: number;\n programId: string;\n mode: Options['mode'];\n id?: string;\n };\n private queue: Promise<void> = Promise.resolve();\n private lastBody?: string;\n private stopped = false;\n private tasksDisabled = false;\n private authDisabled = false;\n private closing?: Promise<void>;\n\n constructor(private readonly options: Options) {}\n\n capture(items: readonly TaskItem[] | undefined): void {\n if (this.stopped || this.tasksDisabled || !items) return;\n if (\n this.options.mode === 'cloud' &&\n !isUUID(this.options.assignedId ?? '')\n ) {\n this.tasksDisabled = true;\n this.report('invalid cloud run assignment');\n return;\n }\n const session = this.options.getSession();\n if (session.runPhase === RunPhase.Idle) return;\n if (!this.context) {\n if (session.runPhase !== RunPhase.Running || !session.credentials) return;\n const { host, projectId } = session.credentials;\n this.context = {\n apiHost: host.appHost.replace(/\\/$/, ''),\n projectId,\n programId: this.options.programId,\n mode: this.options.mode,\n id: this.options.assignedId,\n };\n this.queue = this.initialize(session);\n }\n try {\n const body = JSON.stringify({ tasks: this.names.snapshot(items) });\n if (body === this.lastBody) return;\n this.lastBody = body;\n this.queue = this.queue.then(async () => {\n if (\n this.tasksDisabled ||\n this.tasksAbort.signal.aborted ||\n !this.context?.id\n )\n return;\n const ok = await this.request(\n 'PUT',\n `${this.context.id}/tasks/`,\n body,\n this.tasksAbort.signal,\n );\n if (!ok) this.tasksDisabled = true;\n });\n } catch {\n this.report(\n 'invalid task snapshot (names, statuses, identities or 100-task limit)',\n );\n }\n }\n\n shutdown(outcome: TaskStreamOutcome, timeoutMs: number): Promise<void> {\n if (this.closing) return this.closing;\n this.stopped = true;\n this.closing = this.finish(outcome, Math.max(0, timeoutMs));\n return this.closing;\n }\n\n private async initialize(session: WizardSession): Promise<void> {\n const context = this.context;\n if (!context || context.mode === 'cloud') return;\n const projectName = basename(resolve(session.installDir))\n .trim()\n .slice(0, 255);\n if (!projectName) {\n this.tasksDisabled = true;\n this.report('unsupported program or workspace configuration');\n return;\n }\n const body = JSON.stringify({\n program_id: context.programId,\n environment: 'local',\n workspace: { type: 'local_folder', project_name: projectName },\n ...(validVersion(VERSION) ? { wizard_version: VERSION } : {}),\n idempotency_key: randomUUID(),\n });\n // POST retries remain disabled until deployed local idempotency is verified.\n const result = await this.request('POST', '', body, this.tasksAbort.signal);\n if (\n result &&\n typeof result === 'object' &&\n 'id' in result &&\n typeof result.id === 'string' &&\n isUUID(result.id)\n ) {\n context.id = result.id;\n } else {\n this.tasksDisabled = true;\n this.report(\n 'local creation failed or returned an invalid ID; creation was not retried',\n );\n }\n }\n\n private credentials(\n creds = this.options.getSession().credentials,\n ): Credentials | undefined {\n const context = this.context;\n if (!context) return;\n if (\n !creds ||\n creds.host.appHost.replace(/\\/$/, '') !== context.apiHost ||\n creds.projectId !== context.projectId\n ) {\n this.report('execution credentials changed host or project');\n return;\n }\n if (\n creds.missingScopes?.includes('wizard_run:write') ||\n !creds.accessToken.startsWith('pha_')\n ) {\n this.authDisabled = true;\n this.report('wizard_run:write OAuth grant required; authorize again');\n return;\n }\n return creds;\n }\n\n private async request(\n method: 'POST' | 'PUT' | 'PATCH',\n suffix: string,\n body: string,\n signal: AbortSignal,\n ): Promise<unknown> {\n const context = this.context;\n if (!context || this.authDisabled || signal.aborted) return;\n let refreshed = false;\n let rateLimited = false;\n const attempts = method === 'POST' ? 1 : 3;\n for (let attempt = 0; attempt < attempts && !signal.aborted; attempt++) {\n let retryMs = 500 * 2 ** attempt;\n try {\n const creds = this.credentials();\n if (!creds) return;\n const response = await this.bounded(\n async (requestSignal) => {\n // The run's OAuth session owns refresh, so the agent reads the same rotated token.\n const current = this.credentials(await currentCredentials(creds));\n if (!current) throw new Error('credentials unavailable');\n const res = await (this.options.fetchImpl ?? fetch)(\n `${context.apiHost}/api/projects/${context.projectId}/wizard/runs/${suffix}`,\n {\n method,\n body,\n signal: requestSignal,\n headers: {\n Authorization: `Bearer ${current.accessToken}`,\n 'Content-Type': 'application/json',\n },\n },\n );\n const data: unknown =\n method === 'POST' && (res.status === 200 || res.status === 201)\n ? await res.json()\n : undefined;\n return { res, data, current };\n },\n signal,\n 5000,\n );\n const { res, data, current } = response;\n if (\n (method === 'POST' && [200, 201].includes(res.status)) ||\n (method === 'PUT' && res.status === 204) ||\n (method === 'PATCH' && res.status === 200)\n )\n return data ?? true;\n let recovery: UnauthorizedRecovery | undefined;\n if (res.status === 401 && !refreshed && method !== 'POST') {\n refreshed = true;\n recovery = await this.bounded(\n () =>\n rotateAfterUnauthorized(current, { requireRefreshToken: true }),\n signal,\n 5000,\n );\n }\n if (recovery?.kind === 'unchanged') {\n this.authDisabled = true;\n this.report(\n 'OAuth refresh could not recover the grant; authorize again',\n );\n return;\n }\n if (recovery?.kind === 'rotated') continue;\n if (res.status === 401 || res.status === 403) this.authDisabled = true;\n if (res.status === 429 && !rateLimited) {\n rateLimited = true;\n retryMs = parseRetryAfter(res.headers.get('Retry-After'));\n } else if (res.status < 500 || res.status >= 600) {\n this.report(\n `${method} rejected (${res.status}); check assignment, grant and program configuration`,\n );\n return;\n }\n } catch {\n if (signal.aborted) return;\n }\n if (attempt + 1 < attempts) {\n try {\n await this.sleep(retryMs, signal);\n } catch {\n return;\n }\n }\n }\n this.report(`${method} delivery exhausted`);\n }\n\n private async finish(\n outcome: TaskStreamOutcome,\n timeoutMs: number,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n try {\n await this.bounded(\n () => this.queue,\n new AbortController().signal,\n Math.floor(timeoutMs * 0.75),\n );\n } catch {\n this.report('task drain timed out');\n }\n this.tasksAbort.abort();\n if (this.context?.mode !== 'local' || !this.context.id || this.authDisabled)\n return;\n if (Date.now() >= deadline) {\n this.report('terminal update budget exhausted');\n return;\n }\n const finalAbort = new AbortController();\n const timer = setTimeout(\n () => finalAbort.abort(),\n Math.max(0, deadline - Date.now()),\n );\n try {\n await this.request(\n 'PATCH',\n `${this.context.id}/`,\n JSON.stringify({ status: outcome }),\n finalAbort.signal,\n );\n } finally {\n clearTimeout(timer);\n finalAbort.abort();\n }\n }\n\n private async bounded<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n parent: AbortSignal,\n ms: number,\n ): Promise<T> {\n const controller = new AbortController();\n const abort = () => controller.abort();\n parent.addEventListener('abort', abort, { once: true });\n if (parent.aborted) controller.abort();\n const timer = setTimeout(abort, ms);\n let onAbort: () => void = () => undefined;\n const cancelled = new Promise<never>((_, reject) => {\n onAbort = () => reject(new Error('request cancelled'));\n controller.signal.addEventListener('abort', onAbort, { once: true });\n if (controller.signal.aborted) onAbort();\n });\n try {\n return await Promise.race([operation(controller.signal), cancelled]);\n } finally {\n clearTimeout(timer);\n parent.removeEventListener('abort', abort);\n controller.signal.removeEventListener('abort', onAbort);\n controller.abort();\n }\n }\n\n private sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n const finish = () => {\n clearTimeout(timer);\n signal.removeEventListener('abort', finish);\n resolve();\n };\n const timer = setTimeout(finish, ms);\n signal.addEventListener('abort', finish, { once: true });\n if (signal.aborted) finish();\n });\n }\n\n private report(message: string): void {\n if (this.reported.has(message)) return;\n this.reported.add(message);\n (this.options.onError ?? ((m) => logToFile(`[wizard-run-sync] ${m}`)))(\n message,\n );\n }\n}\n","import type { PlannedEvent, SessionStore } from '../session-store';\nimport {\n startFileWatcher,\n type FileWatcherHandle,\n type FileWatcherOptions,\n} from '@utils/file-watcher';\n\nconst MAX_EVENT_PLAN_FILE_BYTES = 256 * 1024;\nconst MAX_EVENT_COUNT = 50;\nconst MAX_EVENT_NAME_LENGTH = 400;\nconst MAX_EVENT_DESCRIPTION_LENGTH = 4000;\n\nfunction firstString(...values: unknown[]): string | null {\n const value = values.find((candidate) => typeof candidate === 'string');\n return typeof value === 'string' ? value : null;\n}\n\nexport function normalizeEventPlan(parsed: unknown): PlannedEvent[] | null {\n if (!Array.isArray(parsed)) return null;\n\n const events: PlannedEvent[] = [];\n for (const value of parsed) {\n if (events.length >= MAX_EVENT_COUNT) break;\n\n const entry =\n value && typeof value === 'object'\n ? (value as Record<string, unknown>)\n : {};\n const name = firstString(entry.event_name, entry.name, entry.event);\n if (!name || !name.trim() || name.length > MAX_EVENT_NAME_LENGTH) continue;\n\n const description =\n firstString(entry.event_description, entry.description) ?? '';\n events.push({\n name,\n description: description.slice(0, MAX_EVENT_DESCRIPTION_LENGTH),\n });\n }\n\n return events;\n}\n\nexport class EventPlanWatcher {\n private handle: FileWatcherHandle | null = null;\n private captured = false;\n\n constructor(\n private readonly store: SessionStore,\n private readonly path: string,\n private readonly options: FileWatcherOptions = {},\n ) {}\n\n start(): void {\n if (this.handle || this.captured) return;\n\n this.handle = startFileWatcher(\n this.path,\n (parsed) => {\n const events = normalizeEventPlan(parsed);\n if (!events || events.length === 0) return;\n\n this.captured = true;\n this.store.setEventPlan(events);\n this.stop();\n },\n {\n ignoreInitialFile: true,\n maxFileSizeBytes: MAX_EVENT_PLAN_FILE_BYTES,\n ...this.options,\n },\n );\n }\n\n refresh(): void {\n if (this.captured) return;\n this.handle?.refresh();\n }\n\n stop(): void {\n this.handle?.stop();\n this.handle = null;\n }\n}\n","/**\n * Audit ledger → stream tasks: one row per audit *area*, never per check.\n *\n * A run resolves tens of checks, and each row carries a `file` path and a\n * `details` string derived from the user's code. Neither belongs on the wire.\n * A finding is progress, not a failure, so an area never reports `failed`.\n */\n\nimport type { AuditCheck, AuditStatus } from '@shared/audit-ledger';\nimport { StreamTaskStatus, type StreamTask } from './types';\n\nexport const MAX_AUDIT_AREAS = 24;\nconst MAX_AREA_TITLE_LENGTH = 80;\n\nconst RESOLVED: readonly AuditStatus[] = [\n 'pass',\n 'error',\n 'warning',\n 'suggestion',\n];\n\nfunction areaTitle(value: unknown): string | null {\n if (typeof value !== 'string' || !value.trim()) return null;\n return value.trim().slice(0, MAX_AREA_TITLE_LENGTH);\n}\n\n/** `idOffset` continues the program's task ids, which are stringified integers. */\nexport function rollUpAuditAreas(checks: unknown, idOffset = 0): StreamTask[] {\n if (!Array.isArray(checks)) return [];\n\n // Map keeps insertion order, which is the order the ledger seeds its areas in.\n const areas = new Map<string, { total: number; resolved: number }>();\n\n for (const entry of checks) {\n if (!entry || typeof entry !== 'object') continue;\n const title = areaTitle((entry as Partial<AuditCheck>).area);\n if (!title) continue;\n\n let counts = areas.get(title);\n if (!counts) {\n if (areas.size >= MAX_AUDIT_AREAS) continue;\n counts = { total: 0, resolved: 0 };\n areas.set(title, counts);\n }\n counts.total += 1;\n // The ledger is agent-written: anything unrecognized counts as unresolved.\n if (RESOLVED.includes((entry as Partial<AuditCheck>).status as AuditStatus))\n counts.resolved += 1;\n }\n\n return [...areas.entries()].map(([title, { total, resolved }], index) => ({\n id: String(idOffset + index),\n title,\n status:\n resolved === 0\n ? StreamTaskStatus.Pending\n : resolved < total\n ? StreamTaskStatus.InProgress\n : StreamTaskStatus.Completed,\n }));\n}\n","/**\n * Task-stream push — subscribes to SessionStore, builds payloads,\n * and fans out async to all registered destinations.\n *\n * Behaviour:\n * - `attach(store)` subscribe to store changes\n * - task updates debounced 250ms (trailing edge)\n * - phase transitions flush immediately, bypass debounce\n * - RunPhase.Idle skipped (no push)\n * - enabled === false destination delivery is disabled\n * - shutdown(timeoutMs) cancel pending, flush terminal phase\n * with timeout, never throw\n *\n * Concurrency: only one fan-out at a time. Emits during an in-flight\n * push are coalesced — at most one follow-up push fires with the\n * latest state once the current one settles.\n */\n\nimport type { SessionStore, TaskItem } from '../session-store';\nimport { TaskStatus } from '@shared/task-status';\nimport { RunPhase } from '@shared/run-state';\nimport { OutroKind } from '@shared/outro';\nimport { type OutroData, type PendingQuestion } from '@agent/types';\nimport {\n type TaskStreamDestination,\n type TaskStreamUpdate,\n type StreamTask,\n type TaskStreamError,\n type StreamPendingInput,\n StreamTaskStatus,\n StreamEvent,\n} from './types';\nimport { EventPlanWatcher } from './event-plan-watcher';\nimport { rollUpAuditAreas } from './audit-areas';\nimport type { WizardRunSync, TaskStreamOutcome } from './wizard-run-sync';\nimport { logToFile } from '@utils/debug';\nimport { WIZARD_RUN_SYNC_FLAG_KEY } from '@shared/constants';\nimport { sanitizeErrorDetail } from '@shared/errors';\n\n/** Trailing-edge debounce window for non-phase-change emits. */\nconst DEBOUNCE_MS = 250;\n/** Default shutdown timeout for the final terminal flush. */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2000;\n\nconst STATUS_MAP: Record<TaskStatus, StreamTaskStatus> = {\n [TaskStatus.Pending]: StreamTaskStatus.Pending,\n [TaskStatus.InProgress]: StreamTaskStatus.InProgress,\n [TaskStatus.Completed]: StreamTaskStatus.Completed,\n [TaskStatus.Failed]: StreamTaskStatus.Failed,\n // The stream has no skipped state; skipped is terminal, so report it resolved.\n [TaskStatus.Skipped]: StreamTaskStatus.Completed,\n};\n\nfunction buildTasks(items: TaskItem[]): StreamTask[] {\n return items.map((item, i) => ({\n id: String(i),\n title: item.label,\n status: STATUS_MAP[item.status] ?? StreamTaskStatus.Pending,\n }));\n}\n\n/** Drop \".SSSZ\" → \"Z\" so session_id segments stay routing-safe. */\nfunction secondPrecisionIso(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n\n/**\n * `workflow_id` and `skill_id` end up unescaped in Redis pub/sub\n * channel names, so the backend rejects anything outside\n * `^[A-Za-z0-9_.-]{1,255}$` with a 400. All current values already\n * comply; this is defence in depth in case a future caller passes\n * something with `:`, spaces, or other separators.\n */\nfunction sanitizeChannelId(value: string): string {\n return value.replace(/[^A-Za-z0-9_.-]/g, '-').slice(0, 255);\n}\n\nfunction buildError(\n phase: RunPhase,\n outroData: OutroData | null,\n): TaskStreamError | undefined {\n if (phase !== RunPhase.Error) return undefined;\n if (outroData?.kind === OutroKind.Error) {\n const message = outroData.message ?? outroData.body ?? 'Wizard run failed';\n const error: TaskStreamError = { type: 'wizard_error', message };\n if (outroData.errorCode) error.code = outroData.errorCode;\n const safeDetail = sanitizeErrorDetail(outroData.errorDetail);\n if (safeDetail) error.detail = safeDetail;\n return error;\n }\n return { type: 'wizard_error', message: 'Wizard run failed' };\n}\n\n/**\n * Sensitive asks (secrets, API keys) publish only the fact that input is\n * required — never the prompt text. The session row is team-visible and\n * outlives the prompt.\n */\nfunction buildPendingInput(\n question: PendingQuestion | null | undefined,\n): StreamPendingInput | undefined {\n if (!question) return undefined;\n const sensitive = question.questions.some((q) => q.sensitive === true);\n return {\n id: question.id,\n asked_at: question.askedAt ?? new Date().toISOString(),\n question_count: question.questions.length,\n sensitive,\n prompts: sensitive ? undefined : question.questions.map((q) => q.prompt),\n };\n}\n\nexport interface TaskStreamPushOptions {\n store: SessionStore;\n runSync?: WizardRunSync;\n getFlags?: () => Readonly<Record<string, string>> | null;\n programId: string;\n destinations: TaskStreamDestination[];\n /** Optional absolute event-plan path to load into the store once. */\n eventPlanPath?: string;\n /** The run's audit ledger, when it has one. The runner owns the watcher. */\n auditChecks?: () => unknown;\n /** When false, destination subscription/delivery remains disabled. */\n enabled?: boolean;\n}\n\nexport class TaskStreamPush {\n private readonly store: SessionStore;\n private readonly destinations: TaskStreamDestination[];\n private readonly startedAt: string;\n private readonly programId: string;\n private readonly sessionId: string;\n private readonly eventPlanWatcher: EventPlanWatcher | null;\n private readonly auditChecks: (() => unknown) | null;\n\n private readonly runSync?: WizardRunSync;\n private readonly getFlags?: TaskStreamPushOptions['getFlags'];\n private remoteSync?: 'wizard-session' | 'wizard-run';\n private shutdownPromise?: Promise<void>;\n private enabled: boolean;\n private created = false;\n private lastPushedPhase: RunPhase | null = null;\n private lastPushedQuestionId: string | null = null;\n\n private unsubscribe: (() => void) | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private inFlight: Promise<void> | null = null;\n private needsAnotherPush = false;\n private shuttingDown = false;\n\n constructor(opts: TaskStreamPushOptions) {\n this.store = opts.store;\n this.runSync = opts.runSync;\n this.getFlags = opts.getFlags;\n this.remoteSync = !opts.runSync\n ? 'wizard-session'\n : opts.getFlags\n ? undefined\n : 'wizard-run';\n this.programId = sanitizeChannelId(opts.programId);\n this.destinations = opts.destinations;\n this.enabled = opts.enabled ?? true;\n const startedAt = new Date();\n this.eventPlanWatcher = opts.eventPlanPath\n ? new EventPlanWatcher(this.store, opts.eventPlanPath)\n : null;\n this.auditChecks = opts.auditChecks ?? null;\n this.startedAt = secondPrecisionIso(startedAt);\n // skillId may not be set yet — fall back to programId so the\n // session_id is stable for the whole run regardless of when the\n // program metadata is populated.\n const skillId = sanitizeChannelId(\n this.store.session.skillId ?? this.programId,\n );\n this.sessionId = `${this.programId}-${skillId}-${this.startedAt}`;\n }\n\n /**\n * Load the event plan and subscribe to store changes. Destination delivery\n * remains disabled when `enabled === false`, but the plan still populates the\n * store for local and headless consumers.\n */\n attach(store?: SessionStore): void {\n this.eventPlanWatcher?.start();\n if (!this.enabled) return;\n if (this.unsubscribe) return;\n const target = store ?? this.store;\n this.unsubscribe = target.subscribe(() => this.onStoreChange());\n }\n\n /** Stop subscribing. Does not flush. */\n detach(): void {\n this.eventPlanWatcher?.stop();\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = null;\n }\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n // Finalize execution while the legacy session continues through the outro.\n async finishRun(\n outcome: TaskStreamOutcome,\n timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n ): Promise<void> {\n await this.runSync?.shutdown(outcome, timeoutMs);\n }\n\n shutdown(\n timeoutMs: number = DEFAULT_SHUTDOWN_TIMEOUT_MS,\n outcome: TaskStreamOutcome = this.store.session.runPhase ===\n RunPhase.Completed\n ? 'completed'\n : 'failed',\n ): Promise<void> {\n this.shutdownPromise ??= Promise.all([\n this.runSync?.shutdown(outcome, timeoutMs),\n this.shutdownLegacy(timeoutMs),\n ]).then(() => undefined);\n return this.shutdownPromise;\n }\n\n private async shutdownLegacy(timeoutMs: number): Promise<void> {\n this.shuttingDown = true;\n this.eventPlanWatcher?.refresh();\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.detach();\n if (!this.enabled) return;\n\n const phase = this.store.session.runPhase;\n const isTerminal = phase === RunPhase.Completed || phase === RunPhase.Error;\n if (!isTerminal) return;\n\n const flush = this.flush();\n if (timeoutMs <= 0) return;\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n flush,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, timeoutMs);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Imperative push — fires immediately regardless of phase. Kept as\n * the building block for both subscription-driven and direct calls.\n */\n async push(): Promise<void> {\n await this.flush();\n }\n\n // ── Internal ────────────────────────────────────────────────────\n\n private onStoreChange(): void {\n if (!this.enabled || this.shuttingDown) return;\n const phase = this.store.session.runPhase;\n if (phase === RunPhase.Idle) return;\n this.selectRemoteSync();\n if (this.remoteSync === 'wizard-run') {\n this.runSync?.capture(this.store.tasks);\n }\n\n // A push is already in flight — coalesce. The in-flight push's\n // settle handler will trigger one follow-up with the latest state.\n if (this.inFlight) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.needsAnotherPush = true;\n return;\n }\n\n const phaseChanged = phase !== this.lastPushedPhase;\n const questionId = this.store.session.pendingQuestion?.id ?? null;\n const questionChanged = questionId !== this.lastPushedQuestionId;\n if (phaseChanged || questionChanged) {\n // Phase transitions bypass the debounce: the web app needs to\n // see Running → Completed as soon as it lands. A wizard_ask\n // opening or closing is the same shape — the whole point of\n // publishing it is that the user is looking at the web app, so\n // a 250ms-stale \"needs your input\" defeats the purpose.\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n void this.flush();\n return;\n }\n\n // Task updates can arrive faster than we want to push. Debounce\n // them — the last update in a burst wins.\n if (this.debounceTimer) return;\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null;\n void this.flush();\n }, DEBOUNCE_MS);\n }\n\n /**\n * Fan out the current state to every destination. Serialized — if\n * a flush is already running, mark \"needs another\" and let the\n * in-flight one schedule the follow-up when it settles.\n */\n private flush(): Promise<void> {\n if (this.inFlight) {\n this.needsAnotherPush = true;\n return this.inFlight;\n }\n\n const run = async (): Promise<void> => {\n try {\n await this.sendOnce();\n } finally {\n this.inFlight = null;\n if (this.needsAnotherPush) {\n this.needsAnotherPush = false;\n // Re-enter to push the latest snapshot.\n await this.flush();\n }\n }\n };\n\n this.inFlight = run();\n return this.inFlight;\n }\n\n private async sendOnce(): Promise<void> {\n this.selectRemoteSync();\n if (!this.remoteSync) return;\n const { session, tasks, eventPlan, handoffText } = this.store;\n const skillId = sanitizeChannelId(session.skillId ?? this.programId);\n const phase = session.runPhase;\n\n // Program rows carry the phase; the area rows carry the audit's progress.\n const programTasks = buildTasks(tasks);\n const auditAreas = this.auditChecks\n ? rollUpAuditAreas(this.auditChecks(), programTasks.length)\n : [];\n\n const payload: TaskStreamUpdate = {\n session_id: this.sessionId,\n workflow_id: this.programId,\n skill_id: skillId,\n started_at: this.startedAt,\n run_phase: phase,\n tasks: [...programTasks, ...auditAreas],\n event_plan: eventPlan.length > 0 ? { events: eventPlan } : undefined,\n error: buildError(phase, session.outroData),\n pending_input: buildPendingInput(session.pendingQuestion),\n // Included on every push once captured; the backend keeps it sticky, so\n // pushes that raced the capture cannot un-set it.\n handoff_text: handoffText ?? undefined,\n timestamp: new Date().toISOString(),\n };\n logToFile(\n `[task-stream-push] push phase=${phase} handoff_text=${\n handoffText === null ? 'absent' : `${handoffText.length} chars`\n }`,\n );\n\n let event: StreamEvent;\n if (!this.created) {\n this.created = true;\n event = StreamEvent.Create;\n } else if (phase === RunPhase.Completed) {\n event = StreamEvent.Complete;\n } else if (phase === RunPhase.Error) {\n event = StreamEvent.Error;\n } else {\n event = StreamEvent.Update;\n }\n\n this.lastPushedPhase = phase;\n this.lastPushedQuestionId = payload.pending_input?.id ?? null;\n\n await Promise.all(\n this.destinations\n .filter(\n (d) => d.name !== 'posthog' || this.remoteSync === 'wizard-session',\n )\n .map((d) =>\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n d.send(event, payload).catch(() => {}),\n ),\n );\n }\n\n private selectRemoteSync(): void {\n if (this.remoteSync || !this.store.session.credentials) return;\n if (this.store.session.runPhase === RunPhase.Idle) return;\n const flags = this.getFlags?.();\n if (!flags) return;\n this.remoteSync =\n flags[WIZARD_RUN_SYNC_FLAG_KEY] === 'wizard-run'\n ? 'wizard-run'\n : 'wizard-session';\n }\n}\n","/** Log in with a personal API key, the way `--ci` does: no browser, no OAuth. */\n\nimport {\n resolveApiKeyProject,\n type ApiKeyLoginOptions,\n} from '@shared/api-key-login';\nimport type {\n CredentialsProvider,\n ResolvedProgramCredentials,\n} from './credentials';\n\nexport type { ApiKeyLoginOptions };\n\n/** The login for `apiKey`, plus the user's role for role-tailored copy. */\nexport async function resolveApiKeyLogin(\n apiKey: string,\n options: ApiKeyLoginOptions = {},\n): Promise<ResolvedProgramCredentials & { roleAtOrganization: string | null }> {\n const { host, project, projectId, apiUser } = await resolveApiKeyProject(\n apiKey,\n options,\n );\n return {\n posthog: {\n accessToken: apiKey,\n projectApiKey: project.api_token,\n host,\n projectId,\n // A personal API key carries whatever scopes it carries — there is no\n // per-run scope request to diff against.\n missingScopes: [],\n },\n project,\n apiUser,\n roleAtOrganization: apiUser?.role_at_organization ?? null,\n };\n}\n\n/** A credentials provider that logs in with `apiKey` once, on first use. */\nexport function apiKeyCredentials(\n apiKey: string,\n options: ApiKeyLoginOptions = {},\n): CredentialsProvider {\n let login: Promise<ResolvedProgramCredentials> | undefined;\n return {\n resolve: () =>\n (login ??= resolveApiKeyLogin(apiKey, options).catch((error: unknown) => {\n login = undefined; // a failed login is retried on the next call\n throw error;\n })),\n };\n}\n"],"mappings":"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"}