@posthog/wizard 2.44.0 → 2.46.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 (121) hide show
  1. package/README.md +5 -1
  2. package/dist/{add-mcp-server-to-clients-D5qsqCkj.js → add-mcp-server-to-clients-Bv2CBSil.js} +53 -6
  3. package/dist/add-mcp-server-to-clients-Bv2CBSil.js.map +1 -0
  4. package/dist/{agent-interface-BvTxWiol.js → agent-interface-B8roB2UP.js} +1122 -157
  5. package/dist/agent-interface-B8roB2UP.js.map +1 -0
  6. package/dist/{agent-runner-BF6_f8Qh.js → agent-runner-CV3qh_2p.js} +298 -610
  7. package/dist/agent-runner-CV3qh_2p.js.map +1 -0
  8. package/dist/analytics-D7vUZgzf.js +2 -0
  9. package/dist/{analytics-DSlQuwOT.js → analytics-k76OWsl0.js} +20 -6
  10. package/dist/analytics-k76OWsl0.js.map +1 -0
  11. package/dist/{api-BXduJZrw.js → api-CmIWcW-U.js} +3 -3
  12. package/dist/{api-BXduJZrw.js.map → api-CmIWcW-U.js.map} +1 -1
  13. package/dist/bin.js +242 -1043
  14. package/dist/bin.js.map +1 -1
  15. package/dist/{ci-install-GirEJqu2.js → ci-install-CNEJxdHf.js} +4 -4
  16. package/dist/{ci-install-GirEJqu2.js.map → ci-install-CNEJxdHf.js.map} +1 -1
  17. package/dist/commandments-DSNrOeOC.js +42 -0
  18. package/dist/commandments-DSNrOeOC.js.map +1 -0
  19. package/dist/commandments-qTW7pwaT.js +2 -0
  20. package/dist/{debug-BOFxiVVs.js → debug-BwIo7nwn.js} +1 -1
  21. package/dist/{debug-DXfQFmcM.js → debug-C9HmIrwx.js} +36 -6
  22. package/dist/debug-C9HmIrwx.js.map +1 -0
  23. package/dist/{defaults-BWLIffh5.js → defaults-CtWMmC8s.js} +1 -1
  24. package/dist/{defaults-BWLIffh5.js.map → defaults-CtWMmC8s.js.map} +1 -1
  25. package/dist/{env-api-key-cjD4yfGT.js → env-api-key-C3LzH8Lr.js} +1 -1
  26. package/dist/{env-api-key-cjD4yfGT.js.map → env-api-key-C3LzH8Lr.js.map} +1 -1
  27. package/dist/{environment-F1yw21dG.js → environment-NJykOp33.js} +3 -3
  28. package/dist/{environment-F1yw21dG.js.map → environment-NJykOp33.js.map} +1 -1
  29. package/dist/{file-utils-D-7YfJ7A.js → file-utils-CLMcNIKi.js} +2 -2
  30. package/dist/{file-utils-D-7YfJ7A.js.map → file-utils-CLMcNIKi.js.map} +1 -1
  31. package/dist/file-watcher-CBDCNQVV.js +123 -0
  32. package/dist/file-watcher-CBDCNQVV.js.map +1 -0
  33. package/dist/{headless-ui-Dni0gPND.js → headless-ui-B3QKxbz8.js} +5 -4
  34. package/dist/headless-ui-B3QKxbz8.js.map +1 -0
  35. package/dist/{interactive-Bmi82rsO.js → interactive-CIJRex02.js} +2 -2
  36. package/dist/{interactive-Bmi82rsO.js.map → interactive-CIJRex02.js.map} +1 -1
  37. package/dist/{mcp-prompt-streaming-AtntLsJ8.js → mcp-prompt-streaming-D6OgW3lC.js} +3 -3
  38. package/dist/{mcp-prompt-streaming-AtntLsJ8.js.map → mcp-prompt-streaming-D6OgW3lC.js.map} +1 -1
  39. package/dist/{mcp-xKRDnrcq.js → mcp-qTjKHqXN.js} +3 -3
  40. package/dist/mcp-qTjKHqXN.js.map +1 -0
  41. package/dist/{non-interactive-BIHqcW8g.js → non-interactive-8Eypq_h-.js} +2 -2
  42. package/dist/{non-interactive-BIHqcW8g.js.map → non-interactive-8Eypq_h-.js.map} +1 -1
  43. package/dist/orchestrator-tools-DVXOV-kz.js +99 -0
  44. package/dist/orchestrator-tools-DVXOV-kz.js.map +1 -0
  45. package/dist/{package-json-CDs1ZVKu.js → package-json-DCuoye-H.js} +1 -1
  46. package/dist/{package-json-CDs1ZVKu.js.map → package-json-DCuoye-H.js.map} +1 -1
  47. package/dist/{package-manager-C6R_zpML.js → package-manager-CAD9Uklp.js} +2 -2
  48. package/dist/{package-manager-C6R_zpML.js.map → package-manager-CAD9Uklp.js.map} +1 -1
  49. package/dist/pi-C9iHv5uG.js +619 -0
  50. package/dist/pi-C9iHv5uG.js.map +1 -0
  51. package/dist/{playground-DwL36I2u.js → playground-C-9Q3ETa.js} +8 -9
  52. package/dist/{playground-DwL36I2u.js.map → playground-C-9Q3ETa.js.map} +1 -1
  53. package/dist/{posthog-DwKlq0HM.js → posthog-DZZbziJ1.js} +1 -1
  54. package/dist/{posthog-DwKlq0HM.js.map → posthog-DZZbziJ1.js.map} +1 -1
  55. package/dist/{posthog-integration-BhLKCFsI.js → posthog-integration-Do997QON.js} +2206 -112
  56. package/dist/posthog-integration-Do997QON.js.map +1 -0
  57. package/dist/{provisioning-jVZm2E-A.js → provisioning-DpqnGk3z.js} +3 -3
  58. package/dist/{provisioning-jVZm2E-A.js.map → provisioning-DpqnGk3z.js.map} +1 -1
  59. package/dist/queue-tools-CTUgbOU8.js +366 -0
  60. package/dist/queue-tools-CTUgbOU8.js.map +1 -0
  61. package/dist/{registry-DcoBCMQR.js → registry-DT58jEYr.js} +96 -18
  62. package/dist/registry-DT58jEYr.js.map +1 -0
  63. package/dist/{security-jxjVsOpd.js → security-CTigdAWm.js} +10 -8
  64. package/dist/security-CTigdAWm.js.map +1 -0
  65. package/dist/{setup-utils-BQCl28D8.js → setup-utils-Cb5uACEO.js} +31 -9
  66. package/dist/setup-utils-Cb5uACEO.js.map +1 -0
  67. package/dist/{start-tui-DgSKFMVh.js → start-tui-Cwc-PFma.js} +37 -92
  68. package/dist/start-tui-Cwc-PFma.js.map +1 -0
  69. package/dist/{steps-erepgD0V.js → steps-DyEKYM4I.js} +7 -7
  70. package/dist/{steps-erepgD0V.js.map → steps-DyEKYM4I.js.map} +1 -1
  71. package/dist/{store-C79GoHCQ.js → store-4sj-vWr5.js} +5 -6
  72. package/dist/store-4sj-vWr5.js.map +1 -0
  73. package/dist/{subagent-aDlD_Ltz.js → subagent-D8_yDgCB.js} +2 -2
  74. package/dist/{subagent-aDlD_Ltz.js.map → subagent-D8_yDgCB.js.map} +1 -1
  75. package/dist/task-D8Y8pkzd.js +304 -0
  76. package/dist/task-D8Y8pkzd.js.map +1 -0
  77. package/dist/{task-stream-0Pk0tPBN.js → task-stream-DQT3vjwS.js} +72 -7
  78. package/dist/task-stream-DQT3vjwS.js.map +1 -0
  79. package/dist/{tasks-ZkrluXCa.js → tasks-D2WKssA5.js} +2 -2
  80. package/dist/{tasks-ZkrluXCa.js.map → tasks-D2WKssA5.js.map} +1 -1
  81. package/dist/{telemetry-DZShaH-y.js → telemetry-CYr4HgS2.js} +3 -3
  82. package/dist/{telemetry-DZShaH-y.js.map → telemetry-CYr4HgS2.js.map} +1 -1
  83. package/dist/{terminal-CEhufbJ4.js → terminal-1RfyHQC2.js} +13 -13
  84. package/dist/{terminal-CEhufbJ4.js.map → terminal-1RfyHQC2.js.map} +1 -1
  85. package/dist/tools-DQCUOQZX.js +199 -0
  86. package/dist/tools-DQCUOQZX.js.map +1 -0
  87. package/dist/{wizard-abort-UCm4Pd5m.js → wizard-abort-D_-v1zvS.js} +1 -1
  88. package/dist/{wizard-abort-C14PzXZ8.js → wizard-abort-FuyIaI_0.js} +4 -4
  89. package/dist/{wizard-abort-C14PzXZ8.js.map → wizard-abort-FuyIaI_0.js.map} +1 -1
  90. package/dist/{wizard-session-C5lZPPEc.js → wizard-session-DWHe83uk.js} +1 -1
  91. package/dist/{wizard-session-BAZql11N.js → wizard-session-tp4Zdbwl.js} +1 -1
  92. package/dist/{wizard-session-BAZql11N.js.map → wizard-session-tp4Zdbwl.js.map} +1 -1
  93. package/dist/yara-hooks-6gNnPFPo.js +2 -0
  94. package/dist/{yara-hooks-CPH8N4yz.js → yara-hooks-DdYRRxfH.js} +82 -21
  95. package/dist/yara-hooks-DdYRRxfH.js.map +1 -0
  96. package/package.json +1 -1
  97. package/dist/add-mcp-server-to-clients-D5qsqCkj.js.map +0 -1
  98. package/dist/agent-interface-BvTxWiol.js.map +0 -1
  99. package/dist/agent-runner-BF6_f8Qh.js.map +0 -1
  100. package/dist/analytics-C7Xx6csw.js +0 -2
  101. package/dist/analytics-DSlQuwOT.js.map +0 -1
  102. package/dist/debug-DXfQFmcM.js.map +0 -1
  103. package/dist/headless-ui-Dni0gPND.js.map +0 -1
  104. package/dist/mcp-xKRDnrcq.js.map +0 -1
  105. package/dist/posthog-integration-BhLKCFsI.js.map +0 -1
  106. package/dist/registry-DcoBCMQR.js.map +0 -1
  107. package/dist/security-jxjVsOpd.js.map +0 -1
  108. package/dist/setup-utils-BQCl28D8.js.map +0 -1
  109. package/dist/start-tui-DgSKFMVh.js.map +0 -1
  110. package/dist/store-C79GoHCQ.js.map +0 -1
  111. package/dist/task-stream-0Pk0tPBN.js.map +0 -1
  112. package/dist/tools-BIPMan6l.js +0 -122
  113. package/dist/tools-BIPMan6l.js.map +0 -1
  114. package/dist/types-y3HkjtSq.js +0 -46
  115. package/dist/types-y3HkjtSq.js.map +0 -1
  116. package/dist/wizard-tools-B7SLjCic.js +0 -1106
  117. package/dist/wizard-tools-B7SLjCic.js.map +0 -1
  118. package/dist/wizard-ui-WZ48rUgr.js +0 -15
  119. package/dist/wizard-ui-WZ48rUgr.js.map +0 -1
  120. package/dist/yara-hooks-CPH8N4yz.js.map +0 -1
  121. package/dist/yara-hooks-VZlj4Lib.js +0 -2
@@ -1,18 +1,19 @@
1
- import { L as Integration, P as HAIKU_MODEL, T as DETECTION_TIMEOUT_MS, _ as evaluateWizardReadiness, f as getUI, g as SIGNUP_WIZARD_READINESS_CONFIG, pt as getSkillsBaseUrl, rt as WIZARD_INTERACTION_EVENT_NAME, s as logToFile, v as getBlockingServiceKeys } from "./debug-DXfQFmcM.js";
2
- import { n as analytics } from "./analytics-DSlQuwOT.js";
3
- import { i as withUtm, n as openTrackedLink } from "./telemetry-DZShaH-y.js";
4
- import { i as tryGetPackageJson, n as isUsingTypeScript } from "./setup-utils-BQCl28D8.js";
5
- import { r as requestDeepLink } from "./provisioning-jVZm2E-A.js";
6
- import "./wizard-session-BAZql11N.js";
7
- import { i as wizardAbort } from "./wizard-abort-C14PzXZ8.js";
8
- import { n as runAgent } from "./agent-runner-BF6_f8Qh.js";
9
- import { i as runAgent$1, p as AgentSignals, r as initializeAgent } from "./agent-interface-BvTxWiol.js";
10
- import { t as WIZARD_TOOL_NAMES } from "./wizard-tools-B7SLjCic.js";
11
- import { r as detectNodePackageManagers } from "./package-manager-C6R_zpML.js";
12
- import { i as SPINNER_MESSAGE, t as FRAMEWORK_REGISTRY } from "./registry-DcoBCMQR.js";
1
+ import { B as Integration, L as HAIKU_MODEL, O as DETECTION_TIMEOUT_MS, _ as evaluateWizardReadiness, f as getUI, g as SIGNUP_WIZARD_READINESS_CONFIG, s as logToFile, st as WIZARD_INTERACTION_EVENT_NAME, v as getBlockingServiceKeys, vt as getSkillsBaseUrl, x as AGENTIC_DETECTION_TIMEOUT_MS } from "./debug-C9HmIrwx.js";
2
+ import { n as analytics } from "./analytics-k76OWsl0.js";
3
+ import { i as withUtm, n as openTrackedLink } from "./telemetry-CYr4HgS2.js";
4
+ import { i as tryGetPackageJson, n as isUsingTypeScript } from "./setup-utils-Cb5uACEO.js";
5
+ import { r as requestDeepLink } from "./provisioning-DpqnGk3z.js";
6
+ import "./wizard-session-tp4Zdbwl.js";
7
+ import { i as wizardAbort } from "./wizard-abort-FuyIaI_0.js";
8
+ import { n as runAgent, o as authenticate } from "./agent-runner-CV3qh_2p.js";
9
+ import { i as runAgent$1, p as AgentSignals, r as initializeAgent, v as WIZARD_TOOL_NAMES } from "./agent-interface-B8roB2UP.js";
10
+ import { m as EVENT_PLAN_FILE } from "./yara-hooks-DdYRRxfH.js";
11
+ import { r as detectNodePackageManagers } from "./package-manager-CAD9Uklp.js";
12
+ import { i as SPINNER_MESSAGE, t as FRAMEWORK_REGISTRY } from "./registry-DT58jEYr.js";
13
+ import { n as safeReadFile, r as walkProjectFiles } from "./file-utils-CLMcNIKi.js";
13
14
  import * as semver from "semver";
14
- import { readFileSync } from "fs";
15
- import { isAbsolute, join } from "path";
15
+ import { existsSync, readFileSync, statSync } from "fs";
16
+ import { isAbsolute, join, resolve, sep } from "path";
16
17
  import { Text } from "ink";
17
18
  import { useEffect, useMemo, useRef, useState } from "react";
18
19
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -263,8 +264,9 @@ const PROJECT_MANIFESTS = [
263
264
  function manifestGlob() {
264
265
  return `**/{${PROJECT_MANIFESTS.join(",")}}`;
265
266
  }
266
- function buildPrompt(cwd, targets, purpose) {
267
+ function buildPrompt(cwd, targets, purpose, recommend) {
267
268
  const targetList = targets.map((t) => `- ${t.id} → ${t.name}`).join("\n");
269
+ const projectShape = `{"path":string,"framework":string,"targetId":string|null,"hasPostHog":boolean${recommend ? ",\"recommended\":boolean" : ""}}`;
268
270
  return [
269
271
  `You are scanning a code repository to ${purpose}. Be fast and mechanical — do not over-explore. Keep any reasoning to one short sentence, then run the tool calls and output the JSON.`,
270
272
  "",
@@ -280,16 +282,18 @@ function buildPrompt(cwd, targets, purpose) {
280
282
  " - the matching target id from the list below. That list is ordered by priority — most specific first — so when a project could match more than one target (e.g. it uses several of the listed technologies), pick the one listed EARLIEST. Use null only if none matches,",
281
283
  ` - hasPostHog: true if any dependency is a PostHog SDK. This includes: a name containing "posthog" in any ecosystem (e.g. posthog-js, posthog-node, @posthog/*, posthog for pip/gem/hex, a com.posthog:* gradle/maven coordinate, a PostHog NuGet PackageReference); an SPM package named "PostHog" or a repositoryURL of github.com/PostHog/posthog-ios (in Package.swift or a .pbxproj); or a "pod 'PostHog'" line in a Podfile. Else false.`,
282
284
  " Do NOT read any file other than these manifests.",
285
+ ...recommend ? ["4. Pick exactly ONE project as recommended — the main user-facing client application. Prefer a frontend web app or a mobile app over backend servers/APIs, libraries, CLIs, tooling, and example/demo/docs apps. If several client apps exist, pick the primary production app; if no client app exists, pick the primary application project."] : [],
283
286
  "",
284
287
  "Target ids (id → name), in priority order — most specific first; if several match a project, keep the one listed earliest:",
285
288
  targetList,
286
289
  "",
287
290
  "Output requirements:",
288
291
  "- Respond with ONLY a single JSON object. No prose, no markdown code fences.",
289
- "- Shape: {\"repoType\":\"monorepo\"|\"single\",\"projects\":[{\"path\":string,\"framework\":string,\"targetId\":string|null,\"hasPostHog\":boolean}]}",
292
+ `- Shape: {"repoType":"monorepo"|"single","projects":[${projectShape}]}`,
290
293
  "- \"path\" is the project directory relative to the working directory; use \".\" for the repo root.",
291
294
  "- \"targetId\" MUST be exactly one of the target ids above when it matches; if several match, use the one listed earliest; otherwise null.",
292
295
  "- Include projects whose stack matches no target too (targetId: null).",
296
+ ...recommend ? ["- Exactly one project has \"recommended\": true; every other project has \"recommended\": false."] : [],
293
297
  `- If there are no manifests at all, respond with exactly: ${AgentSignals.ABORT} detection failed`
294
298
  ].join("\n");
295
299
  }
@@ -311,107 +315,2125 @@ function coercePath(raw) {
311
315
  if (isAbsolute(raw) || raw.split(/[\\/]/).includes("..")) return ".";
312
316
  return raw;
313
317
  }
318
+ /** Resolve an agent-reported path to an absolute dir clamped to the root — the caller-side counterpart of coercePath. */
319
+ function resolveProjectDir(installDir, rel) {
320
+ if (typeof rel !== "string" || rel === ".") return installDir;
321
+ const root = resolve(installDir);
322
+ const dir = resolve(root, rel);
323
+ return dir === root || dir.startsWith(root + sep) ? dir : root;
324
+ }
325
+ /**
326
+ * Validate the agent's raw JSON into a report, clamping each targetId to the
327
+ * supplied set and each path to a repo-relative dir. Exported for testing.
328
+ * `recommended` is stripped unless requested; at most one (the first) survives.
329
+ */
330
+ function coerceAgenticReport(parsed, validTargetIds, options) {
331
+ const recommend = options?.recommend === true;
332
+ const obj = parsed ?? {};
333
+ const repoType = obj.repoType === "monorepo" ? "monorepo" : "single";
334
+ const rawProjects = Array.isArray(obj.projects) ? obj.projects : [];
335
+ let recommendedSeen = false;
336
+ return {
337
+ repoType,
338
+ projects: rawProjects.map((raw) => {
339
+ const p = raw ?? {};
340
+ const targetId = typeof p.targetId === "string" && validTargetIds.includes(p.targetId) ? p.targetId : null;
341
+ const recommended = recommend && !recommendedSeen && p.recommended === true;
342
+ recommendedSeen ||= recommended;
343
+ return {
344
+ path: coercePath(p.path),
345
+ framework: typeof p.framework === "string" ? p.framework : "Unknown",
346
+ targetId,
347
+ hasPostHog: p.hasPostHog === true,
348
+ ...recommend ? { recommended } : {}
349
+ };
350
+ })
351
+ };
352
+ }
353
+ function formatToolUse(block) {
354
+ const name = typeof block?.name === "string" ? block.name : "tool";
355
+ const input = block?.input ?? {};
356
+ const detail = input.file_path || input.pattern || input.path || "";
357
+ return detail ? `${name} ${detail}` : name;
358
+ }
359
+ function sessionToWizardOptions(session) {
360
+ return {
361
+ installDir: session.installDir,
362
+ ci: session.ci,
363
+ debug: session.debug,
364
+ default: false,
365
+ benchmark: session.benchmark,
366
+ yaraReport: session.yaraReport,
367
+ signup: session.signup,
368
+ apiKey: session.apiKey,
369
+ projectId: session.projectId,
370
+ localMcp: session.localMcp
371
+ };
372
+ }
373
+ const NOOP_SPINNER = {
374
+ start: () => void 0,
375
+ stop: () => void 0,
376
+ message: () => void 0
377
+ };
378
+ /**
379
+ * Drive the wizard's agent loop on HAIKU_MODEL to scan the repo and return a
380
+ * structured detection report. Reuses the same setup every program uses, so
381
+ * MCP, tools, and credentials are wired identically.
382
+ */
383
+ async function detectProjectsWithAgent(session, options) {
384
+ if (!session.credentials) throw new Error("Detection requires authenticated credentials.");
385
+ const { targets, purpose = "set up a PostHog integration", recommend = false, onEvent } = options;
386
+ const { accessToken, host } = session.credentials;
387
+ const cwd = session.installDir;
388
+ const runOptions = sessionToWizardOptions(session);
389
+ const agent = await initializeAgent({
390
+ workingDirectory: cwd,
391
+ posthogMcpUrl: host.mcpUrl,
392
+ posthogApiKey: accessToken,
393
+ host,
394
+ detectPackageManager: detectNodePackageManagers,
395
+ skillsBaseUrl: getSkillsBaseUrl(session.localMcp),
396
+ integrationLabel: "agentic-detect",
397
+ allowedTools: [
398
+ "Read",
399
+ "Grep",
400
+ "Glob"
401
+ ],
402
+ modelOverride: HAIKU_MODEL
403
+ }, runOptions);
404
+ const collected = [];
405
+ let resultText = "";
406
+ const result = await runAgent$1(agent, buildPrompt(cwd, targets, purpose, recommend), runOptions, NOOP_SPINNER, {
407
+ spinnerMessage: "Scanning the repo...",
408
+ successMessage: "Detection complete",
409
+ errorMessage: "Detection failed",
410
+ requestRemark: false
411
+ }, {
412
+ onMessage: (message) => {
413
+ if (message?.type === "assistant") {
414
+ for (const block of message.message?.content ?? []) if (block?.type === "text" && typeof block.text === "string") {
415
+ collected.push(block.text);
416
+ const line = block.text.trim();
417
+ if (line && onEvent) onEvent(line.length > 100 ? `${line.slice(0, 100)}…` : line);
418
+ } else if (block?.type === "tool_use") onEvent?.(formatToolUse(block));
419
+ } else if (message?.type === "result" && typeof message.result === "string") resultText = message.result;
420
+ },
421
+ finalize: (_resultMessage, _durationMs) => void 0
422
+ });
423
+ if (result.error) throw new Error(result.message || `Agent error: ${result.error}`);
424
+ const output = resultText || collected.join("\n");
425
+ try {
426
+ return coerceAgenticReport(extractJson(output), targets.map((t) => t.id), { recommend });
427
+ } catch (err) {
428
+ if (output.includes(AgentSignals.ABORT)) return {
429
+ repoType: "single",
430
+ projects: []
431
+ };
432
+ throw err;
433
+ }
434
+ }
435
+ //#endregion
436
+ //#region src/lib/detection/project-scope.ts
437
+ /** Non-interactive project scoping — scan the repo agentically and pick which project the run integrates. */
438
+ /** Integration framework targets for the agentic detector (id → display name). */
439
+ const INTEGRATION_TARGETS = Object.entries(FRAMEWORK_REGISTRY).map(([id, config]) => ({
440
+ id,
441
+ name: config.metadata.name
442
+ }));
443
+ /** Run the agentic detector for the wizard's integration frameworks — the single home of targets + purpose. */
444
+ async function detectIntegrationProjects(session, options = {}) {
445
+ return detectProjectsWithAgent(session, {
446
+ ...options,
447
+ targets: INTEGRATION_TARGETS,
448
+ purpose: "set up a PostHog SDK integration"
449
+ });
450
+ }
451
+ /** Pick the recommended-if-supported project (even with PostHog — the main app wins), else the first supported PostHog-free one. */
452
+ function chooseIntegrationProject(projects) {
453
+ return projects.find((p) => p.recommended === true && p.targetId != null) ?? projects.find((p) => p.targetId != null && !p.hasPostHog);
454
+ }
455
+ /** Sentinel for a scan that outran AGENTIC_DETECTION_TIMEOUT_MS. */
456
+ const TIMED_OUT = Symbol("timed-out");
457
+ function captureOutcome(outcome, properties = {}) {
458
+ analytics.wizardCapture("agentic detection", {
459
+ outcome,
460
+ ...properties
461
+ });
462
+ }
463
+ /** Flag-gated non-interactive monorepo phase: scan, auto-choose the recommended project, re-point session.installDir; every failure leaves the session untouched. */
464
+ async function scopeInstallDirToProject(session) {
465
+ await authenticate(session, "posthog-integration");
466
+ if ((await analytics.getAllFlagsForWizard())["wizard-basic-integration-agentic-detection"] !== "true") {
467
+ captureOutcome("flag-off");
468
+ return;
469
+ }
470
+ getUI().log.info("Scanning the repo for projects...");
471
+ const startedAt = Date.now();
472
+ let report;
473
+ try {
474
+ report = await Promise.race([detectIntegrationProjects(session, {
475
+ recommend: true,
476
+ onEvent: (line) => logToFile("[agentic detect]", line)
477
+ }), new Promise((resolve) => setTimeout(() => resolve(TIMED_OUT), AGENTIC_DETECTION_TIMEOUT_MS))]);
478
+ } catch (err) {
479
+ const error = err instanceof Error ? err : new Error(String(err));
480
+ analytics.captureException(error, { step: "agentic_detection" });
481
+ captureOutcome("error", {
482
+ duration_ms: Date.now() - startedAt,
483
+ error_message: error.message
484
+ });
485
+ getUI().log.warn(`Project scan failed (${error.message}); continuing with the install dir as-is.`);
486
+ return;
487
+ }
488
+ if (report === TIMED_OUT) {
489
+ captureOutcome("timeout", { duration_ms: Date.now() - startedAt });
490
+ getUI().log.warn(`Project scan timed out after ${AGENTIC_DETECTION_TIMEOUT_MS / 1e3}s; continuing with the install dir as-is.`);
491
+ return;
492
+ }
493
+ const { projects } = report;
494
+ const recommended = projects.find((p) => p.recommended === true);
495
+ const scanProperties = {
496
+ duration_ms: Date.now() - startedAt,
497
+ repo_type: report.repoType,
498
+ project_count: projects.length,
499
+ supported_count: projects.filter((p) => p.targetId != null).length,
500
+ has_recommendation: recommended !== void 0,
501
+ recommended_path: recommended?.path ?? null,
502
+ projects
503
+ };
504
+ const project = chooseIntegrationProject(projects);
505
+ if (!project) {
506
+ captureOutcome("no-project", scanProperties);
507
+ getUI().log.info("The scan found no supported project; continuing with the install dir as-is.");
508
+ return;
509
+ }
510
+ session.installDir = resolveProjectDir(session.installDir, project.path);
511
+ captureOutcome(project.recommended ? "recommended" : "first-instrumentable", {
512
+ ...scanProperties,
513
+ chosen_framework: project.targetId,
514
+ chosen_path: project.path
515
+ });
516
+ getUI().log.info(`Continuing with ${project.path} (${project.framework}).`);
517
+ }
518
+ //#endregion
519
+ //#region src/lib/warehouse-sources/registry.ts
520
+ const SOURCE_DETECTORS = [
521
+ {
522
+ kind: "Postgres",
523
+ label: "PostgreSQL",
524
+ mode: "in-cli",
525
+ signals: {
526
+ npm: [
527
+ "pg",
528
+ "postgres",
529
+ "postgres.js",
530
+ "knex",
531
+ "sequelize"
532
+ ],
533
+ python: [
534
+ "psycopg",
535
+ "psycopg2",
536
+ "psycopg2-binary",
537
+ "asyncpg"
538
+ ],
539
+ ruby: ["pg"],
540
+ envKeys: [
541
+ /^DATABASE_URL$/,
542
+ /^POSTGRES_/,
543
+ /^PG(HOST|DATABASE|USER|PORT)$/
544
+ ]
545
+ }
546
+ },
547
+ {
548
+ kind: "MySQL",
549
+ label: "MySQL",
550
+ mode: "in-cli",
551
+ signals: {
552
+ npm: ["mysql", "mysql2"],
553
+ python: [
554
+ "pymysql",
555
+ "mysqlclient",
556
+ "mysql-connector-python"
557
+ ],
558
+ ruby: ["mysql2"],
559
+ envKeys: [/^MYSQL_/]
560
+ }
561
+ },
562
+ {
563
+ kind: "MongoDB",
564
+ label: "MongoDB",
565
+ mode: "in-cli",
566
+ signals: {
567
+ npm: ["mongodb", "mongoose"],
568
+ python: ["pymongo", "motor"],
569
+ ruby: ["mongo", "mongoid"],
570
+ envKeys: [/^MONGO(DB)?_/]
571
+ }
572
+ },
573
+ {
574
+ kind: "Snowflake",
575
+ label: "Snowflake",
576
+ mode: "in-cli",
577
+ signals: {
578
+ npm: ["snowflake-sdk"],
579
+ python: ["snowflake-connector-python", "snowflake-sqlalchemy"],
580
+ envKeys: [/^SNOWFLAKE_/]
581
+ }
582
+ },
583
+ {
584
+ kind: "BigQuery",
585
+ label: "BigQuery",
586
+ mode: "in-cli",
587
+ signals: {
588
+ npm: ["@google-cloud/bigquery"],
589
+ python: ["google-cloud-bigquery"],
590
+ envKeys: [/^BIGQUERY_/]
591
+ }
592
+ },
593
+ {
594
+ kind: "Redshift",
595
+ label: "Redshift",
596
+ mode: "in-cli",
597
+ signals: {
598
+ npm: ["node-redshift"],
599
+ python: ["redshift-connector"],
600
+ envKeys: [/^REDSHIFT_/]
601
+ }
602
+ },
603
+ {
604
+ kind: "MSSQL",
605
+ label: "SQL Server",
606
+ mode: "in-cli",
607
+ signals: {
608
+ npm: ["mssql", "tedious"],
609
+ python: ["pyodbc", "pymssql"],
610
+ envKeys: [/^MSSQL_/]
611
+ }
612
+ },
613
+ {
614
+ kind: "Supabase",
615
+ label: "Supabase",
616
+ mode: "in-cli",
617
+ signals: {
618
+ npm: ["@supabase/supabase-js"],
619
+ python: ["supabase"],
620
+ envKeys: [/^SUPABASE_/]
621
+ }
622
+ },
623
+ {
624
+ kind: "ClickHouse",
625
+ label: "ClickHouse",
626
+ mode: "in-cli",
627
+ signals: {
628
+ npm: ["@clickhouse/client"],
629
+ python: ["clickhouse-connect", "clickhouse-driver"],
630
+ envKeys: [/^CLICKHOUSE_/]
631
+ }
632
+ },
633
+ {
634
+ kind: "Convex",
635
+ label: "Convex",
636
+ mode: "in-cli",
637
+ signals: {
638
+ npm: ["convex"],
639
+ python: ["convex"],
640
+ envKeys: [/^CONVEX_/, /^NEXT_PUBLIC_CONVEX_URL$/]
641
+ }
642
+ },
643
+ {
644
+ kind: "Stripe",
645
+ label: "Stripe",
646
+ mode: "in-cli",
647
+ signals: {
648
+ npm: [
649
+ "stripe",
650
+ "@stripe/stripe-js",
651
+ "@stripe/react-stripe-js"
652
+ ],
653
+ python: ["stripe"],
654
+ ruby: ["stripe"],
655
+ envKeys: [/^STRIPE_(SECRET|API)_KEY$/]
656
+ }
657
+ },
658
+ {
659
+ kind: "Clerk",
660
+ label: "Clerk",
661
+ mode: "in-cli",
662
+ signals: {
663
+ npm: [
664
+ "@clerk/nextjs",
665
+ "@clerk/clerk-react",
666
+ "@clerk/backend",
667
+ "@clerk/express",
668
+ "@clerk/fastify",
669
+ "@clerk/remix"
670
+ ],
671
+ envKeys: [/^CLERK_SECRET_KEY$/, /^NEXT_PUBLIC_CLERK_/]
672
+ }
673
+ },
674
+ {
675
+ kind: "Resend",
676
+ label: "Resend",
677
+ mode: "in-cli",
678
+ signals: {
679
+ npm: ["resend"],
680
+ python: ["resend"],
681
+ envKeys: [/^RESEND_API_KEY$/]
682
+ }
683
+ },
684
+ {
685
+ kind: "Shopify",
686
+ label: "Shopify",
687
+ mode: "in-cli",
688
+ signals: {
689
+ npm: ["@shopify/shopify-api", "shopify-api-node"],
690
+ python: ["shopifyapi"],
691
+ envKeys: [/^SHOPIFY_/]
692
+ }
693
+ },
694
+ {
695
+ kind: "Klaviyo",
696
+ label: "Klaviyo",
697
+ mode: "in-cli",
698
+ signals: {
699
+ npm: ["klaviyo-api"],
700
+ python: ["klaviyo-api"],
701
+ envKeys: [/^KLAVIYO_/]
702
+ }
703
+ },
704
+ {
705
+ kind: "Chargebee",
706
+ label: "Chargebee",
707
+ mode: "in-cli",
708
+ signals: {
709
+ npm: ["chargebee"],
710
+ python: ["chargebee"],
711
+ envKeys: [/^CHARGEBEE_/]
712
+ }
713
+ },
714
+ {
715
+ kind: "Paddle",
716
+ label: "Paddle",
717
+ mode: "in-cli",
718
+ signals: {
719
+ npm: ["@paddle/paddle-node-sdk", "@paddle/paddle-js"],
720
+ envKeys: [/^PADDLE_/]
721
+ }
722
+ },
723
+ {
724
+ kind: "Polar",
725
+ label: "Polar",
726
+ mode: "in-cli",
727
+ signals: {
728
+ npm: ["@polar-sh/sdk", "@polar-sh/nextjs"],
729
+ envKeys: [/^POLAR_/]
730
+ }
731
+ },
732
+ {
733
+ kind: "Mailchimp",
734
+ label: "Mailchimp",
735
+ mode: "in-cli",
736
+ signals: {
737
+ npm: ["@mailchimp/mailchimp_marketing"],
738
+ python: ["mailchimp-marketing"],
739
+ envKeys: [/^MAILCHIMP_/]
740
+ }
741
+ },
742
+ {
743
+ kind: "CustomerIO",
744
+ label: "Customer.io",
745
+ mode: "in-cli",
746
+ signals: {
747
+ npm: ["customerio-node"],
748
+ python: ["customerio"],
749
+ envKeys: [/^CUSTOMER_?IO_/]
750
+ }
751
+ },
752
+ {
753
+ kind: "Typeform",
754
+ label: "Typeform",
755
+ mode: "in-cli",
756
+ signals: {
757
+ npm: ["@typeform/api-client"],
758
+ envKeys: [/^TYPEFORM_/]
759
+ }
760
+ },
761
+ {
762
+ kind: "Sentry",
763
+ label: "Sentry",
764
+ mode: "in-cli",
765
+ signals: {
766
+ npm: [
767
+ "@sentry/node",
768
+ "@sentry/browser",
769
+ "@sentry/react",
770
+ "@sentry/nextjs"
771
+ ],
772
+ python: ["sentry-sdk"],
773
+ ruby: ["sentry-ruby"]
774
+ }
775
+ },
776
+ {
777
+ kind: "Plaid",
778
+ label: "Plaid",
779
+ mode: "in-cli",
780
+ signals: {
781
+ npm: ["plaid"],
782
+ python: ["plaid-python", "plaid"],
783
+ ruby: ["plaid"],
784
+ envKeys: [/^PLAID_/]
785
+ }
786
+ },
787
+ {
788
+ kind: "Braintree",
789
+ label: "Braintree",
790
+ mode: "in-cli",
791
+ signals: {
792
+ npm: ["braintree"],
793
+ python: ["braintree"],
794
+ ruby: ["braintree"],
795
+ envKeys: [/^BRAINTREE_/]
796
+ }
797
+ },
798
+ {
799
+ kind: "Square",
800
+ label: "Square",
801
+ mode: "in-cli",
802
+ signals: {
803
+ npm: ["square"],
804
+ python: ["squareup"],
805
+ ruby: ["square"],
806
+ envKeys: [/^SQUARE_/]
807
+ }
808
+ },
809
+ {
810
+ kind: "GoCardless",
811
+ label: "GoCardless",
812
+ mode: "in-cli",
813
+ signals: {
814
+ npm: ["gocardless-nodejs"],
815
+ python: ["gocardless-pro"],
816
+ ruby: ["gocardless_pro"],
817
+ envKeys: [/^GOCARDLESS_/]
818
+ }
819
+ },
820
+ {
821
+ kind: "Mollie",
822
+ label: "Mollie",
823
+ mode: "in-cli",
824
+ signals: {
825
+ npm: ["@mollie/api-client"],
826
+ python: ["mollie-api-python"],
827
+ envKeys: [/^MOLLIE_/]
828
+ }
829
+ },
830
+ {
831
+ kind: "CheckoutCom",
832
+ label: "Checkout.com",
833
+ mode: "in-cli",
834
+ signals: {
835
+ npm: ["checkout-sdk-node"],
836
+ python: ["checkout-sdk"],
837
+ envKeys: [/^CKO_/, /^CHECKOUT_COM_/]
838
+ }
839
+ },
840
+ {
841
+ kind: "Recurly",
842
+ label: "Recurly",
843
+ mode: "in-cli",
844
+ signals: {
845
+ npm: ["recurly"],
846
+ python: ["recurly"],
847
+ ruby: ["recurly"],
848
+ envKeys: [/^RECURLY_/]
849
+ }
850
+ },
851
+ {
852
+ kind: "RevenueCat",
853
+ label: "RevenueCat",
854
+ mode: "in-cli",
855
+ signals: {
856
+ npm: [
857
+ "@revenuecat/purchases-js",
858
+ "react-native-purchases",
859
+ "@revenuecat/purchases-capacitor"
860
+ ],
861
+ envKeys: [/^REVENUE_?CAT_/]
862
+ }
863
+ },
864
+ {
865
+ kind: "Twilio",
866
+ label: "Twilio",
867
+ mode: "in-cli",
868
+ signals: {
869
+ npm: ["twilio"],
870
+ python: ["twilio"],
871
+ ruby: ["twilio-ruby"],
872
+ envKeys: [/^TWILIO_/]
873
+ }
874
+ },
875
+ {
876
+ kind: "SendGrid",
877
+ label: "SendGrid",
878
+ mode: "in-cli",
879
+ signals: {
880
+ npm: ["@sendgrid/mail", "@sendgrid/client"],
881
+ python: ["sendgrid"],
882
+ ruby: ["sendgrid-ruby"],
883
+ envKeys: [/^SENDGRID_/]
884
+ }
885
+ },
886
+ {
887
+ kind: "Mailgun",
888
+ label: "Mailgun",
889
+ mode: "in-cli",
890
+ signals: {
891
+ npm: ["mailgun.js", "mailgun-js"],
892
+ python: ["mailgun"],
893
+ ruby: ["mailgun-ruby"],
894
+ envKeys: [/^MAILGUN_/]
895
+ }
896
+ },
897
+ {
898
+ kind: "Postmark",
899
+ label: "Postmark",
900
+ mode: "in-cli",
901
+ signals: {
902
+ npm: ["postmark"],
903
+ python: ["postmarker"],
904
+ ruby: ["postmark"],
905
+ envKeys: [/^POSTMARK_/]
906
+ }
907
+ },
908
+ {
909
+ kind: "Brevo",
910
+ label: "Brevo",
911
+ mode: "in-cli",
912
+ signals: {
913
+ npm: ["@getbrevo/brevo", "sib-api-v3-sdk"],
914
+ python: ["sib-api-v3-sdk"],
915
+ envKeys: [/^BREVO_/]
916
+ }
917
+ },
918
+ {
919
+ kind: "MailerLite",
920
+ label: "MailerLite",
921
+ mode: "in-cli",
922
+ signals: {
923
+ npm: ["@mailerlite/mailerlite-nodejs"],
924
+ python: ["mailerlite"],
925
+ envKeys: [/^MAILERLITE_/]
926
+ }
927
+ },
928
+ {
929
+ kind: "Mailjet",
930
+ label: "Mailjet",
931
+ mode: "in-cli",
932
+ signals: {
933
+ npm: ["node-mailjet"],
934
+ python: ["mailjet-rest"],
935
+ envKeys: [/^MAILJET_/, /^MJ_APIKEY_/]
936
+ }
937
+ },
938
+ {
939
+ kind: "LaunchDarkly",
940
+ label: "LaunchDarkly",
941
+ mode: "in-cli",
942
+ signals: {
943
+ npm: [
944
+ "launchdarkly-node-server-sdk",
945
+ "@launchdarkly/node-server-sdk",
946
+ "launchdarkly-js-client-sdk",
947
+ "launchdarkly-react-client-sdk"
948
+ ],
949
+ python: ["launchdarkly-server-sdk"],
950
+ ruby: ["launchdarkly-server-sdk"],
951
+ envKeys: [/^LAUNCHDARKLY_/, /^LD_SDK_KEY$/]
952
+ }
953
+ },
954
+ {
955
+ kind: "Optimizely",
956
+ label: "Optimizely",
957
+ mode: "in-cli",
958
+ signals: {
959
+ npm: ["@optimizely/optimizely-sdk"],
960
+ python: ["optimizely-sdk"],
961
+ envKeys: [/^OPTIMIZELY_/]
962
+ }
963
+ },
964
+ {
965
+ kind: "Braze",
966
+ label: "Braze",
967
+ mode: "in-cli",
968
+ signals: {
969
+ npm: ["@braze/web-sdk", "@braze/react-native-sdk"],
970
+ python: ["braze-client"],
971
+ envKeys: [/^BRAZE_/]
972
+ }
973
+ },
974
+ {
975
+ kind: "Rollbar",
976
+ label: "Rollbar",
977
+ mode: "in-cli",
978
+ signals: {
979
+ npm: ["rollbar"],
980
+ python: ["rollbar", "pyrollbar"],
981
+ ruby: ["rollbar"],
982
+ envKeys: [/^ROLLBAR_/]
983
+ }
984
+ },
985
+ {
986
+ kind: "Okta",
987
+ label: "Okta",
988
+ mode: "in-cli",
989
+ signals: {
990
+ npm: [
991
+ "@okta/okta-sdk-nodejs",
992
+ "@okta/okta-auth-js",
993
+ "@okta/okta-react"
994
+ ],
995
+ python: ["okta"],
996
+ envKeys: [/^OKTA_/]
997
+ }
998
+ },
999
+ {
1000
+ kind: "WorkOS",
1001
+ label: "WorkOS",
1002
+ mode: "in-cli",
1003
+ signals: {
1004
+ npm: ["@workos-inc/node"],
1005
+ python: ["workos"],
1006
+ envKeys: [/^WORKOS_/]
1007
+ }
1008
+ },
1009
+ {
1010
+ kind: "Notion",
1011
+ label: "Notion",
1012
+ mode: "in-cli",
1013
+ signals: {
1014
+ npm: ["@notionhq/client"],
1015
+ python: ["notion-client"],
1016
+ envKeys: [/^NOTION_/]
1017
+ }
1018
+ },
1019
+ {
1020
+ kind: "FullStory",
1021
+ label: "FullStory",
1022
+ mode: "in-cli",
1023
+ signals: {
1024
+ npm: ["@fullstory/browser", "@fullstory/react-native"],
1025
+ envKeys: [/^FULLSTORY_/]
1026
+ }
1027
+ },
1028
+ {
1029
+ kind: "Amplitude",
1030
+ label: "Amplitude",
1031
+ mode: "in-cli",
1032
+ signals: {
1033
+ npm: [
1034
+ "@amplitude/analytics-browser",
1035
+ "@amplitude/analytics-node",
1036
+ "@amplitude/analytics-react-native",
1037
+ "amplitude-js"
1038
+ ],
1039
+ python: ["amplitude-analytics"],
1040
+ envKeys: [/^AMPLITUDE_/]
1041
+ }
1042
+ },
1043
+ {
1044
+ kind: "Mixpanel",
1045
+ label: "Mixpanel",
1046
+ mode: "in-cli",
1047
+ signals: {
1048
+ npm: ["mixpanel", "mixpanel-browser"],
1049
+ python: ["mixpanel"],
1050
+ ruby: ["mixpanel-ruby"],
1051
+ envKeys: [/^MIXPANEL_/]
1052
+ }
1053
+ },
1054
+ {
1055
+ kind: "Pendo",
1056
+ label: "Pendo",
1057
+ mode: "in-cli",
1058
+ signals: {
1059
+ npm: ["@pendo/agent"],
1060
+ envKeys: [/^PENDO_/]
1061
+ }
1062
+ },
1063
+ {
1064
+ kind: "Salesforce",
1065
+ label: "Salesforce",
1066
+ mode: "deep-link",
1067
+ signals: {
1068
+ npm: ["jsforce"],
1069
+ python: ["simple-salesforce"],
1070
+ envKeys: [/^SALESFORCE_/]
1071
+ }
1072
+ },
1073
+ {
1074
+ kind: "Hubspot",
1075
+ label: "HubSpot",
1076
+ mode: "deep-link",
1077
+ signals: {
1078
+ npm: ["@hubspot/api-client"],
1079
+ python: ["hubspot-api-client"],
1080
+ envKeys: [/^HUBSPOT_/]
1081
+ }
1082
+ },
1083
+ {
1084
+ kind: "Zendesk",
1085
+ label: "Zendesk",
1086
+ mode: "deep-link",
1087
+ signals: {
1088
+ npm: ["node-zendesk"],
1089
+ python: ["zenpy"],
1090
+ envKeys: [/^ZENDESK_/]
1091
+ }
1092
+ },
1093
+ {
1094
+ kind: "Intercom",
1095
+ label: "Intercom",
1096
+ mode: "deep-link",
1097
+ signals: {
1098
+ npm: ["intercom-client", "@intercom/messenger-js-sdk"],
1099
+ python: ["python-intercom"],
1100
+ envKeys: [/^INTERCOM_/]
1101
+ }
1102
+ },
1103
+ {
1104
+ kind: "Linear",
1105
+ label: "Linear",
1106
+ mode: "deep-link",
1107
+ signals: {
1108
+ npm: ["@linear/sdk"],
1109
+ envKeys: [/^LINEAR_API_KEY$/]
1110
+ }
1111
+ },
1112
+ {
1113
+ kind: "Slack",
1114
+ label: "Slack",
1115
+ mode: "deep-link",
1116
+ signals: {
1117
+ npm: ["@slack/web-api", "@slack/bolt"],
1118
+ python: ["slack-sdk", "slack-bolt"],
1119
+ envKeys: [/^SLACK_(BOT|APP|SIGNING|CLIENT)_/]
1120
+ }
1121
+ },
1122
+ {
1123
+ kind: "Github",
1124
+ label: "GitHub",
1125
+ mode: "deep-link",
1126
+ signals: {
1127
+ npm: [
1128
+ "@octokit/rest",
1129
+ "@octokit/core",
1130
+ "@octokit/graphql",
1131
+ "octokit"
1132
+ ],
1133
+ python: ["pygithub"],
1134
+ ruby: ["octokit"]
1135
+ }
1136
+ },
1137
+ {
1138
+ kind: "OpenAI",
1139
+ label: "OpenAI",
1140
+ mode: "in-cli",
1141
+ signals: {
1142
+ npm: ["openai"],
1143
+ python: ["openai"],
1144
+ envKeys: [/^OPENAI_/]
1145
+ }
1146
+ },
1147
+ {
1148
+ kind: "Anthropic",
1149
+ label: "Anthropic",
1150
+ mode: "in-cli",
1151
+ signals: {
1152
+ npm: ["@anthropic-ai/sdk"],
1153
+ python: ["anthropic"],
1154
+ envKeys: [/^ANTHROPIC_/]
1155
+ }
1156
+ },
1157
+ {
1158
+ kind: "Cohere",
1159
+ label: "Cohere",
1160
+ mode: "in-cli",
1161
+ signals: {
1162
+ npm: ["cohere-ai"],
1163
+ python: ["cohere"],
1164
+ envKeys: [/^COHERE_/]
1165
+ }
1166
+ },
1167
+ {
1168
+ kind: "MistralAI",
1169
+ label: "Mistral AI",
1170
+ mode: "in-cli",
1171
+ signals: {
1172
+ npm: ["@mistralai/mistralai"],
1173
+ python: ["mistralai"],
1174
+ envKeys: [/^MISTRAL_/]
1175
+ }
1176
+ },
1177
+ {
1178
+ kind: "Groq",
1179
+ label: "Groq",
1180
+ mode: "in-cli",
1181
+ signals: {
1182
+ npm: ["groq-sdk"],
1183
+ python: ["groq"],
1184
+ envKeys: [/^GROQ_/]
1185
+ }
1186
+ },
1187
+ {
1188
+ kind: "TogetherAI",
1189
+ label: "Together AI",
1190
+ mode: "in-cli",
1191
+ signals: {
1192
+ npm: ["together-ai"],
1193
+ python: ["together"],
1194
+ envKeys: [/^TOGETHER_/]
1195
+ }
1196
+ },
1197
+ {
1198
+ kind: "FireworksAI",
1199
+ label: "Fireworks AI",
1200
+ mode: "in-cli",
1201
+ signals: {
1202
+ python: ["fireworks-ai"],
1203
+ envKeys: [/^FIREWORKS_/]
1204
+ }
1205
+ },
1206
+ {
1207
+ kind: "Replicate",
1208
+ label: "Replicate",
1209
+ mode: "in-cli",
1210
+ signals: {
1211
+ npm: ["replicate"],
1212
+ python: ["replicate"],
1213
+ envKeys: [/^REPLICATE_/]
1214
+ }
1215
+ },
1216
+ {
1217
+ kind: "HuggingFace",
1218
+ label: "Hugging Face",
1219
+ mode: "in-cli",
1220
+ signals: {
1221
+ npm: ["@huggingface/inference"],
1222
+ python: ["huggingface-hub"],
1223
+ envKeys: [/^HUGGINGFACE_/, /^HF_TOKEN$/]
1224
+ }
1225
+ },
1226
+ {
1227
+ kind: "ElevenLabs",
1228
+ label: "ElevenLabs",
1229
+ mode: "in-cli",
1230
+ signals: {
1231
+ npm: ["@elevenlabs/elevenlabs-js", "elevenlabs"],
1232
+ python: ["elevenlabs"],
1233
+ envKeys: [/^ELEVENLABS_/, /^ELEVEN_LABS_/]
1234
+ }
1235
+ },
1236
+ {
1237
+ kind: "Deepgram",
1238
+ label: "Deepgram",
1239
+ mode: "in-cli",
1240
+ signals: {
1241
+ npm: ["@deepgram/sdk"],
1242
+ python: ["deepgram-sdk"],
1243
+ envKeys: [/^DEEPGRAM_/]
1244
+ }
1245
+ },
1246
+ {
1247
+ kind: "AssemblyAI",
1248
+ label: "AssemblyAI",
1249
+ mode: "in-cli",
1250
+ signals: {
1251
+ npm: ["assemblyai"],
1252
+ python: ["assemblyai"],
1253
+ envKeys: [/^ASSEMBLYAI_/]
1254
+ }
1255
+ },
1256
+ {
1257
+ kind: "Langfuse",
1258
+ label: "Langfuse",
1259
+ mode: "in-cli",
1260
+ signals: {
1261
+ npm: ["langfuse"],
1262
+ python: ["langfuse"],
1263
+ envKeys: [/^LANGFUSE_/]
1264
+ }
1265
+ },
1266
+ {
1267
+ kind: "LangSmith",
1268
+ label: "LangSmith",
1269
+ mode: "in-cli",
1270
+ signals: {
1271
+ npm: ["langsmith"],
1272
+ python: ["langsmith"],
1273
+ envKeys: [/^LANGSMITH_/, /^LANGCHAIN_API_KEY$/]
1274
+ }
1275
+ },
1276
+ {
1277
+ kind: "Firecrawl",
1278
+ label: "Firecrawl",
1279
+ mode: "in-cli",
1280
+ signals: {
1281
+ npm: ["@mendable/firecrawl-js"],
1282
+ python: ["firecrawl-py"],
1283
+ envKeys: [/^FIRECRAWL_/]
1284
+ }
1285
+ },
1286
+ {
1287
+ kind: "Mem0",
1288
+ label: "Mem0",
1289
+ mode: "in-cli",
1290
+ signals: {
1291
+ npm: ["mem0ai"],
1292
+ python: ["mem0ai"],
1293
+ envKeys: [/^MEM0_/]
1294
+ }
1295
+ },
1296
+ {
1297
+ kind: "Vellum",
1298
+ label: "Vellum",
1299
+ mode: "in-cli",
1300
+ signals: {
1301
+ npm: ["vellum-ai"],
1302
+ python: ["vellum-ai"],
1303
+ envKeys: [/^VELLUM_/]
1304
+ }
1305
+ },
1306
+ {
1307
+ kind: "Helicone",
1308
+ label: "Helicone",
1309
+ mode: "in-cli",
1310
+ signals: {
1311
+ npm: ["@helicone/helpers"],
1312
+ envKeys: [/^HELICONE_/]
1313
+ }
1314
+ },
1315
+ {
1316
+ kind: "OpenRouter",
1317
+ label: "OpenRouter",
1318
+ mode: "in-cli",
1319
+ signals: {
1320
+ npm: ["@openrouter/ai-sdk-provider"],
1321
+ envKeys: [/^OPENROUTER_/]
1322
+ }
1323
+ },
1324
+ {
1325
+ kind: "Paystack",
1326
+ label: "Paystack",
1327
+ mode: "in-cli",
1328
+ signals: {
1329
+ python: ["paystackapi"],
1330
+ envKeys: [/^PAYSTACK_/]
1331
+ }
1332
+ },
1333
+ {
1334
+ kind: "Orb",
1335
+ label: "Orb",
1336
+ mode: "in-cli",
1337
+ signals: {
1338
+ npm: ["orb-billing"],
1339
+ python: ["orb-billing"],
1340
+ envKeys: [/^ORB_/]
1341
+ }
1342
+ },
1343
+ {
1344
+ kind: "Lago",
1345
+ label: "Lago",
1346
+ mode: "in-cli",
1347
+ signals: {
1348
+ npm: ["lago-javascript-client"],
1349
+ python: ["lago-python-client"],
1350
+ envKeys: [/^LAGO_/]
1351
+ }
1352
+ },
1353
+ {
1354
+ kind: "Zuora",
1355
+ label: "Zuora",
1356
+ mode: "in-cli",
1357
+ signals: { envKeys: [/^ZUORA_/] }
1358
+ },
1359
+ {
1360
+ kind: "Stigg",
1361
+ label: "Stigg",
1362
+ mode: "in-cli",
1363
+ signals: {
1364
+ npm: ["@stigg/node-server-sdk"],
1365
+ envKeys: [/^STIGG_/]
1366
+ }
1367
+ },
1368
+ {
1369
+ kind: "Ramp",
1370
+ label: "Ramp",
1371
+ mode: "in-cli",
1372
+ signals: { envKeys: [/^RAMP_/] }
1373
+ },
1374
+ {
1375
+ kind: "Brex",
1376
+ label: "Brex",
1377
+ mode: "in-cli",
1378
+ signals: { envKeys: [/^BREX_/] }
1379
+ },
1380
+ {
1381
+ kind: "Tremendous",
1382
+ label: "Tremendous",
1383
+ mode: "in-cli",
1384
+ signals: { envKeys: [/^TREMENDOUS_/] }
1385
+ },
1386
+ {
1387
+ kind: "Maxio",
1388
+ label: "Maxio",
1389
+ mode: "in-cli",
1390
+ signals: { envKeys: [/^MAXIO_/] }
1391
+ },
1392
+ {
1393
+ kind: "Chargify",
1394
+ label: "Chargify",
1395
+ mode: "in-cli",
1396
+ signals: { envKeys: [/^CHARGIFY_/] }
1397
+ },
1398
+ {
1399
+ kind: "Invoiced",
1400
+ label: "Invoiced",
1401
+ mode: "in-cli",
1402
+ signals: {
1403
+ npm: ["invoiced"],
1404
+ python: ["invoiced"],
1405
+ envKeys: [/^INVOICED_/]
1406
+ }
1407
+ },
1408
+ {
1409
+ kind: "SparkPost",
1410
+ label: "SparkPost",
1411
+ mode: "in-cli",
1412
+ signals: {
1413
+ npm: ["sparkpost"],
1414
+ python: ["sparkpost"],
1415
+ envKeys: [/^SPARKPOST_/]
1416
+ }
1417
+ },
1418
+ {
1419
+ kind: "MailerSend",
1420
+ label: "MailerSend",
1421
+ mode: "in-cli",
1422
+ signals: {
1423
+ npm: ["mailersend"],
1424
+ python: ["mailersend"],
1425
+ envKeys: [/^MAILERSEND_/]
1426
+ }
1427
+ },
1428
+ {
1429
+ kind: "Iterable",
1430
+ label: "Iterable",
1431
+ mode: "in-cli",
1432
+ signals: { envKeys: [/^ITERABLE_/] }
1433
+ },
1434
+ {
1435
+ kind: "ConvertKit",
1436
+ label: "ConvertKit",
1437
+ mode: "in-cli",
1438
+ signals: { envKeys: [/^CONVERTKIT_/] }
1439
+ },
1440
+ {
1441
+ kind: "Drip",
1442
+ label: "Drip",
1443
+ mode: "in-cli",
1444
+ signals: {
1445
+ npm: ["drip-nodejs"],
1446
+ envKeys: [/^DRIP_/]
1447
+ }
1448
+ },
1449
+ {
1450
+ kind: "Omnisend",
1451
+ label: "Omnisend",
1452
+ mode: "in-cli",
1453
+ signals: { envKeys: [/^OMNISEND_/] }
1454
+ },
1455
+ {
1456
+ kind: "EmailOctopus",
1457
+ label: "EmailOctopus",
1458
+ mode: "in-cli",
1459
+ signals: { envKeys: [/^EMAILOCTOPUS_/, /^EMAIL_OCTOPUS_/] }
1460
+ },
1461
+ {
1462
+ kind: "Elasticemail",
1463
+ label: "Elastic Email",
1464
+ mode: "in-cli",
1465
+ signals: { envKeys: [/^ELASTICEMAIL_/, /^ELASTIC_EMAIL_/] }
1466
+ },
1467
+ {
1468
+ kind: "CampaignMonitor",
1469
+ label: "Campaign Monitor",
1470
+ mode: "in-cli",
1471
+ signals: {
1472
+ npm: ["createsend-node"],
1473
+ envKeys: [/^CAMPAIGN_MONITOR_/, /^CREATESEND_/]
1474
+ }
1475
+ },
1476
+ {
1477
+ kind: "Lemlist",
1478
+ label: "Lemlist",
1479
+ mode: "in-cli",
1480
+ signals: { envKeys: [/^LEMLIST_/] }
1481
+ },
1482
+ {
1483
+ kind: "Segment",
1484
+ label: "Segment",
1485
+ mode: "in-cli",
1486
+ signals: {
1487
+ npm: ["@segment/analytics-node", "analytics-node"],
1488
+ python: ["segment-analytics-python", "analytics-python"],
1489
+ envKeys: [/^SEGMENT_/]
1490
+ }
1491
+ },
1492
+ {
1493
+ kind: "Snowplow",
1494
+ label: "Snowplow Analytics",
1495
+ mode: "in-cli",
1496
+ signals: {
1497
+ npm: ["@snowplow/node-tracker"],
1498
+ python: ["snowplow-tracker"],
1499
+ envKeys: [/^SNOWPLOW_/]
1500
+ }
1501
+ },
1502
+ {
1503
+ kind: "Matomo",
1504
+ label: "Matomo",
1505
+ mode: "in-cli",
1506
+ signals: {
1507
+ npm: ["matomo-tracker"],
1508
+ envKeys: [/^MATOMO_/]
1509
+ }
1510
+ },
1511
+ {
1512
+ kind: "Plausible",
1513
+ label: "Plausible",
1514
+ mode: "in-cli",
1515
+ signals: {
1516
+ npm: ["plausible-tracker"],
1517
+ envKeys: [/^PLAUSIBLE_/]
1518
+ }
1519
+ },
1520
+ {
1521
+ kind: "ConfigCat",
1522
+ label: "ConfigCat",
1523
+ mode: "in-cli",
1524
+ signals: {
1525
+ npm: ["configcat-node", "configcat-js"],
1526
+ python: ["configcat-client"],
1527
+ envKeys: [/^CONFIGCAT_/]
1528
+ }
1529
+ },
1530
+ {
1531
+ kind: "Flagsmith",
1532
+ label: "Flagsmith",
1533
+ mode: "in-cli",
1534
+ signals: {
1535
+ npm: ["flagsmith-nodejs", "flagsmith"],
1536
+ python: ["flagsmith"],
1537
+ envKeys: [/^FLAGSMITH_/]
1538
+ }
1539
+ },
1540
+ {
1541
+ kind: "Unleash",
1542
+ label: "Unleash",
1543
+ mode: "in-cli",
1544
+ signals: {
1545
+ npm: ["unleash-client"],
1546
+ python: ["unleashclient"],
1547
+ envKeys: [/^UNLEASH_/]
1548
+ }
1549
+ },
1550
+ {
1551
+ kind: "SplitIo",
1552
+ label: "Split.io",
1553
+ mode: "in-cli",
1554
+ signals: {
1555
+ npm: ["@splitsoftware/splitio"],
1556
+ python: ["splitio-client"],
1557
+ envKeys: [/^SPLITIO_/, /^SPLIT_IO_/]
1558
+ }
1559
+ },
1560
+ {
1561
+ kind: "Stytch",
1562
+ label: "Stytch",
1563
+ mode: "in-cli",
1564
+ signals: {
1565
+ npm: ["stytch"],
1566
+ python: ["stytch"],
1567
+ envKeys: [/^STYTCH_/]
1568
+ }
1569
+ },
1570
+ {
1571
+ kind: "Doppler",
1572
+ label: "Doppler",
1573
+ mode: "in-cli",
1574
+ signals: {
1575
+ npm: ["@dopplerhq/node-sdk"],
1576
+ envKeys: [/^DOPPLER_/]
1577
+ }
1578
+ },
1579
+ {
1580
+ kind: "Infisical",
1581
+ label: "Infisical",
1582
+ mode: "in-cli",
1583
+ signals: {
1584
+ npm: ["@infisical/sdk"],
1585
+ python: ["infisicalsdk"],
1586
+ envKeys: [/^INFISICAL_/]
1587
+ }
1588
+ },
1589
+ {
1590
+ kind: "OnePassword",
1591
+ label: "1Password",
1592
+ mode: "in-cli",
1593
+ signals: {
1594
+ npm: ["@1password/sdk"],
1595
+ python: ["onepassword-sdk"],
1596
+ envKeys: [/^ONEPASSWORD_/, /^OP_SERVICE_ACCOUNT_TOKEN$/]
1597
+ }
1598
+ },
1599
+ {
1600
+ kind: "Persona",
1601
+ label: "Persona",
1602
+ mode: "in-cli",
1603
+ signals: { envKeys: [/^PERSONA_/] }
1604
+ },
1605
+ {
1606
+ kind: "Svix",
1607
+ label: "Svix",
1608
+ mode: "in-cli",
1609
+ signals: {
1610
+ npm: ["svix"],
1611
+ python: ["svix"],
1612
+ envKeys: [/^SVIX_/]
1613
+ }
1614
+ },
1615
+ {
1616
+ kind: "Mux",
1617
+ label: "Mux",
1618
+ mode: "in-cli",
1619
+ signals: {
1620
+ npm: ["@mux/mux-node"],
1621
+ python: ["mux-python"],
1622
+ envKeys: [/^MUX_/]
1623
+ }
1624
+ },
1625
+ {
1626
+ kind: "Vapi",
1627
+ label: "Vapi",
1628
+ mode: "in-cli",
1629
+ signals: {
1630
+ npm: ["@vapi-ai/server-sdk"],
1631
+ python: ["vapi-server-sdk"],
1632
+ envKeys: [/^VAPI_/]
1633
+ }
1634
+ },
1635
+ {
1636
+ kind: "Aircall",
1637
+ label: "Aircall",
1638
+ mode: "in-cli",
1639
+ signals: { envKeys: [/^AIRCALL_/] }
1640
+ },
1641
+ {
1642
+ kind: "JustCall",
1643
+ label: "JustCall",
1644
+ mode: "in-cli",
1645
+ signals: { envKeys: [/^JUSTCALL_/] }
1646
+ },
1647
+ {
1648
+ kind: "WooCommerce",
1649
+ label: "WooCommerce",
1650
+ mode: "in-cli",
1651
+ signals: {
1652
+ npm: ["@woocommerce/woocommerce-rest-api"],
1653
+ python: ["woocommerce"],
1654
+ envKeys: [/^WOOCOMMERCE_/]
1655
+ }
1656
+ },
1657
+ {
1658
+ kind: "Recharge",
1659
+ label: "Recharge",
1660
+ mode: "in-cli",
1661
+ signals: { envKeys: [/^RECHARGE_/] }
1662
+ },
1663
+ {
1664
+ kind: "Commercetools",
1665
+ label: "commercetools",
1666
+ mode: "in-cli",
1667
+ signals: {
1668
+ npm: ["@commercetools/platform-sdk"],
1669
+ python: ["commercetools"],
1670
+ envKeys: [/^CTP_/, /^COMMERCETOOLS_/]
1671
+ }
1672
+ },
1673
+ {
1674
+ kind: "Webflow",
1675
+ label: "Webflow",
1676
+ mode: "in-cli",
1677
+ signals: {
1678
+ npm: ["webflow-api"],
1679
+ envKeys: [/^WEBFLOW_/]
1680
+ }
1681
+ },
1682
+ {
1683
+ kind: "Easypost",
1684
+ label: "EasyPost",
1685
+ mode: "in-cli",
1686
+ signals: {
1687
+ npm: ["@easypost/api"],
1688
+ python: ["easypost"],
1689
+ envKeys: [/^EASYPOST_/]
1690
+ }
1691
+ },
1692
+ {
1693
+ kind: "Shippo",
1694
+ label: "Shippo",
1695
+ mode: "in-cli",
1696
+ signals: {
1697
+ npm: ["shippo"],
1698
+ python: ["shippo"],
1699
+ envKeys: [/^SHIPPO_/]
1700
+ }
1701
+ },
1702
+ {
1703
+ kind: "ShipStation",
1704
+ label: "ShipStation",
1705
+ mode: "in-cli",
1706
+ signals: { envKeys: [/^SHIPSTATION_/] }
1707
+ },
1708
+ {
1709
+ kind: "Squarespace",
1710
+ label: "Squarespace",
1711
+ mode: "in-cli",
1712
+ signals: { envKeys: [/^SQUARESPACE_/] }
1713
+ },
1714
+ {
1715
+ kind: "Printify",
1716
+ label: "Printify",
1717
+ mode: "in-cli",
1718
+ signals: { envKeys: [/^PRINTIFY_/] }
1719
+ },
1720
+ {
1721
+ kind: "Freshdesk",
1722
+ label: "Freshdesk",
1723
+ mode: "in-cli",
1724
+ signals: {
1725
+ python: ["python-freshdesk"],
1726
+ envKeys: [/^FRESHDESK_/]
1727
+ }
1728
+ },
1729
+ {
1730
+ kind: "Front",
1731
+ label: "Front",
1732
+ mode: "in-cli",
1733
+ signals: { envKeys: [/^FRONTAPP_/, /^FRONT_API_/] }
1734
+ },
1735
+ {
1736
+ kind: "Gorgias",
1737
+ label: "Gorgias",
1738
+ mode: "in-cli",
1739
+ signals: { envKeys: [/^GORGIAS_/] }
1740
+ },
1741
+ {
1742
+ kind: "Chatwoot",
1743
+ label: "Chatwoot",
1744
+ mode: "in-cli",
1745
+ signals: { envKeys: [/^CHATWOOT_/] }
1746
+ },
1747
+ {
1748
+ kind: "Kustomer",
1749
+ label: "Kustomer",
1750
+ mode: "in-cli",
1751
+ signals: { envKeys: [/^KUSTOMER_/] }
1752
+ },
1753
+ {
1754
+ kind: "Plain",
1755
+ label: "Plain",
1756
+ mode: "in-cli",
1757
+ signals: {
1758
+ npm: ["@team-plain/typescript-sdk"],
1759
+ envKeys: [/^PLAIN_/]
1760
+ }
1761
+ },
1762
+ {
1763
+ kind: "Canny",
1764
+ label: "Canny",
1765
+ mode: "in-cli",
1766
+ signals: { envKeys: [/^CANNY_/] }
1767
+ },
1768
+ {
1769
+ kind: "Pylon",
1770
+ label: "Pylon",
1771
+ mode: "in-cli",
1772
+ signals: { envKeys: [/^PYLON_/] }
1773
+ },
1774
+ {
1775
+ kind: "GitLab",
1776
+ label: "GitLab",
1777
+ mode: "in-cli",
1778
+ signals: {
1779
+ npm: ["@gitbeaker/rest"],
1780
+ python: ["python-gitlab"],
1781
+ ruby: ["gitlab"],
1782
+ envKeys: [/^GITLAB_/]
1783
+ }
1784
+ },
1785
+ {
1786
+ kind: "Bitbucket",
1787
+ label: "Atlassian Bitbucket Cloud",
1788
+ mode: "in-cli",
1789
+ signals: {
1790
+ npm: ["bitbucket"],
1791
+ envKeys: [/^BITBUCKET_/]
1792
+ }
1793
+ },
1794
+ {
1795
+ kind: "Gitea",
1796
+ label: "Gitea",
1797
+ mode: "in-cli",
1798
+ signals: {
1799
+ npm: ["gitea-js"],
1800
+ envKeys: [/^GITEA_/]
1801
+ }
1802
+ },
1803
+ {
1804
+ kind: "Datadog",
1805
+ label: "Datadog",
1806
+ mode: "in-cli",
1807
+ signals: {
1808
+ npm: ["@datadog/datadog-api-client", "dd-trace"],
1809
+ python: ["datadog", "datadog-api-client"],
1810
+ ruby: ["dogapi"],
1811
+ envKeys: [
1812
+ /^DATADOG_/,
1813
+ /^DD_API_KEY$/,
1814
+ /^DD_APP_KEY$/
1815
+ ]
1816
+ }
1817
+ },
1818
+ {
1819
+ kind: "NewRelic",
1820
+ label: "New Relic",
1821
+ mode: "in-cli",
1822
+ signals: {
1823
+ npm: ["newrelic"],
1824
+ python: ["newrelic"],
1825
+ ruby: ["newrelic_rpm"],
1826
+ envKeys: [/^NEW_RELIC_/, /^NEWRELIC_/]
1827
+ }
1828
+ },
1829
+ {
1830
+ kind: "Cloudflare",
1831
+ label: "Cloudflare",
1832
+ mode: "in-cli",
1833
+ signals: {
1834
+ npm: ["cloudflare"],
1835
+ python: ["cloudflare"],
1836
+ envKeys: [/^CLOUDFLARE_/, /^CF_API_TOKEN$/]
1837
+ }
1838
+ },
1839
+ {
1840
+ kind: "Elasticsearch",
1841
+ label: "Elasticsearch",
1842
+ mode: "in-cli",
1843
+ signals: {
1844
+ npm: ["@elastic/elasticsearch"],
1845
+ python: ["elasticsearch"],
1846
+ ruby: ["elasticsearch"],
1847
+ envKeys: [/^ELASTICSEARCH_/]
1848
+ }
1849
+ },
1850
+ {
1851
+ kind: "Snyk",
1852
+ label: "Snyk",
1853
+ mode: "in-cli",
1854
+ signals: {
1855
+ npm: ["snyk"],
1856
+ python: ["pysnyk"],
1857
+ envKeys: [/^SNYK_/]
1858
+ }
1859
+ },
1860
+ {
1861
+ kind: "PagerDuty",
1862
+ label: "PagerDuty",
1863
+ mode: "in-cli",
1864
+ signals: {
1865
+ npm: ["@pagerduty/pdjs"],
1866
+ python: ["pdpyras"],
1867
+ envKeys: [/^PAGERDUTY_/, /^PD_API_KEY$/]
1868
+ }
1869
+ },
1870
+ {
1871
+ kind: "Opsgenie",
1872
+ label: "Opsgenie",
1873
+ mode: "in-cli",
1874
+ signals: { envKeys: [/^OPSGENIE_/] }
1875
+ },
1876
+ {
1877
+ kind: "IncidentIo",
1878
+ label: "incident.io",
1879
+ mode: "in-cli",
1880
+ signals: { envKeys: [/^INCIDENT_IO_/, /^INCIDENTIO_/] }
1881
+ },
1882
+ {
1883
+ kind: "Bugsnag",
1884
+ label: "Bugsnag",
1885
+ mode: "in-cli",
1886
+ signals: {
1887
+ npm: ["@bugsnag/js"],
1888
+ python: ["bugsnag"],
1889
+ ruby: ["bugsnag"],
1890
+ envKeys: [/^BUGSNAG_/]
1891
+ }
1892
+ },
1893
+ {
1894
+ kind: "Honeybadger",
1895
+ label: "Honeybadger",
1896
+ mode: "in-cli",
1897
+ signals: {
1898
+ npm: ["@honeybadger-io/js"],
1899
+ python: ["honeybadger"],
1900
+ ruby: ["honeybadger"],
1901
+ envKeys: [/^HONEYBADGER_/]
1902
+ }
1903
+ },
1904
+ {
1905
+ kind: "Raygun",
1906
+ label: "Raygun",
1907
+ mode: "in-cli",
1908
+ signals: {
1909
+ npm: ["raygun"],
1910
+ python: ["raygun4py"],
1911
+ ruby: ["raygun4ruby"],
1912
+ envKeys: [/^RAYGUN_/]
1913
+ }
1914
+ },
1915
+ {
1916
+ kind: "CircleCI",
1917
+ label: "CircleCI",
1918
+ mode: "in-cli",
1919
+ signals: { envKeys: [/^CIRCLECI_/, /^CIRCLE_TOKEN$/] }
1920
+ },
1921
+ {
1922
+ kind: "Upstash",
1923
+ label: "Upstash",
1924
+ mode: "in-cli",
1925
+ signals: {
1926
+ npm: ["@upstash/redis"],
1927
+ python: ["upstash-redis"],
1928
+ envKeys: [/^UPSTASH_/]
1929
+ }
1930
+ },
1931
+ {
1932
+ kind: "Vercel",
1933
+ label: "Vercel",
1934
+ mode: "in-cli",
1935
+ signals: {
1936
+ npm: ["@vercel/sdk"],
1937
+ envKeys: [/^VERCEL_API_TOKEN$/]
1938
+ }
1939
+ },
1940
+ {
1941
+ kind: "Pipedrive",
1942
+ label: "Pipedrive",
1943
+ mode: "in-cli",
1944
+ signals: {
1945
+ npm: ["pipedrive"],
1946
+ envKeys: [/^PIPEDRIVE_/]
1947
+ }
1948
+ },
1949
+ {
1950
+ kind: "Close",
1951
+ label: "Close",
1952
+ mode: "in-cli",
1953
+ signals: {
1954
+ python: ["closeio"],
1955
+ envKeys: [/^CLOSE_API_KEY$/]
1956
+ }
1957
+ },
1958
+ {
1959
+ kind: "Copper",
1960
+ label: "Copper",
1961
+ mode: "in-cli",
1962
+ signals: { envKeys: [/^COPPER_/] }
1963
+ },
1964
+ {
1965
+ kind: "Insightly",
1966
+ label: "Insightly",
1967
+ mode: "in-cli",
1968
+ signals: { envKeys: [/^INSIGHTLY_/] }
1969
+ },
1970
+ {
1971
+ kind: "Salesflare",
1972
+ label: "Salesflare",
1973
+ mode: "in-cli",
1974
+ signals: { envKeys: [/^SALESFLARE_/] }
1975
+ },
1976
+ {
1977
+ kind: "SalesLoft",
1978
+ label: "Salesloft",
1979
+ mode: "in-cli",
1980
+ signals: { envKeys: [/^SALESLOFT_/] }
1981
+ },
1982
+ {
1983
+ kind: "Attio",
1984
+ label: "Attio",
1985
+ mode: "in-cli",
1986
+ signals: {
1987
+ npm: ["attio"],
1988
+ envKeys: [/^ATTIO_/]
1989
+ }
1990
+ },
1991
+ {
1992
+ kind: "ActiveCampaign",
1993
+ label: "ActiveCampaign",
1994
+ mode: "in-cli",
1995
+ signals: { envKeys: [/^ACTIVECAMPAIGN_/, /^ACTIVE_CAMPAIGN_/] }
1996
+ },
1997
+ {
1998
+ kind: "Vitally",
1999
+ label: "Vitally",
2000
+ mode: "in-cli",
2001
+ signals: { envKeys: [/^VITALLY_/] }
2002
+ },
2003
+ {
2004
+ kind: "Asana",
2005
+ label: "Asana",
2006
+ mode: "in-cli",
2007
+ signals: {
2008
+ npm: ["asana"],
2009
+ python: ["asana"],
2010
+ envKeys: [/^ASANA_/]
2011
+ }
2012
+ },
2013
+ {
2014
+ kind: "Trello",
2015
+ label: "Trello",
2016
+ mode: "in-cli",
2017
+ signals: {
2018
+ npm: ["trello"],
2019
+ envKeys: [/^TRELLO_/]
2020
+ }
2021
+ },
2022
+ {
2023
+ kind: "Todoist",
2024
+ label: "Todoist",
2025
+ mode: "in-cli",
2026
+ signals: {
2027
+ npm: ["@doist/todoist-api-typescript"],
2028
+ python: ["todoist-api-python"],
2029
+ envKeys: [/^TODOIST_/]
2030
+ }
2031
+ },
2032
+ {
2033
+ kind: "Monday",
2034
+ label: "monday.com",
2035
+ mode: "in-cli",
2036
+ signals: {
2037
+ npm: ["@mondaydotcomorg/api"],
2038
+ envKeys: [/^MONDAY_/]
2039
+ }
2040
+ },
2041
+ {
2042
+ kind: "ClickUp",
2043
+ label: "ClickUp",
2044
+ mode: "in-cli",
2045
+ signals: { envKeys: [/^CLICKUP_/] }
2046
+ },
2047
+ {
2048
+ kind: "Height",
2049
+ label: "Height",
2050
+ mode: "in-cli",
2051
+ signals: { envKeys: [/^HEIGHT_API_KEY$/] }
2052
+ },
2053
+ {
2054
+ kind: "Shortcut",
2055
+ label: "Shortcut",
2056
+ mode: "in-cli",
2057
+ signals: {
2058
+ npm: ["@shortcut/client"],
2059
+ envKeys: [/^SHORTCUT_/]
2060
+ }
2061
+ },
2062
+ {
2063
+ kind: "Jira",
2064
+ label: "Jira",
2065
+ mode: "in-cli",
2066
+ signals: {
2067
+ npm: ["jira-client"],
2068
+ python: ["jira", "atlassian-python-api"],
2069
+ envKeys: [/^JIRA_/]
2070
+ }
2071
+ },
2072
+ {
2073
+ kind: "Confluence",
2074
+ label: "Confluence",
2075
+ mode: "in-cli",
2076
+ signals: { envKeys: [/^CONFLUENCE_/] }
2077
+ },
2078
+ {
2079
+ kind: "Airtable",
2080
+ label: "Airtable",
2081
+ mode: "in-cli",
2082
+ signals: {
2083
+ npm: ["airtable"],
2084
+ python: ["pyairtable"],
2085
+ envKeys: [/^AIRTABLE_/]
2086
+ }
2087
+ },
2088
+ {
2089
+ kind: "Smartsheet",
2090
+ label: "Smartsheet",
2091
+ mode: "in-cli",
2092
+ signals: {
2093
+ npm: ["smartsheet"],
2094
+ python: ["smartsheet-python-sdk"],
2095
+ envKeys: [/^SMARTSHEET_/]
2096
+ }
2097
+ },
2098
+ {
2099
+ kind: "Coda",
2100
+ label: "Coda",
2101
+ mode: "in-cli",
2102
+ signals: { envKeys: [/^CODA_API_/] }
2103
+ },
2104
+ {
2105
+ kind: "Productboard",
2106
+ label: "Productboard",
2107
+ mode: "in-cli",
2108
+ signals: { envKeys: [/^PRODUCTBOARD_/] }
2109
+ },
2110
+ {
2111
+ kind: "Calendly",
2112
+ label: "Calendly",
2113
+ mode: "in-cli",
2114
+ signals: { envKeys: [/^CALENDLY_/] }
2115
+ },
2116
+ {
2117
+ kind: "CalCom",
2118
+ label: "Cal.com",
2119
+ mode: "in-cli",
2120
+ signals: { envKeys: [/^CALCOM_/, /^CAL_API_KEY$/] }
2121
+ },
2122
+ {
2123
+ kind: "Eventbrite",
2124
+ label: "Eventbrite",
2125
+ mode: "in-cli",
2126
+ signals: {
2127
+ python: ["eventbrite"],
2128
+ envKeys: [/^EVENTBRITE_/]
2129
+ }
2130
+ },
2131
+ {
2132
+ kind: "BambooHR",
2133
+ label: "BambooHR",
2134
+ mode: "in-cli",
2135
+ signals: {
2136
+ python: ["pybamboohr"],
2137
+ envKeys: [/^BAMBOOHR_/, /^BAMBOO_HR_/]
2138
+ }
2139
+ },
2140
+ {
2141
+ kind: "Personio",
2142
+ label: "Personio",
2143
+ mode: "in-cli",
2144
+ signals: { envKeys: [/^PERSONIO_/] }
2145
+ },
2146
+ {
2147
+ kind: "Rippling",
2148
+ label: "Rippling",
2149
+ mode: "in-cli",
2150
+ signals: { envKeys: [/^RIPPLING_/] }
2151
+ },
2152
+ {
2153
+ kind: "Greenhouse",
2154
+ label: "Greenhouse",
2155
+ mode: "in-cli",
2156
+ signals: { envKeys: [/^GREENHOUSE_/] }
2157
+ },
2158
+ {
2159
+ kind: "Ashby",
2160
+ label: "Ashby",
2161
+ mode: "in-cli",
2162
+ signals: { envKeys: [/^ASHBY_/] }
2163
+ },
2164
+ {
2165
+ kind: "Workable",
2166
+ label: "Workable",
2167
+ mode: "in-cli",
2168
+ signals: { envKeys: [/^WORKABLE_/] }
2169
+ },
2170
+ {
2171
+ kind: "GoogleAds",
2172
+ label: "Google Ads",
2173
+ mode: "deep-link",
2174
+ signals: {
2175
+ npm: ["google-ads-api"],
2176
+ python: ["google-ads"],
2177
+ envKeys: [/^GOOGLE_ADS_/]
2178
+ }
2179
+ },
2180
+ {
2181
+ kind: "MetaAds",
2182
+ label: "Meta Ads",
2183
+ mode: "deep-link",
2184
+ signals: {
2185
+ npm: ["facebook-nodejs-business-sdk"],
2186
+ python: ["facebook-business"],
2187
+ envKeys: [/^META_ADS_/, /^FACEBOOK_ADS_/]
2188
+ }
2189
+ },
2190
+ {
2191
+ kind: "LinkedinAds",
2192
+ label: "LinkedIn Ads",
2193
+ mode: "deep-link",
2194
+ signals: { envKeys: [/^LINKEDIN_ADS_/] }
2195
+ },
2196
+ {
2197
+ kind: "TikTokAds",
2198
+ label: "TikTok Ads",
2199
+ mode: "deep-link",
2200
+ signals: { envKeys: [/^TIKTOK_ADS_/] }
2201
+ },
2202
+ {
2203
+ kind: "RedditAds",
2204
+ label: "Reddit Ads",
2205
+ mode: "deep-link",
2206
+ signals: { envKeys: [/^REDDIT_ADS_/] }
2207
+ },
2208
+ {
2209
+ kind: "PinterestAds",
2210
+ label: "Pinterest Ads",
2211
+ mode: "deep-link",
2212
+ signals: { envKeys: [/^PINTEREST_ADS_/] }
2213
+ },
2214
+ {
2215
+ kind: "BingAds",
2216
+ label: "Bing Ads",
2217
+ mode: "deep-link",
2218
+ signals: {
2219
+ python: ["bingads"],
2220
+ envKeys: [/^BING_ADS_/, /^MICROSOFT_ADS_/]
2221
+ }
2222
+ },
2223
+ {
2224
+ kind: "AmazonAds",
2225
+ label: "Amazon Ads",
2226
+ mode: "deep-link",
2227
+ signals: { envKeys: [/^AMAZON_ADS_/] }
2228
+ },
2229
+ {
2230
+ kind: "GoogleAnalytics",
2231
+ label: "Google Analytics",
2232
+ mode: "deep-link",
2233
+ signals: {
2234
+ npm: ["react-ga4"],
2235
+ python: ["google-analytics-data"],
2236
+ envKeys: [/^GOOGLE_ANALYTICS_/, /^GA4_/]
2237
+ }
2238
+ },
2239
+ {
2240
+ kind: "GoogleSearchConsole",
2241
+ label: "Google Search Console",
2242
+ mode: "deep-link",
2243
+ signals: { envKeys: [/^SEARCH_CONSOLE_/, /^GSC_/] }
2244
+ },
2245
+ {
2246
+ kind: "AppsFlyer",
2247
+ label: "AppsFlyer",
2248
+ mode: "in-cli",
2249
+ signals: {
2250
+ npm: ["react-native-appsflyer"],
2251
+ envKeys: [/^APPSFLYER_/]
2252
+ }
2253
+ }
2254
+ ];
2255
+ //#endregion
2256
+ //#region src/lib/warehouse-sources/detect.ts
314
2257
  /**
315
- * Validate the agent's raw JSON into a report, clamping each targetId to the
316
- * supplied set and each path to a repo-relative dir. Exported for testing.
2258
+ * Data warehouse source detection.
2259
+ *
2260
+ * Scans a project for codebase signals (npm/python/ruby deps, `.env` key
2261
+ * names) and matches them against the `SOURCE_DETECTORS` registry. Pure
2262
+ * function: no store mutations, no UI calls. Reads files locally — only the
2263
+ * wizard process does this; the agent never sees secret values.
2264
+ *
2265
+ * Note we only read `.env` KEY NAMES, never values.
317
2266
  */
318
- function coerceAgenticReport(parsed, validTargetIds) {
319
- const obj = parsed ?? {};
320
- return {
321
- repoType: obj.repoType === "monorepo" ? "monorepo" : "single",
322
- projects: (Array.isArray(obj.projects) ? obj.projects : []).map((raw) => {
323
- const p = raw ?? {};
324
- const targetId = typeof p.targetId === "string" && validTargetIds.includes(p.targetId) ? p.targetId : null;
325
- return {
326
- path: coercePath(p.path),
327
- framework: typeof p.framework === "string" ? p.framework : "Unknown",
328
- targetId,
329
- hasPostHog: p.hasPostHog === true
330
- };
331
- })
332
- };
2267
+ const MAX_DEPTH = 3;
2268
+ /**
2269
+ * Detect which warehouse sources the project at `installDir` appears to use.
2270
+ * Returns one `DetectedSource` per matched registry entry (kinds are unique,
2271
+ * so the result is naturally deduped).
2272
+ */
2273
+ function detectWarehouseSources(installDir) {
2274
+ const signals = collectSignals(installDir);
2275
+ const detected = [];
2276
+ for (const detector of SOURCE_DETECTORS) {
2277
+ const match = matchDetector(detector, signals);
2278
+ if (match) detected.push({
2279
+ kind: detector.kind,
2280
+ label: detector.label,
2281
+ mode: detector.mode,
2282
+ matchedSignal: match
2283
+ });
2284
+ }
2285
+ return detected;
333
2286
  }
334
- function formatToolUse(block) {
335
- const name = typeof block?.name === "string" ? block.name : "tool";
336
- const input = block?.input ?? {};
337
- const detail = input.file_path || input.pattern || input.path || "";
338
- return detail ? `${name} ${detail}` : name;
2287
+ /** Returns a human-readable description of the first matching signal, or null. */
2288
+ function matchDetector(detector, signals) {
2289
+ const { npm, python, ruby, envKeys } = detector.signals;
2290
+ const npmHit = npm?.find((dep) => signals.npm.has(dep));
2291
+ if (npmHit) return `found \`${npmHit}\` in package.json`;
2292
+ const pyHit = python?.find((dep) => signals.python.has(dep));
2293
+ if (pyHit) return `found \`${pyHit}\` in Python dependencies`;
2294
+ const rubyHit = ruby?.find((gem) => signals.ruby.has(gem));
2295
+ if (rubyHit) return `found \`${rubyHit}\` in Gemfile`;
2296
+ if (envKeys) {
2297
+ for (const key of signals.envKeys) if (envKeys.some((re) => re.test(key))) return `found \`${key}\` in .env`;
2298
+ }
2299
+ return null;
339
2300
  }
340
- function sessionToWizardOptions(session) {
341
- return {
342
- installDir: session.installDir,
343
- ci: session.ci,
344
- debug: session.debug,
345
- default: false,
346
- benchmark: session.benchmark,
347
- yaraReport: session.yaraReport,
348
- signup: session.signup,
349
- apiKey: session.apiKey,
350
- projectId: session.projectId,
351
- localMcp: session.localMcp
2301
+ function collectSignals(installDir) {
2302
+ const signals = {
2303
+ npm: /* @__PURE__ */ new Set(),
2304
+ python: /* @__PURE__ */ new Set(),
2305
+ ruby: /* @__PURE__ */ new Set(),
2306
+ envKeys: /* @__PURE__ */ new Set()
352
2307
  };
2308
+ walkProjectFiles(installDir, (name, fullPath) => ingestFile(name, fullPath, signals), MAX_DEPTH);
2309
+ return signals;
353
2310
  }
354
- const NOOP_SPINNER = {
355
- start: () => void 0,
356
- stop: () => void 0,
357
- message: () => void 0
358
- };
359
2311
  /**
360
- * Drive the wizard's agent loop on HAIKU_MODEL to scan the repo and return a
361
- * structured detection report. Reuses the same setup every program uses, so
362
- * MCP, tools, and credentials are wired identically.
2312
+ * Route a file to the right parser BY NAME, reading its contents only when the
2313
+ * name matches one of the ~6 manifests we care about. Checking the name first
2314
+ * avoids slurping every file in the tree (lockfiles, binaries, assets) into
2315
+ * memory just to discard it.
363
2316
  */
364
- async function detectProjectsWithAgent(session, options) {
365
- if (!session.credentials) throw new Error("Detection requires authenticated credentials.");
366
- const { targets, purpose = "set up a PostHog integration", onEvent } = options;
367
- const { accessToken, host } = session.credentials;
368
- const cwd = session.installDir;
369
- const runOptions = sessionToWizardOptions(session);
370
- const agent = await initializeAgent({
371
- workingDirectory: cwd,
372
- posthogMcpUrl: host.mcpUrl,
373
- posthogApiKey: accessToken,
374
- host,
375
- detectPackageManager: detectNodePackageManagers,
376
- skillsBaseUrl: getSkillsBaseUrl(session.localMcp),
377
- integrationLabel: "agentic-detect",
378
- allowedTools: [
379
- "Read",
380
- "Grep",
381
- "Glob"
382
- ],
383
- modelOverride: HAIKU_MODEL
384
- }, runOptions);
385
- const collected = [];
386
- let resultText = "";
387
- const result = await runAgent$1(agent, buildPrompt(cwd, targets, purpose), runOptions, NOOP_SPINNER, {
388
- spinnerMessage: "Scanning the repo...",
389
- successMessage: "Detection complete",
390
- errorMessage: "Detection failed",
391
- requestRemark: false
392
- }, {
393
- onMessage: (message) => {
394
- if (message?.type === "assistant") {
395
- for (const block of message.message?.content ?? []) if (block?.type === "text" && typeof block.text === "string") {
396
- collected.push(block.text);
397
- const line = block.text.trim();
398
- if (line && onEvent) onEvent(line.length > 100 ? `${line.slice(0, 100)}…` : line);
399
- } else if (block?.type === "tool_use") onEvent?.(formatToolUse(block));
400
- } else if (message?.type === "result" && typeof message.result === "string") resultText = message.result;
401
- },
402
- finalize: (_resultMessage, _durationMs) => void 0
403
- });
404
- if (result.error) throw new Error(result.message || `Agent error: ${result.error}`);
405
- const output = resultText || collected.join("\n");
2317
+ function ingestFile(name, fullPath, signals) {
2318
+ const ingest = ingestorFor(name);
2319
+ if (!ingest) return;
2320
+ const content = safeReadFile(fullPath);
2321
+ if (content === null) return;
2322
+ ingest(content, signals);
2323
+ }
2324
+ /** Pick the parser for a manifest filename, or null if it's not one we read. */
2325
+ function ingestorFor(name) {
2326
+ if (name === "package.json") return addNpmDeps;
2327
+ if (name === "requirements.txt") return (c, s) => parseRequirementsTxt(c).forEach((d) => s.python.add(d));
2328
+ if (name === "pyproject.toml") return (c, s) => parsePyprojectToml(c).forEach((d) => s.python.add(d));
2329
+ if (name === "Pipfile") return (c, s) => parsePipfile(c).forEach((d) => s.python.add(d));
2330
+ if (name === "Gemfile") return (c, s) => parseGemfile(c).forEach((g) => s.ruby.add(g));
2331
+ if (name.startsWith(".env")) return (c, s) => parseEnvKeys(c).forEach((k) => s.envKeys.add(k));
2332
+ return null;
2333
+ }
2334
+ function addNpmDeps(content, signals) {
406
2335
  try {
407
- return coerceAgenticReport(extractJson(output), targets.map((t) => t.id));
408
- } catch (err) {
409
- if (output.includes(AgentSignals.ABORT)) return {
410
- repoType: "single",
411
- projects: []
412
- };
413
- throw err;
2336
+ const pkg = JSON.parse(content);
2337
+ for (const dep of Object.keys({
2338
+ ...pkg.dependencies,
2339
+ ...pkg.devDependencies
2340
+ })) signals.npm.add(dep);
2341
+ } catch (error) {
2342
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), { step: "detectWarehouseSources.parsePackageJson" });
2343
+ }
2344
+ }
2345
+ /** Extract gem names from `gem 'name'` declarations. */
2346
+ function parseGemfile(content) {
2347
+ const gems = [];
2348
+ for (const match of content.matchAll(/^\s*gem\s+['"]([^'"]+)['"]/gm)) gems.push(match[1]);
2349
+ return gems;
2350
+ }
2351
+ /** Extract KEY NAMES from a dotenv file. Values are intentionally discarded. */
2352
+ function parseEnvKeys(content) {
2353
+ const keys = [];
2354
+ for (const line of content.split("\n")) {
2355
+ const trimmed = line.trim();
2356
+ if (!trimmed || trimmed.startsWith("#")) continue;
2357
+ const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
2358
+ if (match) keys.push(match[1]);
2359
+ }
2360
+ return keys;
2361
+ }
2362
+ //#endregion
2363
+ //#region src/lib/programs/warehouse-source/detect.ts
2364
+ /**
2365
+ * Warehouse-source program detection step.
2366
+ *
2367
+ * Thin adapter over `detectWarehouseSources` that writes results into
2368
+ * frameworkContext for the intro screen, plus the `[ABORT]` cases the
2369
+ * data-warehouse-source-setup skill can emit.
2370
+ */
2371
+ /** frameworkContext key holding the detected sources (set on success). */
2372
+ const DETECTED_WAREHOUSE_SOURCES_KEY = "detectedWarehouseSources";
2373
+ /**
2374
+ * Read the detected sources out of frameworkContext. Single accessor shared by
2375
+ * the intro screen and the prompt builder so the key + cast live in one place.
2376
+ */
2377
+ function getDetectedWarehouseSources(session) {
2378
+ return session.frameworkContext["detectedWarehouseSources"] ?? [];
2379
+ }
2380
+ /** `[ABORT] <reason>` cases the skill can emit. */
2381
+ const WAREHOUSE_ABORT_CASES = [{
2382
+ match: /^no data sources? detected\.?$/i,
2383
+ message: "No data source detected",
2384
+ body: "The agent could not confirm a data warehouse source to connect. Run this command from a project that uses a supported source (a database, Stripe, etc.).",
2385
+ docsUrl: "https://posthog.com/docs/data-warehouse"
2386
+ }, {
2387
+ match: /^source creation failed\.?$/i,
2388
+ message: "Source creation failed",
2389
+ body: "PostHog could not create the data warehouse source with the credentials provided. Double-check the connection details and try again, or set the source up directly in the PostHog app.",
2390
+ docsUrl: "https://posthog.com/docs/data-warehouse"
2391
+ }];
2392
+ /**
2393
+ * Scan `session.installDir` for warehouse-source signals. Writes the detected
2394
+ * sources (or a `detectError`) into frameworkContext for the intro screen.
2395
+ */
2396
+ function detectWarehousePrerequisites(session, setFrameworkContext) {
2397
+ const fail = (error) => setFrameworkContext("detectError", error);
2398
+ const installDir = session.installDir;
2399
+ if (!existsSync(installDir)) {
2400
+ fail({
2401
+ kind: "bad-directory",
2402
+ path: installDir,
2403
+ reason: "missing"
2404
+ });
2405
+ return;
2406
+ }
2407
+ try {
2408
+ if (!statSync(installDir).isDirectory()) {
2409
+ fail({
2410
+ kind: "bad-directory",
2411
+ path: installDir,
2412
+ reason: "not-dir"
2413
+ });
2414
+ return;
2415
+ }
2416
+ } catch (error) {
2417
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), {
2418
+ step: "detectWarehousePrerequisites.stat",
2419
+ path: installDir
2420
+ });
2421
+ fail({
2422
+ kind: "bad-directory",
2423
+ path: installDir,
2424
+ reason: "unreadable"
2425
+ });
2426
+ return;
2427
+ }
2428
+ const sources = detectWarehouseSources(installDir);
2429
+ if (sources.length === 0) {
2430
+ fail({ kind: "no-sources" });
2431
+ return;
414
2432
  }
2433
+ analytics.setTag("warehouse_source_kinds", sources.map((s) => s.kind).join(","));
2434
+ analytics.setTag("warehouse_source_modes", sources.map((s) => `${s.kind}:${s.mode}`).join(","));
2435
+ analytics.setTag("warehouse_source_count", sources.length);
2436
+ setFrameworkContext(DETECTED_WAREHOUSE_SOURCES_KEY, sources);
415
2437
  }
416
2438
  //#endregion
417
2439
  //#region src/lib/programs/shared/health-check-step.ts
@@ -472,8 +2494,41 @@ async function detectPostHogIntegration(ctx) {
472
2494
  if (versionResult.supported !== true) ctx.setUnsupportedVersion(versionResult.supported);
473
2495
  }
474
2496
  for (const feature of discoverFeatures(installDir)) ctx.addDiscoveredFeature(feature);
2497
+ detectWarehouseSourcesForSuggestion(ctx, installDir);
475
2498
  ctx.setDetectionComplete();
476
2499
  }
2500
+ /**
2501
+ * Scan for data warehouse source signals (Postgres, Stripe, Hubspot, …) and,
2502
+ * when found, stash them for a `wizard warehouse` suggestion on the outro.
2503
+ *
2504
+ * Deliberately a suggestion, not an inline agent run: a second credential-
2505
+ * collecting agent run before the outro could `process.exit()` on any of its
2506
+ * failure paths and cost the user the success outro on a run where PostHog
2507
+ * installed fine.
2508
+ *
2509
+ * Calls `detectWarehouseSources` (a pure scan) rather than the warehouse
2510
+ * program's `detectWarehousePrerequisites`, which writes a `detectError` when
2511
+ * it finds nothing — that's right for `wizard warehouse` but here would show a
2512
+ * spurious error and trip the CI prerequisites abort. No sources is a no-op.
2513
+ *
2514
+ * Best-effort: never let a scan failure break the surrounding detect step.
2515
+ */
2516
+ function detectWarehouseSourcesForSuggestion(ctx, installDir) {
2517
+ try {
2518
+ const sources = detectWarehouseSources(installDir);
2519
+ analytics.wizardCapture("warehouse sources detected", {
2520
+ warehouse_source_count: sources.length,
2521
+ warehouse_source_kinds: sources.map((s) => s.kind),
2522
+ warehouse_source_modes: sources.map((s) => s.mode)
2523
+ });
2524
+ if (sources.length === 0) return;
2525
+ analytics.setTag("warehouse_source_kinds", sources.map((s) => s.kind).join(","));
2526
+ analytics.setTag("warehouse_source_count", sources.length);
2527
+ ctx.setFrameworkContext(DETECTED_WAREHOUSE_SOURCES_KEY, sources);
2528
+ } catch (error) {
2529
+ analytics.captureException(error instanceof Error ? error : new Error(String(error)), { step: "detectWarehouseSourcesForSuggestion" });
2530
+ }
2531
+ }
477
2532
  //#endregion
478
2533
  //#region src/lib/programs/posthog-integration/steps.ts
479
2534
  function needsSetup(session) {
@@ -1560,15 +3615,53 @@ function resolveContinueUrl(sess, host, deepLink) {
1560
3615
  if (typeof deepLink === "string" && deepLink) return deepLink;
1561
3616
  return withUtm(`${host.appHost}/products?source=wizard`, "outro-continue");
1562
3617
  }
3618
+ /**
3619
+ * Outro suggestion for data sources found in the project but not connected.
3620
+ *
3621
+ * A pointer at `wizard warehouse`, not an inline flow. Connecting a source
3622
+ * needs interactive credential collection, and chaining that as a second agent
3623
+ * run before the outro would let any of its terminal failure paths
3624
+ * `process.exit()` — costing the user the success outro and the post-outro
3625
+ * MCP / Slack steps on a run where PostHog installed fine.
3626
+ *
3627
+ * Returns undefined when nothing was detected, so the outro is unchanged for
3628
+ * projects with no connectable source.
3629
+ */
3630
+ function buildWarehouseNextSteps(sess) {
3631
+ const sources = getDetectedWarehouseSources(sess);
3632
+ if (sources.length === 0) return void 0;
3633
+ return {
3634
+ heading: "Query your other data in PostHog:",
3635
+ items: [`Found in this project: ${sources.map((s) => s.label).join(", ")}`, "Import it into the data warehouse with: npx @posthog/wizard warehouse"]
3636
+ };
3637
+ }
3638
+ /**
3639
+ * Prompt fragment asking the agent to note the detected data sources in the
3640
+ * setup report's checklist.
3641
+ *
3642
+ * Empty string when nothing was detected, so the prompt is byte-identical to
3643
+ * today for projects with no connectable source. Best-effort by nature — the
3644
+ * agent may word it differently or skip it. That is acceptable here precisely
3645
+ * because it is a note in a report: the outro `nextSteps` bullet carries the
3646
+ * same information deterministically, so nothing is lost if the agent drops it.
3647
+ */
3648
+ function warehouseReportInstruction(sess) {
3649
+ const sources = getDetectedWarehouseSources(sess);
3650
+ if (sources.length === 0) return "";
3651
+ return `Finally: this project also contains data sources PostHog can import (${sources.map((s) => s.label).join(", ")}). In the setup report's "Verify before merging" checklist, add one item noting these were found and that \`npx @posthog/wizard warehouse\` will connect them to PostHog's data warehouse. Do not attempt to set them up yourself in this run.`;
3652
+ }
1563
3653
  const SETUP_REPORT_FILE = "posthog-setup-report.md";
1564
3654
  const posthogIntegrationConfig = {
1565
3655
  command: "integrate",
1566
3656
  description: "Set up PostHog SDK integration",
1567
3657
  id: "posthog-integration",
3658
+ agentFlow: "integration-v2",
3659
+ eventPlanFile: EVENT_PLAN_FILE,
1568
3660
  steps: POSTHOG_INTEGRATION_PROGRAM,
1569
3661
  getContentBlocks,
1570
3662
  disallowedTools: [WIZARD_TOOL_NAMES.wizardAsk],
1571
3663
  ciPreRun: async (session) => {
3664
+ await scopeInstallDirToProject(session);
1572
3665
  const integration = await detectFramework(session.installDir);
1573
3666
  if (!integration) {
1574
3667
  await wizardAbort({ message: "Could not auto-detect your framework for this project." });
@@ -1599,7 +3692,7 @@ const posthogIntegrationConfig = {
1599
3692
  if (usesPackageJson) {
1600
3693
  const packageJson = await tryGetPackageJson({ installDir: session.installDir });
1601
3694
  if (packageJson) {
1602
- const { hasDeclaredDependency } = await import("./package-json-CDs1ZVKu.js").then((n) => n.i);
3695
+ const { hasDeclaredDependency } = await import("./package-json-DCuoye-H.js").then((n) => n.i);
1603
3696
  if (!hasDeclaredDependency(config.detection.packageName, packageJson)) getUI().log.warn(`${config.detection.packageDisplayName} does not seem to be installed. Continuing anyway — the agent will handle it.`);
1604
3697
  frameworkVersion = config.detection.getVersion(packageJson);
1605
3698
  } else getUI().log.warn("Could not find package.json. Continuing anyway — the agent will handle it.");
@@ -1661,13 +3754,13 @@ STEP 5: Set up environment variables for PostHog using the wizard-tools MCP serv
1661
3754
 
1662
3755
  Important: Use the detect_package_manager tool (from the wizard-tools MCP server) to determine which package manager the project uses, then run its install command to add the SDK. Do not manually search for lockfiles or config files. If a file already EXISTS, read it immediately before you edit or overwrite it — writing from a stale read causes a tool failure. Creating a brand-new file needs no prior read: never read a path that does not exist yet; just write it.
1663
3756
 
1664
-
3757
+ ${warehouseReportInstruction(session)}
1665
3758
  `;
1666
3759
  },
1667
3760
  postRun: async (sess, credentials) => {
1668
3761
  const envVars = config.environment.getEnvVars(credentials.projectApiKey, credentials.host.apiHost);
1669
3762
  if (config.environment.uploadToHosting) {
1670
- const { uploadEnvironmentVariablesStep } = await import("./steps-erepgD0V.js");
3763
+ const { uploadEnvironmentVariablesStep } = await import("./steps-DyEKYM4I.js");
1671
3764
  const uploadedEnvVars = await uploadEnvironmentVariablesStep(envVars, {
1672
3765
  integration: config.metadata.integration,
1673
3766
  session: sess
@@ -1699,6 +3792,7 @@ Important: Use the detect_package_manager tool (from the wizard-tools MCP server
1699
3792
  changes: [...config.ui.getOutroChanges(frameworkContext), Object.keys(envVars).length > 0 ? "Added environment variables to .env file" : ""].filter(Boolean),
1700
3793
  docsUrl: config.metadata.docsUrl,
1701
3794
  continueUrl,
3795
+ nextSteps: buildWarehouseNextSteps(sess),
1702
3796
  notebookUrl: sess.notebookUrl ?? void 0,
1703
3797
  handoffPrompt: buildCodingAgentPrompt(SETUP_REPORT_FILE)
1704
3798
  };
@@ -1721,6 +3815,6 @@ const integrationRunStep = {
1721
3815
  isComplete: (session) => session.runPhase === "completed" || session.runPhase === "error"
1722
3816
  };
1723
3817
  //#endregion
1724
- export { gatherFrameworkContext as _, LINE_CHART_BLOCK as a, parseRequirementsTxt as b, TextBlock as c, isLinesBlock as d, isObjectBlock as f, detectProjectsWithAgent as g, HEALTH_CHECK_STEP as h, FUNNEL_BLOCK as i, computeVisibleRange as l, Icons as m, integrationRunStep as n, PRODUCT_SUITE_BLOCK as o, Colors as p, posthogIntegrationConfig as r, StatusPeekTrigger as s, SETUP_REPORT_FILE as t, isClearBlock as u, parsePipfile as v, parsePyprojectToml as y };
3818
+ export { gatherFrameworkContext as S, detectWarehousePrerequisites as _, LINE_CHART_BLOCK as a, detectProjectsWithAgent as b, TextBlock as c, isLinesBlock as d, isObjectBlock as f, WAREHOUSE_ABORT_CASES as g, HEALTH_CHECK_STEP as h, FUNNEL_BLOCK as i, computeVisibleRange as l, Icons as m, integrationRunStep as n, PRODUCT_SUITE_BLOCK as o, Colors as p, posthogIntegrationConfig as r, StatusPeekTrigger as s, SETUP_REPORT_FILE as t, isClearBlock as u, getDetectedWarehouseSources as v, resolveProjectDir as x, detectIntegrationProjects as y };
1725
3819
 
1726
- //# sourceMappingURL=posthog-integration-BhLKCFsI.js.map
3820
+ //# sourceMappingURL=posthog-integration-Do997QON.js.map