@posthog/wizard 2.63.0 → 2.64.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 (92) hide show
  1. package/dist/{add-mcp-server-to-clients-Bsyb6MUB.js → add-mcp-server-to-clients-D9l1dyw9.js} +4 -4
  2. package/dist/{add-mcp-server-to-clients-Bsyb6MUB.js.map → add-mcp-server-to-clients-D9l1dyw9.js.map} +1 -1
  3. package/dist/{agent-interface-C4CV7p0L.js → agent-interface-BQfwaSe-.js} +6 -6
  4. package/dist/{agent-interface-C4CV7p0L.js.map → agent-interface-BQfwaSe-.js.map} +1 -1
  5. package/dist/{agent-prompt-loader-DiM8xfSZ.js → agent-prompt-loader-BRKe3VE6.js} +7 -6
  6. package/dist/agent-prompt-loader-BRKe3VE6.js.map +1 -0
  7. package/dist/{agent-runner-X3JWS2WK.js → agent-runner-B4ZdNkj4.js} +28 -22
  8. package/dist/{agent-runner-X3JWS2WK.js.map → agent-runner-B4ZdNkj4.js.map} +1 -1
  9. package/dist/analytics-BlE8Yjoh.js +2 -0
  10. package/dist/{analytics-YLZKudpE.js → analytics-Duy9Vfk-.js} +2 -2
  11. package/dist/{analytics-YLZKudpE.js.map → analytics-Duy9Vfk-.js.map} +1 -1
  12. package/dist/{api-DlRJAFI2.js → api-DEb25KDf.js} +3 -3
  13. package/dist/{api-DlRJAFI2.js.map → api-DEb25KDf.js.map} +1 -1
  14. package/dist/bin.js +197 -43
  15. package/dist/bin.js.map +1 -1
  16. package/dist/{bounded-fs-CYxETRCt.js → bounded-fs-CvYIzQbg.js} +3 -3
  17. package/dist/{bounded-fs-CYxETRCt.js.map → bounded-fs-CvYIzQbg.js.map} +1 -1
  18. package/dist/check-screens.tsx +15 -0
  19. package/dist/{ci-install-Cyqj_i3I.js → ci-install-k2HWuLQ3.js} +4 -4
  20. package/dist/{ci-install-Cyqj_i3I.js.map → ci-install-k2HWuLQ3.js.map} +1 -1
  21. package/dist/{debug-BYIoy1Oz.js → debug-3FJsmlmC.js} +3 -3
  22. package/dist/{debug-BYIoy1Oz.js.map → debug-3FJsmlmC.js.map} +1 -1
  23. package/dist/{debug-2W78IhFE.js → debug-D7eikh3K.js} +1 -1
  24. package/dist/{env-api-key-B7kiHT3h.js → env-api-key-uk6HInaB.js} +2 -2
  25. package/dist/{env-api-key-B7kiHT3h.js.map → env-api-key-uk6HInaB.js.map} +1 -1
  26. package/dist/{environment-Br-oy8cq.js → environment-Cyq61jKV.js} +4 -4
  27. package/dist/{environment-Br-oy8cq.js.map → environment-Cyq61jKV.js.map} +1 -1
  28. package/dist/{file-watcher-HaK4IrJ5.js → file-watcher-DZDDYHrv.js} +2 -2
  29. package/dist/{file-watcher-HaK4IrJ5.js.map → file-watcher-DZDDYHrv.js.map} +1 -1
  30. package/dist/{headless-ui-Dd7Ty4Ul.js → headless-ui-MpPKNNH9.js} +2 -2
  31. package/dist/{headless-ui-Dd7Ty4Ul.js.map → headless-ui-MpPKNNH9.js.map} +1 -1
  32. package/dist/{interactive-CYv97Lgg.js → interactive-BDCUPSmr.js} +2 -2
  33. package/dist/{interactive-CYv97Lgg.js.map → interactive-BDCUPSmr.js.map} +1 -1
  34. package/dist/{mcp-D6cJWFPn.js → mcp-DOH54TGI.js} +2 -2
  35. package/dist/{mcp-D6cJWFPn.js.map → mcp-DOH54TGI.js.map} +1 -1
  36. package/dist/{mcp-prompt-streaming-DYX7vQ3q.js → mcp-prompt-streaming-BKY-srkT.js} +3 -3
  37. package/dist/{mcp-prompt-streaming-DYX7vQ3q.js.map → mcp-prompt-streaming-BKY-srkT.js.map} +1 -1
  38. package/dist/{non-interactive-BR4kmPE5.js → non-interactive-DOG-p4ku.js} +2 -2
  39. package/dist/{non-interactive-BR4kmPE5.js.map → non-interactive-DOG-p4ku.js.map} +1 -1
  40. package/dist/{orchestrator-tools-CTP6l34W.js → orchestrator-tools-CfaYl_W4.js} +3 -3
  41. package/dist/{orchestrator-tools-CTP6l34W.js.map → orchestrator-tools-CfaYl_W4.js.map} +1 -1
  42. package/dist/{package-manager-FyJmIsqr.js → package-manager-62MxCa1Y.js} +2 -2
  43. package/dist/{package-manager-FyJmIsqr.js.map → package-manager-62MxCa1Y.js.map} +1 -1
  44. package/dist/{pi-DeF8mWlb.js → pi-DohBskuT.js} +11 -11
  45. package/dist/{pi-DeF8mWlb.js.map → pi-DohBskuT.js.map} +1 -1
  46. package/dist/{playground-BSMgOp3k.js → playground-DaTeK9-L.js} +6 -6
  47. package/dist/{playground-BSMgOp3k.js.map → playground-DaTeK9-L.js.map} +1 -1
  48. package/dist/{posthog-ZWC-083a.js → posthog-DvM4V-to.js} +2 -2
  49. package/dist/{posthog-ZWC-083a.js.map → posthog-DvM4V-to.js.map} +1 -1
  50. package/dist/{posthog-integration-CoAID6XW.js → posthog-integration-BHqFQd4U.js} +15 -15
  51. package/dist/{posthog-integration-CoAID6XW.js.map → posthog-integration-BHqFQd4U.js.map} +1 -1
  52. package/dist/{provisioning-xirriYQJ.js → provisioning-B41dKvno.js} +3 -3
  53. package/dist/{provisioning-xirriYQJ.js.map → provisioning-B41dKvno.js.map} +1 -1
  54. package/dist/{queue-tools-BtJ7_fEW.js → queue-tools-DED9kPuc.js} +3 -3
  55. package/dist/{queue-tools-BtJ7_fEW.js.map → queue-tools-DED9kPuc.js.map} +1 -1
  56. package/dist/{registry-B-whj8SQ.js → registry-Dh9aqVC6.js} +5 -5
  57. package/dist/{registry-B-whj8SQ.js.map → registry-Dh9aqVC6.js.map} +1 -1
  58. package/dist/{security-CPtn_dUW.js → security-HZDvt7uh.js} +5 -5
  59. package/dist/{security-CPtn_dUW.js.map → security-HZDvt7uh.js.map} +1 -1
  60. package/dist/{setup-utils-e78zkZoW.js → setup-utils-DJF-LjYE.js} +38 -9
  61. package/dist/setup-utils-DJF-LjYE.js.map +1 -0
  62. package/dist/{start-tui-B08PfE0V.js → start-tui-0p-Ybwxe.js} +37 -18
  63. package/dist/{start-tui-B08PfE0V.js.map → start-tui-0p-Ybwxe.js.map} +1 -1
  64. package/dist/{steps-Dpd8UTJX.js → steps-BSp0kHUG.js} +7 -7
  65. package/dist/{steps-Dpd8UTJX.js.map → steps-BSp0kHUG.js.map} +1 -1
  66. package/dist/{store-B-ctPH7O.js → store-BgOhG49V.js} +4 -4
  67. package/dist/{store-B-ctPH7O.js.map → store-BgOhG49V.js.map} +1 -1
  68. package/dist/{subagent-B18BesIW.js → subagent-DDsAb9we.js} +2 -2
  69. package/dist/{subagent-B18BesIW.js.map → subagent-DDsAb9we.js.map} +1 -1
  70. package/dist/{task-D1RaGg3b.js → task-CCZAWF2U.js} +12 -12
  71. package/dist/{task-D1RaGg3b.js.map → task-CCZAWF2U.js.map} +1 -1
  72. package/dist/{task-stream-DvTf1NOr.js → task-stream-B0Eo4KX1.js} +4 -4
  73. package/dist/{task-stream-DvTf1NOr.js.map → task-stream-B0Eo4KX1.js.map} +1 -1
  74. package/dist/{tasks-CvHNNFB1.js → tasks-DRqU9G95.js} +2 -2
  75. package/dist/{tasks-CvHNNFB1.js.map → tasks-DRqU9G95.js.map} +1 -1
  76. package/dist/{telemetry-C72C8V6q.js → telemetry-4VTeK1h9.js} +3 -3
  77. package/dist/{telemetry-C72C8V6q.js.map → telemetry-4VTeK1h9.js.map} +1 -1
  78. package/dist/{terminal-BJms6G77.js → terminal-CDqEDXWF.js} +11 -11
  79. package/dist/{terminal-BJms6G77.js.map → terminal-CDqEDXWF.js.map} +1 -1
  80. package/dist/{tools-BcXOgEvh.js → tools-CjNt0TUI.js} +6 -6
  81. package/dist/{tools-BcXOgEvh.js.map → tools-CjNt0TUI.js.map} +1 -1
  82. package/dist/{wizard-abort-DRscjUOg.js → wizard-abort-BeuIRy5a.js} +3 -3
  83. package/dist/{wizard-abort-DRscjUOg.js.map → wizard-abort-BeuIRy5a.js.map} +1 -1
  84. package/dist/{wizard-abort-BUpx4qnK.js → wizard-abort-DNHeIZ8N.js} +1 -1
  85. package/dist/yara-hooks-BE3OXk2V.js +2 -0
  86. package/dist/{yara-hooks-DKRsn0lN.js → yara-hooks-CCGjiVA2.js} +4 -4
  87. package/dist/{yara-hooks-DKRsn0lN.js.map → yara-hooks-CCGjiVA2.js.map} +1 -1
  88. package/package.json +1 -1
  89. package/dist/agent-prompt-loader-DiM8xfSZ.js.map +0 -1
  90. package/dist/analytics-F6z7o3N2.js +0 -2
  91. package/dist/setup-utils-e78zkZoW.js.map +0 -1
  92. package/dist/yara-hooks-fHntXaFu.js +0 -2
@@ -0,0 +1,2 @@
1
+ import { n as analytics } from "./analytics-Duy9Vfk-.js";
2
+ export { analytics };
@@ -1,4 +1,4 @@
1
- import { C as ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, S as ANALYTICS_HOST_URL, bt as TASK_RUN_ID, gt as VERSION, it as WIZARD_FLAG_KEYS, r as debug, s as logToFile, vt as RUN_SURFACE, w as ANALYTICS_TEAM_TAG, yt as TASK_ID } from "./debug-BYIoy1Oz.js";
1
+ import { C as ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, S as ANALYTICS_HOST_URL, bt as TASK_RUN_ID, gt as VERSION, it as WIZARD_FLAG_KEYS, r as debug, s as logToFile, vt as RUN_SURFACE, w as ANALYTICS_TEAM_TAG, yt as TASK_ID } from "./debug-3FJsmlmC.js";
2
2
  import { PostHog } from "posthog-node";
3
3
  import { v4 } from "uuid";
4
4
  //#region src/utils/ci-flag-overrides.ts
@@ -281,4 +281,4 @@ const analytics = new Analytics();
281
281
  //#endregion
282
282
  export { ciExcludedTaskTypes as a, sessionProperties as i, analytics as n, groupsFromUser as r, Analytics as t };
283
283
 
284
- //# sourceMappingURL=analytics-YLZKudpE.js.map
284
+ //# sourceMappingURL=analytics-Duy9Vfk-.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-YLZKudpE.js","names":["uuidv4"],"sources":["../src/utils/ci-flag-overrides.ts","../src/utils/analytics.ts"],"sourcesContent":["/**\n * CI-only feature-flag overrides.\n *\n * CI must route deterministically: a run that tests the orchestrator arm says\n * so explicitly instead of depending on a live feature flag someone can edit\n * mid-week. The override env var (see the allowlist in `env.ts`) is a JSON\n * object of flag key → value, merged over whatever PostHog returned.\n *\n * The override path exists only in CI builds (`pnpm build:ci`). Published\n * builds inline NODE_ENV as the literal \"production\", the guards collapse,\n * and tsdown strips the rest from the bundle — and the smoke test asserts the\n * env var names are physically absent from production output (which is also\n * why no comment in this file may spell them out), so this can never quietly\n * become a production surface.\n */\nimport { runtimeEnv } from '@env';\nimport { logToFile } from './debug';\n\nexport function applyCiFlagOverrides(\n flags: Record<string, string>,\n payloads: Record<string, unknown> = {},\n): { flags: Record<string, string>; payloads: Record<string, unknown> } {\n // Compared inline (not via env.ts's IS_PRODUCTION_BUILD) so tsdown replaces\n // it with a literal right here and the bundler can prove the rest of this\n // function unreachable in production builds. The smoke test enforces that.\n if (process.env.NODE_ENV === 'production') return { flags, payloads };\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return { flags, payloads };\n\n let overrides: Record<string, unknown>;\n try {\n overrides = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // A malformed override is a CI misconfiguration. Fail the run loudly\n // rather than silently testing whatever the live flags happen to say.\n throw new Error(\n 'The CI flag-override env var is not valid JSON (expected {\"flag-key\": value, ...}).',\n );\n }\n\n const mergedFlags = { ...flags };\n const mergedPayloads = { ...payloads };\n for (const [key, value] of Object.entries(overrides)) {\n // An object/array override is a flag payload: the flag reads 'true' and\n // the value rides the payload channel, matching a payload-carrying flag.\n if (typeof value === 'object' && value !== null) {\n mergedFlags[key] = 'true';\n mergedPayloads[key] = value;\n } else {\n mergedFlags[key] = String(value);\n }\n }\n logToFile('[flags] CI overrides applied', overrides);\n return { flags: mergedFlags, payloads: mergedPayloads };\n}\n\n/**\n * Task types excluded from this run. The exclusion env var (see the allowlist\n * in `env.ts`) is a comma-separated list (e.g. `dashboard`), set by the CI\n * harness that owns the policy — the wizard and the served content stay\n * run-mode agnostic. CI-build only, same as the flag overrides: published\n * builds strip this path.\n */\nexport function ciExcludedTaskTypes(): readonly string[] {\n if (process.env.NODE_ENV === 'production') return [];\n\n const raw = runtimeEnv('WIZARD_CI_EXCLUDE_TASKS');\n if (!raw) return [];\n const types = raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n if (types.length > 0) logToFile('[flags] CI task exclusions', types);\n return types;\n}\n","import { PostHog } from 'posthog-node';\nimport {\n ANALYTICS_HOST_URL,\n ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,\n ANALYTICS_TEAM_TAG,\n WIZARD_FLAG_KEYS,\n} from '@lib/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { ApiUser } from '@lib/api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IS_PRODUCTION_BUILD, RUN_SURFACE, TASK_ID, TASK_RUN_ID } from '@env';\nimport { VERSION } from '@lib/version';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\n\n/**\n * The invocation, reduced to flag-safe strings: the command word (first\n * positional, 'default' for the bare flow) and the sorted option NAMES.\n * Option values never leave the machine — they carry paths, emails, keys.\n */\nfunction invocationProperties(): { command: string; cli_flags: string } {\n const args = process.argv.slice(2);\n const command = args.find((a) => !a.startsWith('-')) ?? 'default';\n const flags = [\n ...new Set(\n args\n .filter((a) => a.startsWith('--'))\n .map((a) => a.slice(2).split('=')[0]),\n ),\n ].sort();\n return { command, cli_flags: flags.join(',') };\n}\n\n/**\n * Extract a standard property bag from the current session.\n * Used by store-level analytics and available for ad-hoc captures.\n */\nexport function sessionProperties(\n session: WizardSession,\n): Record<string, unknown> {\n return {\n integration: session.integration,\n detected_framework: session.detectedFrameworkLabel,\n typescript: session.typescript,\n project_id: session.credentials?.projectId,\n discovered_features: session.discoveredFeatures,\n additional_features: session.additionalFeatureQueue,\n run_phase: session.runPhase,\n };\n}\n\nexport function groupsFromUser(\n user: ApiUser | null,\n host: string,\n): Record<string, string> {\n const groups: Record<string, string> = { instance: host };\n if (!user) return groups;\n\n const organizationId = user.organization?.id;\n if (organizationId) groups.organization = organizationId;\n\n const customerId = user.organization?.customer_id;\n if (customerId) groups.customer = customerId;\n\n const projectUuid = user.team?.uuid;\n if (projectUuid) groups.project = projectUuid;\n\n return groups;\n}\n\nconst WIZARD_FLAGS: ReadonlySet<string> = new Set(WIZARD_FLAG_KEYS);\n\n// Widen back to the SDK's shape — a filter on `true` never matches `'true'`.\nfunction flagResponseValue(value: string): string | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return value;\n}\n\nexport class Analytics {\n private client: PostHog;\n private tags: Record<string, string | boolean | number | null | undefined> =\n {};\n private distinctId?: string;\n private anonymousId: string;\n private _runId: string;\n private sessionId: string | null = null;\n private appName = 'wizard';\n private activeFlags: Record<string, string> | null = null;\n private activeFlagPayloads: Record<string, unknown> = {};\n private groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\n private terminalEventSent = false;\n\n constructor() {\n this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {\n host: ANALYTICS_HOST_URL,\n flushAt: 1,\n flushInterval: 0,\n enableExceptionAutocapture: true,\n before_send: (event) => {\n if (!event) return event;\n if (Object.keys(this.groups).length > 0) {\n event.groups = { ...this.groups, ...event.groups };\n }\n // Autocaptured exceptions arrive with a random uuid and\n // `$process_person_profile: false` — reattach the run's identity\n // and tags so they land on the same person as everything else.\n if (event.event === '$exception') {\n event.distinctId = this.distinctId ?? this.anonymousId;\n const { $process_person_profile, ...properties } =\n event.properties ?? {};\n void $process_person_profile;\n event.properties = { ...this.tags, ...properties };\n }\n // The SDK captures this one itself, bypassing capture(), so tags merge here.\n if (event.event === '$feature_flag_called') {\n event.properties = { ...this.tags, ...(event.properties ?? {}) };\n }\n return event;\n },\n });\n\n this.tags = { $app_name: this.appName };\n // Tag every run with its build type so prod / dev / ci / headless segment\n // cleanly in analytics. tsdown inlines IS_PRODUCTION_BUILD to `true` in\n // published builds and `false` for dev/tsx/test runs. Non-interactive runs\n // upgrade this in runWizardCI: dev `--ci` runs to 'ci', published headless\n // runs to 'headless'.\n this.tags.build = IS_PRODUCTION_BUILD ? 'prod' : 'dev';\n\n this.tags.run_surface = RUN_SURFACE;\n\n // The build this run came from. Already sent as a flag-evaluation person\n // property; tagging it makes every event attributable to a release, so a\n // regression can be bounded to the versions that carry it.\n this.tags.version = VERSION;\n\n // Cloud runs only: the task run that owns the sandbox. Without these the\n // wizard's events and the run that launched it are two unrelated streams,\n // since run_id above is minted per process and never leaves the wizard.\n // Left unset for local runs so the properties stay absent rather than null.\n if (TASK_RUN_ID) this.tags.task_run_id = TASK_RUN_ID;\n if (TASK_ID) this.tags.task_id = TASK_ID;\n\n this.anonymousId = uuidv4();\n\n // One id per process = one id per wizard run, registered in the tag bag\n // so it rides on every capture, exception, and autocaptured exception\n // (all of which merge `this.tags`). Lets you separate two runs by the\n // same logged-in user, who otherwise share one distinct id. Distinct\n // from `anonymousId`, the pre-login *person* id that gets aliased onto\n // the real user at login. `$session_id` is intentionally not set here —\n // it stays null until OAuth completes (see identifyUser).\n this._runId = uuidv4();\n this.tags.run_id = this._runId;\n\n this.distinctId = undefined;\n }\n\n /** Per-process run id, tagged on every event and gateway trace. */\n get runId(): string {\n return this._runId;\n }\n\n /** Build type for this run ('prod' | 'dev' | 'ci' | 'headless') — the same value tagged on every analytics event. */\n get build(): string {\n return String(this.tags.build ?? 'dev');\n }\n\n /**\n * Associate the run with the logged-in user, once per id. Identifies them\n * (email, name) and records those person properties so events carry them and\n * feature flags can target the individual user — without the email here the\n * wizard only sends `$app_name`, so email-targeted flags never match. Opens\n * the analytics session on first login, then aliases the run's anonymous id\n * onto the identified person so pre-login events merge in.\n */\n identifyUser(user: ApiUser) {\n const distinctId = user.distinct_id;\n if (this.distinctId === distinctId || distinctId === this.anonymousId) {\n return;\n }\n this.distinctId = distinctId;\n // Open the analytics session on first login. Null until here, so\n // pre-OAuth events carry only `run_id`; from now on every event also\n // carries `$session_id` and PostHog groups the authenticated run into a\n // native Session. Stored in the tag bag so it rides on every subsequent\n // capture and exception.\n if (!this.sessionId) {\n this.sessionId = uuidv4();\n this.tags.$session_id = this.sessionId;\n }\n const props: Record<string, string> = {};\n if (user.email) props.email = user.email;\n const name = [user.first_name, user.last_name]\n .filter(Boolean)\n .join(' ')\n .trim();\n if (name) props.name = name;\n this.personProperties = props;\n this.client.identify({ distinctId, properties: { $set: props } });\n this.client.alias({\n distinctId,\n alias: this.anonymousId,\n });\n // The flag snapshot is per identity. Anything evaluated before login (the\n // intro screen reads the tools-menu flag) was anonymous — drop it so the\n // next read re-evaluates as this user.\n this.activeFlags = null;\n this.activeFlagPayloads = {};\n }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n // Environment facts flag conditions can scope on — evaluation-time only,\n // never persisted onto the person. `version` gates an experiment to\n // releases that implement it (a condition on a property older builds\n // don't send silently never matches them); `run_surface`/`build` scope\n // by invocation surface; `command`/`cli_flags` by subcommand; `os`/\n // `node_major` by platform.\n return {\n $app_name: this.appName,\n version: VERSION,\n run_surface: RUN_SURFACE,\n build: String(this.tags.build ?? 'dev'),\n os: process.platform,\n node_major: process.versions.node.split('.')[0],\n ...invocationProperties(),\n ...this.personProperties,\n };\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n setGroups(groups: Record<string, string>) {\n this.groups = groups;\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n this.client.captureException(error, this.distinctId ?? this.anonymousId, {\n team: ANALYTICS_TEAM_TAG,\n ...this.tags,\n ...properties,\n });\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: eventName,\n properties: {\n ...this.tags,\n ...this.wizardFlagFeatureProperties(),\n ...properties,\n },\n });\n }\n\n // Built from the resolved map, not the SDK snapshot: a CI-forced variant must own its events.\n private wizardFlagFeatureProperties(): Record<string, unknown> {\n if (this.activeFlags === null) return {};\n const props: Record<string, unknown> = {};\n const active: string[] = [];\n for (const [key, value] of Object.entries(this.activeFlags)) {\n if (!WIZARD_FLAGS.has(key)) continue;\n const resolved = flagResponseValue(value);\n props[`$feature/${key}`] = resolved;\n if (resolved !== false) active.push(key);\n }\n if (active.length > 0) props.$active_feature_flags = active.sort();\n return props;\n }\n\n /**\n * Capture a wizard-specific event. Automatically prepends \"wizard: \" to the event name.\n * All new wizard analytics should use this method instead of capture() directly.\n */\n wizardCapture(eventName: string, properties?: Record<string, unknown>): void {\n this.capture(`wizard: ${eventName}`, properties);\n }\n\n /**\n * Flush pending events without firing the \"setup wizard finished\" terminal\n * event. Use this from CLI error paths that exit before any wizard run\n * starts — `shutdown()` would inflate the run count with a \"finished\" event\n * for a parse error that never actually ran the wizard.\n */\n async flush(): Promise<void> {\n await this.client.shutdown();\n }\n\n /**\n * Evaluate all feature flags for the current user at the start of a run.\n * Result is cached; subsequent calls in the same run return the same map.\n * Returns flag key -> string value (booleans become 'true'/'false').\n */\n // Only a getFlag read records an exposure; the bulk response records none.\n async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\n const payloads: Record<string, unknown> = {};\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n logToFile('[flags] evaluating as', {\n distinctId,\n identified: this.distinctId !== undefined,\n personProperties: this.flagPersonProperties(),\n });\n const evaluation = await this.client.evaluateFlags(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n for (const key of WIZARD_FLAG_KEYS) {\n const value = evaluation.getFlag(key);\n if (value === undefined) continue;\n out[key] = String(value);\n const payload = evaluation.getFlagPayload(key);\n if (payload !== undefined) payloads[key] = payload;\n }\n } catch (error) {\n debug('Failed to get all feature flags:', error);\n this.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'get_all_flags' },\n );\n }\n // Outside the fetch guard on purpose: a malformed CI override must fail\n // the run loudly, and a valid one applies even when the fetch failed —\n // CI routing stays deterministic either way.\n const merged = applyCiFlagOverrides(out, payloads);\n this.activeFlags = merged.flags;\n this.activeFlagPayloads = merged.payloads;\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n /** Flag payloads from the same snapshot; empty until the flags are fetched. */\n getWizardFlagPayloads(): Record<string, unknown> {\n return this.activeFlagPayloads;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\n\n // First status wins. The interrupt fallback in start-tui fires\n // shutdown('cancelled') on every TUI teardown; without this guard a run\n // that already reported 'success' would emit a second, contradicting\n // terminal event when the user dismisses the outro.\n if (this.terminalEventSent) {\n return;\n }\n this.terminalEventSent = true;\n\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: 'setup wizard finished',\n properties: {\n // Flat for filtering; the nested `tags` snapshot stays for back-compat.\n ...this.tags,\n run_id: this._runId,\n ...(this.sessionId ? { $session_id: this.sessionId } : {}),\n status,\n tags: this.tags,\n },\n });\n\n await this.client.shutdown();\n }\n}\n\nexport const analytics = new Analytics();\n"],"mappings":"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"}
1
+ {"version":3,"file":"analytics-Duy9Vfk-.js","names":["uuidv4"],"sources":["../src/utils/ci-flag-overrides.ts","../src/utils/analytics.ts"],"sourcesContent":["/**\n * CI-only feature-flag overrides.\n *\n * CI must route deterministically: a run that tests the orchestrator arm says\n * so explicitly instead of depending on a live feature flag someone can edit\n * mid-week. The override env var (see the allowlist in `env.ts`) is a JSON\n * object of flag key → value, merged over whatever PostHog returned.\n *\n * The override path exists only in CI builds (`pnpm build:ci`). Published\n * builds inline NODE_ENV as the literal \"production\", the guards collapse,\n * and tsdown strips the rest from the bundle — and the smoke test asserts the\n * env var names are physically absent from production output (which is also\n * why no comment in this file may spell them out), so this can never quietly\n * become a production surface.\n */\nimport { runtimeEnv } from '@env';\nimport { logToFile } from './debug';\n\nexport function applyCiFlagOverrides(\n flags: Record<string, string>,\n payloads: Record<string, unknown> = {},\n): { flags: Record<string, string>; payloads: Record<string, unknown> } {\n // Compared inline (not via env.ts's IS_PRODUCTION_BUILD) so tsdown replaces\n // it with a literal right here and the bundler can prove the rest of this\n // function unreachable in production builds. The smoke test enforces that.\n if (process.env.NODE_ENV === 'production') return { flags, payloads };\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return { flags, payloads };\n\n let overrides: Record<string, unknown>;\n try {\n overrides = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // A malformed override is a CI misconfiguration. Fail the run loudly\n // rather than silently testing whatever the live flags happen to say.\n throw new Error(\n 'The CI flag-override env var is not valid JSON (expected {\"flag-key\": value, ...}).',\n );\n }\n\n const mergedFlags = { ...flags };\n const mergedPayloads = { ...payloads };\n for (const [key, value] of Object.entries(overrides)) {\n // An object/array override is a flag payload: the flag reads 'true' and\n // the value rides the payload channel, matching a payload-carrying flag.\n if (typeof value === 'object' && value !== null) {\n mergedFlags[key] = 'true';\n mergedPayloads[key] = value;\n } else {\n mergedFlags[key] = String(value);\n }\n }\n logToFile('[flags] CI overrides applied', overrides);\n return { flags: mergedFlags, payloads: mergedPayloads };\n}\n\n/**\n * Task types excluded from this run. The exclusion env var (see the allowlist\n * in `env.ts`) is a comma-separated list (e.g. `dashboard`), set by the CI\n * harness that owns the policy — the wizard and the served content stay\n * run-mode agnostic. CI-build only, same as the flag overrides: published\n * builds strip this path.\n */\nexport function ciExcludedTaskTypes(): readonly string[] {\n if (process.env.NODE_ENV === 'production') return [];\n\n const raw = runtimeEnv('WIZARD_CI_EXCLUDE_TASKS');\n if (!raw) return [];\n const types = raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n if (types.length > 0) logToFile('[flags] CI task exclusions', types);\n return types;\n}\n","import { PostHog } from 'posthog-node';\nimport {\n ANALYTICS_HOST_URL,\n ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,\n ANALYTICS_TEAM_TAG,\n WIZARD_FLAG_KEYS,\n} from '@lib/constants';\nimport type { WizardSession } from '@lib/wizard-session';\nimport type { ApiUser } from '@lib/api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IS_PRODUCTION_BUILD, RUN_SURFACE, TASK_ID, TASK_RUN_ID } from '@env';\nimport { VERSION } from '@lib/version';\nimport { debug, logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\n\n/**\n * The invocation, reduced to flag-safe strings: the command word (first\n * positional, 'default' for the bare flow) and the sorted option NAMES.\n * Option values never leave the machine — they carry paths, emails, keys.\n */\nfunction invocationProperties(): { command: string; cli_flags: string } {\n const args = process.argv.slice(2);\n const command = args.find((a) => !a.startsWith('-')) ?? 'default';\n const flags = [\n ...new Set(\n args\n .filter((a) => a.startsWith('--'))\n .map((a) => a.slice(2).split('=')[0]),\n ),\n ].sort();\n return { command, cli_flags: flags.join(',') };\n}\n\n/**\n * Extract a standard property bag from the current session.\n * Used by store-level analytics and available for ad-hoc captures.\n */\nexport function sessionProperties(\n session: WizardSession,\n): Record<string, unknown> {\n return {\n integration: session.integration,\n detected_framework: session.detectedFrameworkLabel,\n typescript: session.typescript,\n project_id: session.credentials?.projectId,\n discovered_features: session.discoveredFeatures,\n additional_features: session.additionalFeatureQueue,\n run_phase: session.runPhase,\n };\n}\n\nexport function groupsFromUser(\n user: ApiUser | null,\n host: string,\n): Record<string, string> {\n const groups: Record<string, string> = { instance: host };\n if (!user) return groups;\n\n const organizationId = user.organization?.id;\n if (organizationId) groups.organization = organizationId;\n\n const customerId = user.organization?.customer_id;\n if (customerId) groups.customer = customerId;\n\n const projectUuid = user.team?.uuid;\n if (projectUuid) groups.project = projectUuid;\n\n return groups;\n}\n\nconst WIZARD_FLAGS: ReadonlySet<string> = new Set(WIZARD_FLAG_KEYS);\n\n// Widen back to the SDK's shape — a filter on `true` never matches `'true'`.\nfunction flagResponseValue(value: string): string | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return value;\n}\n\nexport class Analytics {\n private client: PostHog;\n private tags: Record<string, string | boolean | number | null | undefined> =\n {};\n private distinctId?: string;\n private anonymousId: string;\n private _runId: string;\n private sessionId: string | null = null;\n private appName = 'wizard';\n private activeFlags: Record<string, string> | null = null;\n private activeFlagPayloads: Record<string, unknown> = {};\n private groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\n private terminalEventSent = false;\n\n constructor() {\n this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {\n host: ANALYTICS_HOST_URL,\n flushAt: 1,\n flushInterval: 0,\n enableExceptionAutocapture: true,\n before_send: (event) => {\n if (!event) return event;\n if (Object.keys(this.groups).length > 0) {\n event.groups = { ...this.groups, ...event.groups };\n }\n // Autocaptured exceptions arrive with a random uuid and\n // `$process_person_profile: false` — reattach the run's identity\n // and tags so they land on the same person as everything else.\n if (event.event === '$exception') {\n event.distinctId = this.distinctId ?? this.anonymousId;\n const { $process_person_profile, ...properties } =\n event.properties ?? {};\n void $process_person_profile;\n event.properties = { ...this.tags, ...properties };\n }\n // The SDK captures this one itself, bypassing capture(), so tags merge here.\n if (event.event === '$feature_flag_called') {\n event.properties = { ...this.tags, ...(event.properties ?? {}) };\n }\n return event;\n },\n });\n\n this.tags = { $app_name: this.appName };\n // Tag every run with its build type so prod / dev / ci / headless segment\n // cleanly in analytics. tsdown inlines IS_PRODUCTION_BUILD to `true` in\n // published builds and `false` for dev/tsx/test runs. Non-interactive runs\n // upgrade this in runWizardCI: dev `--ci` runs to 'ci', published headless\n // runs to 'headless'.\n this.tags.build = IS_PRODUCTION_BUILD ? 'prod' : 'dev';\n\n this.tags.run_surface = RUN_SURFACE;\n\n // The build this run came from. Already sent as a flag-evaluation person\n // property; tagging it makes every event attributable to a release, so a\n // regression can be bounded to the versions that carry it.\n this.tags.version = VERSION;\n\n // Cloud runs only: the task run that owns the sandbox. Without these the\n // wizard's events and the run that launched it are two unrelated streams,\n // since run_id above is minted per process and never leaves the wizard.\n // Left unset for local runs so the properties stay absent rather than null.\n if (TASK_RUN_ID) this.tags.task_run_id = TASK_RUN_ID;\n if (TASK_ID) this.tags.task_id = TASK_ID;\n\n this.anonymousId = uuidv4();\n\n // One id per process = one id per wizard run, registered in the tag bag\n // so it rides on every capture, exception, and autocaptured exception\n // (all of which merge `this.tags`). Lets you separate two runs by the\n // same logged-in user, who otherwise share one distinct id. Distinct\n // from `anonymousId`, the pre-login *person* id that gets aliased onto\n // the real user at login. `$session_id` is intentionally not set here —\n // it stays null until OAuth completes (see identifyUser).\n this._runId = uuidv4();\n this.tags.run_id = this._runId;\n\n this.distinctId = undefined;\n }\n\n /** Per-process run id, tagged on every event and gateway trace. */\n get runId(): string {\n return this._runId;\n }\n\n /** Build type for this run ('prod' | 'dev' | 'ci' | 'headless') — the same value tagged on every analytics event. */\n get build(): string {\n return String(this.tags.build ?? 'dev');\n }\n\n /**\n * Associate the run with the logged-in user, once per id. Identifies them\n * (email, name) and records those person properties so events carry them and\n * feature flags can target the individual user — without the email here the\n * wizard only sends `$app_name`, so email-targeted flags never match. Opens\n * the analytics session on first login, then aliases the run's anonymous id\n * onto the identified person so pre-login events merge in.\n */\n identifyUser(user: ApiUser) {\n const distinctId = user.distinct_id;\n if (this.distinctId === distinctId || distinctId === this.anonymousId) {\n return;\n }\n this.distinctId = distinctId;\n // Open the analytics session on first login. Null until here, so\n // pre-OAuth events carry only `run_id`; from now on every event also\n // carries `$session_id` and PostHog groups the authenticated run into a\n // native Session. Stored in the tag bag so it rides on every subsequent\n // capture and exception.\n if (!this.sessionId) {\n this.sessionId = uuidv4();\n this.tags.$session_id = this.sessionId;\n }\n const props: Record<string, string> = {};\n if (user.email) props.email = user.email;\n const name = [user.first_name, user.last_name]\n .filter(Boolean)\n .join(' ')\n .trim();\n if (name) props.name = name;\n this.personProperties = props;\n this.client.identify({ distinctId, properties: { $set: props } });\n this.client.alias({\n distinctId,\n alias: this.anonymousId,\n });\n // The flag snapshot is per identity. Anything evaluated before login (the\n // intro screen reads the tools-menu flag) was anonymous — drop it so the\n // next read re-evaluates as this user.\n this.activeFlags = null;\n this.activeFlagPayloads = {};\n }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n // Environment facts flag conditions can scope on — evaluation-time only,\n // never persisted onto the person. `version` gates an experiment to\n // releases that implement it (a condition on a property older builds\n // don't send silently never matches them); `run_surface`/`build` scope\n // by invocation surface; `command`/`cli_flags` by subcommand; `os`/\n // `node_major` by platform.\n return {\n $app_name: this.appName,\n version: VERSION,\n run_surface: RUN_SURFACE,\n build: String(this.tags.build ?? 'dev'),\n os: process.platform,\n node_major: process.versions.node.split('.')[0],\n ...invocationProperties(),\n ...this.personProperties,\n };\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n setGroups(groups: Record<string, string>) {\n this.groups = groups;\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n this.client.captureException(error, this.distinctId ?? this.anonymousId, {\n team: ANALYTICS_TEAM_TAG,\n ...this.tags,\n ...properties,\n });\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: eventName,\n properties: {\n ...this.tags,\n ...this.wizardFlagFeatureProperties(),\n ...properties,\n },\n });\n }\n\n // Built from the resolved map, not the SDK snapshot: a CI-forced variant must own its events.\n private wizardFlagFeatureProperties(): Record<string, unknown> {\n if (this.activeFlags === null) return {};\n const props: Record<string, unknown> = {};\n const active: string[] = [];\n for (const [key, value] of Object.entries(this.activeFlags)) {\n if (!WIZARD_FLAGS.has(key)) continue;\n const resolved = flagResponseValue(value);\n props[`$feature/${key}`] = resolved;\n if (resolved !== false) active.push(key);\n }\n if (active.length > 0) props.$active_feature_flags = active.sort();\n return props;\n }\n\n /**\n * Capture a wizard-specific event. Automatically prepends \"wizard: \" to the event name.\n * All new wizard analytics should use this method instead of capture() directly.\n */\n wizardCapture(eventName: string, properties?: Record<string, unknown>): void {\n this.capture(`wizard: ${eventName}`, properties);\n }\n\n /**\n * Flush pending events without firing the \"setup wizard finished\" terminal\n * event. Use this from CLI error paths that exit before any wizard run\n * starts — `shutdown()` would inflate the run count with a \"finished\" event\n * for a parse error that never actually ran the wizard.\n */\n async flush(): Promise<void> {\n await this.client.shutdown();\n }\n\n /**\n * Evaluate all feature flags for the current user at the start of a run.\n * Result is cached; subsequent calls in the same run return the same map.\n * Returns flag key -> string value (booleans become 'true'/'false').\n */\n // Only a getFlag read records an exposure; the bulk response records none.\n async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\n const payloads: Record<string, unknown> = {};\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n logToFile('[flags] evaluating as', {\n distinctId,\n identified: this.distinctId !== undefined,\n personProperties: this.flagPersonProperties(),\n });\n const evaluation = await this.client.evaluateFlags(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n for (const key of WIZARD_FLAG_KEYS) {\n const value = evaluation.getFlag(key);\n if (value === undefined) continue;\n out[key] = String(value);\n const payload = evaluation.getFlagPayload(key);\n if (payload !== undefined) payloads[key] = payload;\n }\n } catch (error) {\n debug('Failed to get all feature flags:', error);\n this.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'get_all_flags' },\n );\n }\n // Outside the fetch guard on purpose: a malformed CI override must fail\n // the run loudly, and a valid one applies even when the fetch failed —\n // CI routing stays deterministic either way.\n const merged = applyCiFlagOverrides(out, payloads);\n this.activeFlags = merged.flags;\n this.activeFlagPayloads = merged.payloads;\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n /** Flag payloads from the same snapshot; empty until the flags are fetched. */\n getWizardFlagPayloads(): Record<string, unknown> {\n return this.activeFlagPayloads;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\n\n // First status wins. The interrupt fallback in start-tui fires\n // shutdown('cancelled') on every TUI teardown; without this guard a run\n // that already reported 'success' would emit a second, contradicting\n // terminal event when the user dismisses the outro.\n if (this.terminalEventSent) {\n return;\n }\n this.terminalEventSent = true;\n\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: 'setup wizard finished',\n properties: {\n // Flat for filtering; the nested `tags` snapshot stays for back-compat.\n ...this.tags,\n run_id: this._runId,\n ...(this.sessionId ? { $session_id: this.sessionId } : {}),\n status,\n tags: this.tags,\n },\n });\n\n await this.client.shutdown();\n }\n}\n\nexport const analytics = new Analytics();\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { pt as WIZARD_USER_AGENT } from "./debug-BYIoy1Oz.js";
3
- import { n as analytics } from "./analytics-YLZKudpE.js";
2
+ import { pt as WIZARD_USER_AGENT } from "./debug-3FJsmlmC.js";
3
+ import { n as analytics } from "./analytics-Duy9Vfk-.js";
4
4
  import axios from "axios";
5
5
  import { z } from "zod";
6
6
  //#region src/lib/api.ts
@@ -170,4 +170,4 @@ function handleApiError(error, operation) {
170
170
  //#endregion
171
171
  export { fetchUserData as a, fetchSlackConnected as i, api_exports as n, handleApiError as o, fetchProjectData as r, ApiError as t };
172
172
 
173
- //# sourceMappingURL=api-DlRJAFI2.js.map
173
+ //# sourceMappingURL=api-DEb25KDf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-DlRJAFI2.js","names":[],"sources":["../src/lib/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api-DEb25KDf.js","names":[],"sources":["../src/lib/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":"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"}
package/dist/bin.js CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { Ct as isHeadless, L as Integration, St as headlessOption, U as POSTHOG_DOCS_URL, a as getLogFilePath, f as getUI, gt as VERSION, m as LoggingUI, mt as getSkillsBaseUrl, p as setUI, pt as WIZARD_USER_AGENT, r as debug, s as logToFile, wt as regionOption } from "./debug-BYIoy1Oz.js";
3
- import { n as analytics } from "./analytics-YLZKudpE.js";
4
- import { r as setEntryCommand } from "./telemetry-C72C8V6q.js";
5
- import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-CYxETRCt.js";
6
- import { n as isUsingTypeScript } from "./setup-utils-e78zkZoW.js";
7
- import { o as handleApiError } from "./api-DlRJAFI2.js";
2
+ import { Ct as isHeadless, L as Integration, St as headlessOption, U as POSTHOG_DOCS_URL, a as getLogFilePath, f as getUI, gt as VERSION, m as LoggingUI, mt as getSkillsBaseUrl, p as setUI, pt as WIZARD_USER_AGENT, r as debug, s as logToFile, wt as regionOption } from "./debug-3FJsmlmC.js";
3
+ import { n as analytics } from "./analytics-Duy9Vfk-.js";
4
+ import { r as setEntryCommand } from "./telemetry-4VTeK1h9.js";
5
+ import { a as safeReadFile, o as walkProjectFiles } from "./bounded-fs-CvYIzQbg.js";
6
+ import { n as isUsingTypeScript } from "./setup-utils-DJF-LjYE.js";
7
+ import { o as handleApiError } from "./api-DEb25KDf.js";
8
8
  import "./wizard-session-BUhlJ1b8.js";
9
- import { r as runCleanups } from "./wizard-abort-DRscjUOg.js";
10
- import { n as isNonInteractiveEnvironment } from "./environment-Br-oy8cq.js";
11
- import { n as runAgent, s as authenticate } from "./agent-runner-X3JWS2WK.js";
12
- import { p as writeJsonAtomic } from "./queue-tools-BtJ7_fEW.js";
13
- import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-C4CV7p0L.js";
14
- import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-DKRsn0lN.js";
15
- import { i as SPINNER_MESSAGE } from "./registry-B-whj8SQ.js";
16
- import { C as resolveProjectDir, _ as WAREHOUSE_ABORT_CASES, a as LINE_CHART_BLOCK, b as detectWarehouseSources, d as isClearBlock, g as HEALTH_CHECK_STEP, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as detectWarehousePrerequisites, w as gatherFrameworkContext, x as detectIntegrationProjects, y as getDetectedWarehouseSources } from "./posthog-integration-CoAID6XW.js";
17
- import { n as readApiKeyFromEnv } from "./env-api-key-B7kiHT3h.js";
9
+ import { i as wizardAbort, r as runCleanups } from "./wizard-abort-BeuIRy5a.js";
10
+ import { n as isNonInteractiveEnvironment } from "./environment-Cyq61jKV.js";
11
+ import { n as runAgent, s as authenticate } from "./agent-runner-B4ZdNkj4.js";
12
+ import { p as writeJsonAtomic } from "./queue-tools-DED9kPuc.js";
13
+ import { S as WIZARD_TOOL_NAMES, T as fetchSkillMenu, l as recoverOrphanedSettingsBackups, p as AgentSignals } from "./agent-interface-BQfwaSe-.js";
14
+ import { S as SETUP_REPORT_FILE, _ as AUDIT_CHECKS_KEY, g as AUDIT_CHECKS_FILE, v as AUDIT_REPORT_FILE } from "./yara-hooks-CCGjiVA2.js";
15
+ import { i as SPINNER_MESSAGE, t as FRAMEWORK_REGISTRY } from "./registry-Dh9aqVC6.js";
16
+ import { C as scopeInstallDirToProject, D as detectFramework, E as gatherFrameworkContext, S as detectIntegrationProjects, T as resolveProjectDir, _ as HEALTH_CHECK_STEP, a as LINE_CHART_BLOCK, b as getDetectedWarehouseSources, d as isClearBlock, g as detectPostHogIntegration, h as Icons, i as FUNNEL_BLOCK, m as Colors, n as integrationRunStep, o as PRODUCT_SUITE_BLOCK, r as posthogIntegrationConfig, s as StatusPeekTrigger, u as wordWrap, v as WAREHOUSE_ABORT_CASES, x as detectWarehouseSources, y as detectWarehousePrerequisites } from "./posthog-integration-BHqFQd4U.js";
17
+ import { n as readApiKeyFromEnv } from "./env-api-key-uk6HInaB.js";
18
18
  import { satisfies } from "semver";
19
19
  import yargs from "yargs";
20
20
  import { hideBin } from "yargs/helpers";
@@ -202,7 +202,7 @@ function runProvision(argv) {
202
202
  }
203
203
  async function provision({ email, region, name, baseUrl, jsonMode }) {
204
204
  try {
205
- const { provisionNewAccount } = await import("./provisioning-xirriYQJ.js").then((n) => n.r);
205
+ const { provisionNewAccount } = await import("./provisioning-B41dKvno.js").then((n) => n.r);
206
206
  if (!jsonMode) getUI().log.info(`Provisioning account for ${email} in ${region}...`);
207
207
  emitResult(await provisionNewAccount(email, name, region, { baseUrl }), jsonMode);
208
208
  process.exit(0);
@@ -269,22 +269,22 @@ const basicIntegrationCommand = {
269
269
  setEntryCommand("integrate");
270
270
  (async () => {
271
271
  if (isHeadless(argv)) {
272
- const { runHeadlessInstall } = await import("./ci-install-Cyqj_i3I.js");
272
+ const { runHeadlessInstall } = await import("./ci-install-k2HWuLQ3.js");
273
273
  return runHeadlessInstall(argv);
274
274
  }
275
275
  if (argv.ci) {
276
- const { runCIInstall } = await import("./ci-install-Cyqj_i3I.js");
276
+ const { runCIInstall } = await import("./ci-install-k2HWuLQ3.js");
277
277
  return runCIInstall(argv);
278
278
  }
279
279
  if (isNonInteractiveEnvironment()) {
280
- const { failNonInteractive } = await import("./non-interactive-BR4kmPE5.js");
280
+ const { failNonInteractive } = await import("./non-interactive-DOG-p4ku.js");
281
281
  return failNonInteractive();
282
282
  }
283
283
  if (argv.playground) {
284
- const { runPlayground } = await import("./playground-BSMgOp3k.js");
284
+ const { runPlayground } = await import("./playground-DaTeK9-L.js");
285
285
  return runPlayground();
286
286
  }
287
- const { runInteractive } = await import("./interactive-CYv97Lgg.js");
287
+ const { runInteractive } = await import("./interactive-BDCUPSmr.js");
288
288
  runInteractive(argv);
289
289
  })();
290
290
  }
@@ -3876,6 +3876,147 @@ const mcpAnalyticsConfig = createSkillProgram({
3876
3876
  ]
3877
3877
  });
3878
3878
  //#endregion
3879
+ //#region src/lib/programs/replay-vision/index.ts
3880
+ const REPLAY_VISION_REPORT_FILE = "posthog-replay-vision-report.md";
3881
+ /**
3882
+ * The platforms session replay can actually record on. Replay vision watches
3883
+ * recordings, so a platform with no recordings has nothing to set up — the
3884
+ * run must stop before any work, not after a pointless agent run.
3885
+ *
3886
+ * Web frameworks record through posthog-js (server-rendered frameworks
3887
+ * included — they serve pages), and the mobile SDKs with replay support are
3888
+ * React Native, Android, iOS, and Flutter. Excluded: pure backend targets
3889
+ * (`javascript_node`, `python`, `ruby`) and KMP, which has no replay support
3890
+ * yet.
3891
+ */
3892
+ const REPLAY_VISION_SUPPORTED = new Set([
3893
+ "nextjs",
3894
+ "nuxt",
3895
+ "vue",
3896
+ "react-router",
3897
+ "tanstack-start",
3898
+ "tanstack-router",
3899
+ "angular",
3900
+ "astro",
3901
+ "sveltekit",
3902
+ "javascript_web",
3903
+ "django",
3904
+ "flask",
3905
+ "fastapi",
3906
+ "laravel",
3907
+ "rails",
3908
+ "react-native",
3909
+ "android",
3910
+ "swift",
3911
+ "flutter"
3912
+ ]);
3913
+ async function abortUnsupportedPlatform(integration) {
3914
+ await wizardAbort({
3915
+ message: `Session replay isn't available for ${FRAMEWORK_REGISTRY[integration]?.metadata.name ?? integration} projects, and Replay vision needs session recordings to watch — so there is nothing to set up here.
3916
+
3917
+ If this repo also contains a web or mobile app, run the command from that project directory instead. See what replay supports at:
3918
+ https://posthog.com/docs/session-replay`,
3919
+ error: /* @__PURE__ */ new Error(`Replay vision unsupported platform: ${integration}`)
3920
+ });
3921
+ }
3922
+ /**
3923
+ * `[ABORT]` reasons the replay-vision skill emits when the run can't proceed.
3924
+ * Kept in sync with the stop conditions in the skill's `description.md`
3925
+ * (context-mill `context/skills/replay-vision`).
3926
+ */
3927
+ const REPLAY_VISION_ABORT_CASES = [{
3928
+ match: /^replay vision not available for this project$/i,
3929
+ message: "Replay vision is not available for this project",
3930
+ body: "Every Replay vision scanner endpoint reported that the feature is not available here yet. Session replay setup done so far is kept. See https://posthog.com/docs/replay-vision for availability."
3931
+ }];
3932
+ /**
3933
+ * Framework detection ahead of the run, exactly like the default integration
3934
+ * program. The orchestrator requires it: `session.skillId` must hold the
3935
+ * detected framework id before the run arm starts, because the runner
3936
+ * resolves the reference integration skill and every task's mini-skill
3937
+ * variants (`integration-v2-install`, `integration-v2-init`, …) against it in
3938
+ * preflight. Without this step the session would still carry the program's
3939
+ * own skill id and preflight would abort.
3940
+ */
3941
+ const DETECT_STEP = {
3942
+ id: "detect",
3943
+ label: "Detecting framework",
3944
+ onReady: async (ctx) => {
3945
+ const integration = await detectFramework(ctx.session.installDir);
3946
+ if (integration && !REPLAY_VISION_SUPPORTED.has(integration)) {
3947
+ await abortUnsupportedPlatform(integration);
3948
+ return;
3949
+ }
3950
+ await detectPostHogIntegration(ctx);
3951
+ }
3952
+ };
3953
+ /**
3954
+ * `wizard replay-vision` — flat skill command on the orchestrator sequence.
3955
+ *
3956
+ * Makes session replay record (server toggle + client init check), then
3957
+ * creates vision scanners scoped to the product's key flows, read out of the
3958
+ * repo. The orchestrator runs the `replay-vision` agent flow (context-mill
3959
+ * `context/agents/replay-vision`): the seed enqueues the install/init tasks
3960
+ * (copied from integration-v2, sharing its step-skills) when the project has
3961
+ * no PostHog yet, so the command works on uninstrumented repos instead of
3962
+ * aborting.
3963
+ *
3964
+ * Departures from a plain `createSkillProgram`:
3965
+ * - `DETECT_STEP` in front, so `session.skillId` carries the framework id the
3966
+ * orchestrator's preflight resolves reference + mini-skill variants with.
3967
+ * - `agentFlow` pinned (the id would default to the same value — explicit so
3968
+ * renaming the program can't silently detach the flow).
3969
+ * - `ciPreRun` mirrors the default integration program: scope the install dir
3970
+ * to the right project (monorepos), then detect the framework — the
3971
+ * headless equivalent of the detect step's onReady hook.
3972
+ */
3973
+ const replayVisionConfig = {
3974
+ ...createSkillProgram({
3975
+ skillId: "replay-vision-setup",
3976
+ command: "replay-vision",
3977
+ id: "replay-vision",
3978
+ description: "Set up PostHog Replay vision scanners for your product",
3979
+ integrationLabel: "replay-vision",
3980
+ customPrompt: `Set up PostHog Replay vision. Run the \`replay-vision\` skill end-to-end: make sure session replay is recording (server-side enable plus a posthog-js init check), then create the vision scanners the skill defines, scoped to this product's key flows read out of the repo. If PostHog is not integrated yet, install and initialize the SDK first as the skill instructs — do not abort. The final report is written to ./${REPLAY_VISION_REPORT_FILE}.`,
3981
+ successMessage: `Replay vision configured! View the report at ./${REPLAY_VISION_REPORT_FILE}`,
3982
+ reportFile: REPLAY_VISION_REPORT_FILE,
3983
+ docsUrl: "https://posthog.com/docs/replay-vision",
3984
+ spinnerMessage: "Setting up Replay vision...",
3985
+ estimatedDurationMinutes: 6,
3986
+ abortCases: REPLAY_VISION_ABORT_CASES
3987
+ }),
3988
+ agentFlow: "replay-vision",
3989
+ steps: [DETECT_STEP, ...AGENT_SKILL_STEPS],
3990
+ ciPreRun: async (session) => {
3991
+ await scopeInstallDirToProject(session);
3992
+ const integration = await detectFramework(session.installDir);
3993
+ if (!integration) {
3994
+ await wizardAbort({ message: "Could not auto-detect your framework for this project." });
3995
+ return;
3996
+ }
3997
+ if (!REPLAY_VISION_SUPPORTED.has(integration)) {
3998
+ await abortUnsupportedPlatform(integration);
3999
+ return;
4000
+ }
4001
+ session.integration = integration;
4002
+ analytics.setTag("integration", integration);
4003
+ const frameworkConfig = FRAMEWORK_REGISTRY[integration];
4004
+ session.frameworkConfig = frameworkConfig;
4005
+ session.skillId = integration;
4006
+ const context = await gatherFrameworkContext(frameworkConfig, {
4007
+ installDir: session.installDir,
4008
+ debug: session.debug,
4009
+ default: false,
4010
+ signup: session.signup,
4011
+ localMcp: session.localMcp,
4012
+ ci: true,
4013
+ benchmark: session.benchmark,
4014
+ yaraReport: session.yaraReport
4015
+ });
4016
+ for (const [key, value] of Object.entries(context)) if (!(key in session.frameworkContext)) session.frameworkContext[key] = value;
4017
+ }
4018
+ };
4019
+ //#endregion
3879
4020
  //#region src/lib/programs/ai-observability/index.ts
3880
4021
  const AI_OBSERVABILITY_STEPS = AGENT_SKILL_STEPS.map((step) => step.id === "intro" ? {
3881
4022
  ...step,
@@ -3979,6 +4120,7 @@ const PROGRAM_REGISTRY = [
3979
4120
  mcpRemoveConfig,
3980
4121
  mcpTutorialConfig,
3981
4122
  mcpAnalyticsConfig,
4123
+ replayVisionConfig,
3982
4124
  aiObservabilityConfig,
3983
4125
  slackConnectConfig
3984
4126
  ];
@@ -4003,6 +4145,7 @@ const Program = {
4003
4145
  McpRemove: mcpRemoveConfig.id,
4004
4146
  McpTutorial: mcpTutorialConfig.id,
4005
4147
  McpAnalytics: mcpAnalyticsConfig.id,
4148
+ ReplayVision: replayVisionConfig.id,
4006
4149
  AiObservability: aiObservabilityConfig.id,
4007
4150
  SlackConnect: slackConnectConfig.id
4008
4151
  };
@@ -4050,12 +4193,12 @@ const mcpAddCommand = {
4050
4193
  function runMcpAdd(argv) {
4051
4194
  const features = parseFeatures(argv.features);
4052
4195
  (async () => {
4053
- const { readApiKeyFromEnv } = await import("./env-api-key-B7kiHT3h.js").then((n) => n.t);
4196
+ const { readApiKeyFromEnv } = await import("./env-api-key-uk6HInaB.js").then((n) => n.t);
4054
4197
  const apiKey = argv.apiKey || readApiKeyFromEnv();
4055
4198
  const debug = argv.debug;
4056
4199
  const localMcp = argv.local;
4057
4200
  try {
4058
- const { startTUI } = await import("./start-tui-B08PfE0V.js");
4201
+ const { startTUI } = await import("./start-tui-0p-Ybwxe.js");
4059
4202
  const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
4060
4203
  const tui = startTUI(VERSION, Program.McpAdd);
4061
4204
  tui.store.session = buildSession({
@@ -4068,7 +4211,7 @@ function runMcpAdd(argv) {
4068
4211
  } catch (error) {
4069
4212
  if (!isTUIUnavailable(error)) throw error;
4070
4213
  setUI(new LoggingUI());
4071
- const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-Bsyb6MUB.js").then((n) => n.r);
4214
+ const { addMCPServerToClientsStep } = await import("./add-mcp-server-to-clients-D9l1dyw9.js").then((n) => n.r);
4072
4215
  await addMCPServerToClientsStep({
4073
4216
  local: localMcp,
4074
4217
  features,
@@ -4098,7 +4241,7 @@ function runMcpRemove(argv) {
4098
4241
  const debug = argv.debug;
4099
4242
  const localMcp = argv.local;
4100
4243
  try {
4101
- const { startTUI } = await import("./start-tui-B08PfE0V.js");
4244
+ const { startTUI } = await import("./start-tui-0p-Ybwxe.js");
4102
4245
  const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
4103
4246
  const tui = startTUI(VERSION, Program.McpRemove);
4104
4247
  tui.store.session = buildSession({
@@ -4109,7 +4252,7 @@ function runMcpRemove(argv) {
4109
4252
  } catch (error) {
4110
4253
  if (!isTUIUnavailable(error)) throw error;
4111
4254
  setUI(new LoggingUI());
4112
- const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-Bsyb6MUB.js").then((n) => n.r);
4255
+ const { removeMCPServerFromClientsStep } = await import("./add-mcp-server-to-clients-D9l1dyw9.js").then((n) => n.r);
4113
4256
  await removeMCPServerFromClientsStep({ local: localMcp });
4114
4257
  }
4115
4258
  })();
@@ -4131,7 +4274,7 @@ function runMcpTutorial(argv) {
4131
4274
  const debug = argv.debug;
4132
4275
  const localMcp = argv.local;
4133
4276
  try {
4134
- const { startTUI } = await import("./start-tui-B08PfE0V.js");
4277
+ const { startTUI } = await import("./start-tui-0p-Ybwxe.js");
4135
4278
  const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
4136
4279
  const tui = startTUI(VERSION, Program.McpTutorial);
4137
4280
  tui.store.session = buildSession({
@@ -4213,10 +4356,10 @@ function runWizard(config, options) {
4213
4356
  (async () => {
4214
4357
  try {
4215
4358
  const installDir = options.installDir || process.cwd();
4216
- const { startTUI } = await import("./start-tui-B08PfE0V.js");
4359
+ const { startTUI } = await import("./start-tui-0p-Ybwxe.js");
4217
4360
  const { buildSession, RunPhase } = await import("./wizard-session-DZUDtS5d.js");
4218
- const { TaskStreamPush } = await import("./task-stream-DvTf1NOr.js");
4219
- const { PostHogDestination } = await import("./posthog-ZWC-083a.js");
4361
+ const { TaskStreamPush } = await import("./task-stream-B0Eo4KX1.js");
4362
+ const { PostHogDestination } = await import("./posthog-DvM4V-to.js");
4220
4363
  tui = startTUI(WIZARD_VERSION, config.id);
4221
4364
  const activeTui = tui;
4222
4365
  const session = buildSession({
@@ -4282,7 +4425,7 @@ function runWizard(config, options) {
4282
4425
  if (shown(step)) await advanceStep(step, activeTui.store, config);
4283
4426
  }
4284
4427
  else if (skipAgent) {
4285
- const { getOrAskForProjectData } = await import("./setup-utils-e78zkZoW.js").then((n) => n.r);
4428
+ const { getOrAskForProjectData } = await import("./setup-utils-DJF-LjYE.js").then((n) => n.r);
4286
4429
  const { projectApiKey, host, accessToken, projectId } = await getOrAskForProjectData({
4287
4430
  signup: session.signup,
4288
4431
  ci: session.ci,
@@ -4384,10 +4527,10 @@ function runNonInteractive(config, options, mode) {
4384
4527
  (async () => {
4385
4528
  const path = await import("path");
4386
4529
  const { buildSession, RunPhase, OutroKind } = await import("./wizard-session-DZUDtS5d.js");
4387
- const { readEnvironment } = await import("./environment-Br-oy8cq.js").then((n) => n.t);
4388
- const { readApiKeyFromEnv } = await import("./env-api-key-B7kiHT3h.js").then((n) => n.t);
4389
- const { configureLogFileFromEnvironment, logToFile } = await import("./debug-2W78IhFE.js");
4390
- const { wizardAbort, WizardError } = await import("./wizard-abort-BUpx4qnK.js");
4530
+ const { readEnvironment } = await import("./environment-Cyq61jKV.js").then((n) => n.t);
4531
+ const { readApiKeyFromEnv } = await import("./env-api-key-uk6HInaB.js").then((n) => n.t);
4532
+ const { configureLogFileFromEnvironment, logToFile } = await import("./debug-D7eikh3K.js");
4533
+ const { wizardAbort, WizardError } = await import("./wizard-abort-DNHeIZ8N.js");
4391
4534
  configureLogFileFromEnvironment();
4392
4535
  const env = readEnvironment();
4393
4536
  const apiKey = options.apiKey ?? readApiKeyFromEnv() ?? void 0;
@@ -4420,9 +4563,9 @@ function runNonInteractive(config, options, mode) {
4420
4563
  let store = null;
4421
4564
  let taskStream = null;
4422
4565
  if (mode === "headless") {
4423
- const { WizardStore } = await import("./store-B-ctPH7O.js").then((n) => n.n);
4424
- const { HeadlessUI } = await import("./headless-ui-Dd7Ty4Ul.js");
4425
- const { TaskStreamPush, PostHogDestination } = await import("./task-stream-DvTf1NOr.js");
4566
+ const { WizardStore } = await import("./store-BgOhG49V.js").then((n) => n.n);
4567
+ const { HeadlessUI } = await import("./headless-ui-MpPKNNH9.js");
4568
+ const { TaskStreamPush, PostHogDestination } = await import("./task-stream-B0Eo4KX1.js");
4426
4569
  store = new WizardStore(config.id);
4427
4570
  store.session = session;
4428
4571
  setUI(new HeadlessUI(store));
@@ -4478,7 +4621,7 @@ function runNonInteractive(config, options, mode) {
4478
4621
  });
4479
4622
  }
4480
4623
  }
4481
- const { runAgent } = await import("./agent-runner-X3JWS2WK.js").then((n) => n.t);
4624
+ const { runAgent } = await import("./agent-runner-B4ZdNkj4.js").then((n) => n.t);
4482
4625
  await runAgent(config, session);
4483
4626
  await settleStream(RunPhase.Completed);
4484
4627
  } catch (error) {
@@ -4622,6 +4765,17 @@ function nativeCommandFactory(config, opts = {}) {
4622
4765
  */
4623
4766
  const mcpAnalyticsCommand = nativeCommandFactory(mcpAnalyticsConfig);
4624
4767
  //#endregion
4768
+ //#region src/commands/replay-vision.ts
4769
+ /**
4770
+ * `wizard replay-vision` — flat skill command, set up Replay vision today.
4771
+ *
4772
+ * Enables session replay recording and creates vision scanners tailored to
4773
+ * the product's key flows. Runs the `replay-vision` orchestrator flow, which
4774
+ * reuses the integration-v2 install/init mini-agents when the repo has no
4775
+ * PostHog integration yet.
4776
+ */
4777
+ const replayVisionCommand = nativeCommandFactory(replayVisionConfig);
4778
+ //#endregion
4625
4779
  //#region src/commands/ai-observability.ts
4626
4780
  /**
4627
4781
  * `wizard ai-observability` — flat skill command, wire AI Observability into a
@@ -5684,7 +5838,7 @@ async function runDoctorCI(options) {
5684
5838
  getUI().intro("Welcome to the PostHog setup wizard");
5685
5839
  getUI().log.info("Running posthog-doctor in CI mode");
5686
5840
  try {
5687
- const { getOrAskForProjectData } = await import("./setup-utils-e78zkZoW.js").then((n) => n.r);
5841
+ const { getOrAskForProjectData } = await import("./setup-utils-DJF-LjYE.js").then((n) => n.r);
5688
5842
  const { host, accessToken, projectId } = await getOrAskForProjectData({
5689
5843
  signup: false,
5690
5844
  ci: true,
@@ -5702,7 +5856,7 @@ async function runDoctorCI(options) {
5702
5856
  for (const issue of sorted) getUI().log.info(` • [${issue.severity}] ${getKindMeta(issue.kind).title}`);
5703
5857
  process.exit(1);
5704
5858
  } catch (error) {
5705
- const { ApiError } = await import("./api-DlRJAFI2.js").then((n) => n.n);
5859
+ const { ApiError } = await import("./api-DEb25KDf.js").then((n) => n.n);
5706
5860
  const message = error instanceof ApiError && error.statusCode === 401 ? "Your PostHog API key is invalid or expired." : error instanceof Error ? error.message : String(error);
5707
5861
  getUI().log.error(`Doctor failed: ${message}`);
5708
5862
  process.exit(1);
@@ -5784,7 +5938,7 @@ function runSlackConnect(argv) {
5784
5938
  (async () => {
5785
5939
  const debug = argv.debug;
5786
5940
  try {
5787
- const { startTUI } = await import("./start-tui-B08PfE0V.js");
5941
+ const { startTUI } = await import("./start-tui-0p-Ybwxe.js");
5788
5942
  const { buildSession } = await import("./wizard-session-DZUDtS5d.js");
5789
5943
  const tui = startTUI(VERSION, Program.SlackConnect);
5790
5944
  tui.store.session = buildSession({
@@ -6115,7 +6269,7 @@ function resolveInstallDir() {
6115
6269
  if (inline) return inline.slice(14);
6116
6270
  return process.env.POSTHOG_WIZARD_INSTALL_DIR ?? process.cwd();
6117
6271
  }
6118
- Wizard.use(basicIntegrationCommand).use(mcpCommand).use(mcpAnalyticsCommand).use(aiObservabilityCommand).use(cliCommand).use(auditCommand).use(doctorCommand).use(migrateCommand).use(revenueCommand).use(warehouseCommand).use(selfDrivingCommand).use(slackCommand).use(uploadSourcemapsCommand).use(skillCommand).init();
6272
+ Wizard.use(basicIntegrationCommand).use(mcpCommand).use(mcpAnalyticsCommand).use(replayVisionCommand).use(aiObservabilityCommand).use(cliCommand).use(auditCommand).use(doctorCommand).use(migrateCommand).use(revenueCommand).use(warehouseCommand).use(selfDrivingCommand).use(slackCommand).use(uploadSourcemapsCommand).use(skillCommand).init();
6119
6273
  //#endregion
6120
6274
  export { STRIPE_SDKS as A, MANUAL_SDK_VARIANTS as C, fetchHealthIssues as D, getKindMeta as E, getContentBlocks$3 as O, MANUAL_SDK_INSTALL as S, SOURCE_MAPS_CONTEXT_KEYS as T, detectSelfDrivingIntegrationProjects as _, useStdoutDimensions as a, DISPLAY_NAME as b, runWizardHeadless as c, PROGRAM_REGISTRY as d, Program as f, PRICING_SHORT as g, PRICING_LONG as h, useKeyboardHintsContext as i, POSTHOG_SDKS as k, runWizardCI as l, NO_DEFAULT_LIMIT as m, useKeyBindings as n, ConfirmButton as o, getProgramConfig as p, KeyboardHintsProvider as r, PromptLabel as s, PickerMenu as t, runWizard as u, SELF_DRIVING_INTEGRATE_PATH_KEY as v, RUST_SDK_CRATE as w, GO_SDK_MODULE as x, AUTOMATABLE_VARIANTS as y };
6121
6275