@posthog/wizard 2.15.0 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +0 -4
  2. package/dist/{TextBlock-B_8bXLLs.js → TextBlock-CdeZog_6.js} +4 -4
  3. package/dist/TextBlock-CdeZog_6.js.map +1 -0
  4. package/dist/{add-mcp-server-to-clients-Dq0n2yzq.js → add-mcp-server-to-clients-BS6Rjcwh.js} +6 -6
  5. package/dist/{add-mcp-server-to-clients-Dq0n2yzq.js.map → add-mcp-server-to-clients-BS6Rjcwh.js.map} +1 -1
  6. package/dist/{agent-interface-yB_27jG8.js → agent-interface-B4eUlMso.js} +33 -7
  7. package/dist/agent-interface-B4eUlMso.js.map +1 -0
  8. package/dist/{agent-runner-C9sSudE0.js → agent-runner-BxqiKVEf.js} +21 -22
  9. package/dist/{agent-runner-C9sSudE0.js.map → agent-runner-BxqiKVEf.js.map} +1 -1
  10. package/dist/{analytics-Da4QHjMw.js → analytics-DUuUurR3.js} +3 -3
  11. package/dist/{analytics-Da4QHjMw.js.map → analytics-DUuUurR3.js.map} +1 -1
  12. package/dist/api-B8OR0N1V.js +2 -0
  13. package/dist/api-CGJ1iGps.js +138 -0
  14. package/dist/api-CGJ1iGps.js.map +1 -0
  15. package/dist/bin.js +808 -476
  16. package/dist/bin.js.map +1 -1
  17. package/dist/ci-install-DD7WMmIF.js +73 -0
  18. package/dist/ci-install-DD7WMmIF.js.map +1 -0
  19. package/dist/{debug-D5kt4fxB.js → debug-Cd0hPlZy.js} +1 -1
  20. package/dist/{debug-DRKLej5r.js → debug-ubpO6102.js} +14 -6
  21. package/dist/debug-ubpO6102.js.map +1 -0
  22. package/dist/{defaults-CPH6eWhN.js → defaults-zrYmZ2ID.js} +1 -1
  23. package/dist/{defaults-CPH6eWhN.js.map → defaults-zrYmZ2ID.js.map} +1 -1
  24. package/dist/{env-api-key-HFqv1l-z.js → env-api-key-DEl3LJBv.js} +4 -2
  25. package/dist/{env-api-key-HFqv1l-z.js.map → env-api-key-DEl3LJBv.js.map} +1 -1
  26. package/dist/environment-BAaC5THg.js +22 -0
  27. package/dist/environment-BAaC5THg.js.map +1 -0
  28. package/dist/{file-utils-DnTSiTJw.js → file-utils-DPmgn9Vm.js} +1 -1
  29. package/dist/{file-utils-DnTSiTJw.js.map → file-utils-DPmgn9Vm.js.map} +1 -1
  30. package/dist/interactive-BaMAq88Q.js +11 -0
  31. package/dist/interactive-BaMAq88Q.js.map +1 -0
  32. package/dist/mcp-prompt-streaming-clGsVw8q.js +200 -0
  33. package/dist/mcp-prompt-streaming-clGsVw8q.js.map +1 -0
  34. package/dist/non-interactive-l2AKE3jD.js +12 -0
  35. package/dist/non-interactive-l2AKE3jD.js.map +1 -0
  36. package/dist/package-json-CumwmZpv.js +2 -0
  37. package/dist/{package-json-v_g2YlN1.js → package-json-Cynjr9k4.js} +1 -1
  38. package/dist/{package-json-v_g2YlN1.js.map → package-json-Cynjr9k4.js.map} +1 -1
  39. package/dist/{package-manager-DlTISyej.js → package-manager-BqsJK3ej.js} +2 -2
  40. package/dist/{package-manager-DlTISyej.js.map → package-manager-BqsJK3ej.js.map} +1 -1
  41. package/dist/{start-playground-Bxd2KG2L.js → playground-DlE5RNfE.js} +297 -8
  42. package/dist/playground-DlE5RNfE.js.map +1 -0
  43. package/dist/{posthog-B1G0raJU.js → posthog-DWL8uOcl.js} +1 -1
  44. package/dist/{posthog-B1G0raJU.js.map → posthog-DWL8uOcl.js.map} +1 -1
  45. package/dist/{posthog-integration-D-DyEJvz.js → posthog-integration-Bf_vtWI9.js} +229 -21
  46. package/dist/posthog-integration-Bf_vtWI9.js.map +1 -0
  47. package/dist/provisioning-BlBnlcFd.js +2 -0
  48. package/dist/{provisioning-DmN8ZDbE.js → provisioning-D_hAuxUN.js} +3 -3
  49. package/dist/{provisioning-DmN8ZDbE.js.map → provisioning-D_hAuxUN.js.map} +1 -1
  50. package/dist/{registry-CofBzIdU.js → registry-DKgYqROt.js} +5 -5
  51. package/dist/{registry-CofBzIdU.js.map → registry-DKgYqROt.js.map} +1 -1
  52. package/dist/setup-utils-BHZEdkNZ.js +2 -0
  53. package/dist/{setup-utils-_P-or31U.js → setup-utils-D-uTycLX.js} +24 -90
  54. package/dist/setup-utils-D-uTycLX.js.map +1 -0
  55. package/dist/skill-CnOQAZXp.js +29 -0
  56. package/dist/skill-CnOQAZXp.js.map +1 -0
  57. package/dist/{slides-D3I6JzlG.js → slides-CL1mv_Kq.js} +692 -68
  58. package/dist/slides-CL1mv_Kq.js.map +1 -0
  59. package/dist/{start-tui-Bl8fCbp_.js → start-tui-DXrv6cof.js} +470 -31
  60. package/dist/start-tui-DXrv6cof.js.map +1 -0
  61. package/dist/{steps-B-vmvb2V.js → steps-CgScwqso.js} +6 -6
  62. package/dist/{steps-B-vmvb2V.js.map → steps-CgScwqso.js.map} +1 -1
  63. package/dist/{task-stream-z6QFZtpC.js → task-stream-CF6QMVMv.js} +3 -3
  64. package/dist/{task-stream-z6QFZtpC.js.map → task-stream-CF6QMVMv.js.map} +1 -1
  65. package/dist/{telemetry-XO0SlTFs.js → telemetry-v6O12Bep.js} +2 -2
  66. package/dist/{telemetry-XO0SlTFs.js.map → telemetry-v6O12Bep.js.map} +1 -1
  67. package/dist/{wizard-abort-CuaS1eXn.js → wizard-abort-BGoBKgvC.js} +1 -1
  68. package/dist/{wizard-abort-uolun8Q3.js → wizard-abort-iTaJ8wC8.js} +3 -3
  69. package/dist/{wizard-abort-uolun8Q3.js.map → wizard-abort-iTaJ8wC8.js.map} +1 -1
  70. package/dist/{wizard-session-DxU5ZMBN.js → wizard-session-7tMjgOvP.js} +1 -1
  71. package/dist/{wizard-session-BlgiX-5d.js → wizard-session-gsn8Z3bZ.js} +5 -3
  72. package/dist/wizard-session-gsn8Z3bZ.js.map +1 -0
  73. package/dist/wizard-ui-YdGFRyu_.js.map +1 -1
  74. package/package.json +1 -1
  75. package/dist/TextBlock-B_8bXLLs.js.map +0 -1
  76. package/dist/agent-interface-yB_27jG8.js.map +0 -1
  77. package/dist/analytics-BnR9904x.js +0 -2
  78. package/dist/debug-DRKLej5r.js.map +0 -1
  79. package/dist/detection-0Pz2NncX.js +0 -206
  80. package/dist/detection-0Pz2NncX.js.map +0 -1
  81. package/dist/package-json-Cttzi3C8.js +0 -2
  82. package/dist/posthog-integration-D-DyEJvz.js.map +0 -1
  83. package/dist/provisioning-COeHnCVG.js +0 -2
  84. package/dist/setup-utils-C5iSJ3eg.js +0 -2
  85. package/dist/setup-utils-_P-or31U.js.map +0 -1
  86. package/dist/slides-D3I6JzlG.js.map +0 -1
  87. package/dist/start-playground-Bxd2KG2L.js.map +0 -1
  88. package/dist/start-tui-Bl8fCbp_.js.map +0 -1
  89. package/dist/wizard-session-BlgiX-5d.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { g as SERVICE_LABELS } from "./debug-DRKLej5r.js";
1
+ import { g as SERVICE_LABELS, s as logToFile } from "./debug-ubpO6102.js";
2
2
  import { n as isTaskStatus } from "./wizard-ui-YdGFRyu_.js";
3
- import { n as analytics, r as sessionProperties } from "./analytics-Da4QHjMw.js";
4
- import { i as buildSession } from "./wizard-session-BlgiX-5d.js";
5
- import { _ as AUDIT_SEVERITY_STYLE } from "./agent-interface-yB_27jG8.js";
6
- import { a as isObjectBlock, i as isLinesBlock, n as computeVisibleRange, o as Colors, r as isClearBlock, s as Icons, t as TextBlock } from "./TextBlock-B_8bXLLs.js";
7
- import { n as Program, r as getProgramConfig, s as getKindMeta, t as PROGRAM_REGISTRY } from "./bin.js";
8
- import { n as AVAILABLE_FEATURES, t as ALL_FEATURE_VALUES } from "./defaults-CPH6eWhN.js";
3
+ import { n as sessionProperties, t as analytics } from "./analytics-DUuUurR3.js";
4
+ import { i as buildSession } from "./wizard-session-gsn8Z3bZ.js";
5
+ import { _ as AUDIT_SEVERITY_STYLE } from "./agent-interface-B4eUlMso.js";
6
+ import { a as isObjectBlock, i as isLinesBlock, n as computeVisibleRange, o as Colors, r as isClearBlock, s as Icons, t as TextBlock } from "./TextBlock-CdeZog_6.js";
7
+ import { a as getProgramConfig, i as Program, l as getKindMeta, r as PROGRAM_REGISTRY } from "./bin.js";
8
+ import { n as AVAILABLE_FEATURES, t as ALL_FEATURE_VALUES } from "./defaults-zrYmZ2ID.js";
9
9
  import * as fs$1 from "fs";
10
10
  import { Box, Text, measureElement, useInput, useStdout } from "ink";
11
11
  import { Component, Fragment, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -274,6 +274,14 @@ var WizardStore = class {
274
274
  analytics.wizardCapture("auth complete", { project_id: credentials?.projectId });
275
275
  this.emitChange();
276
276
  }
277
+ setRoleAtOrganization(role) {
278
+ this.$session.setKey("roleAtOrganization", role);
279
+ this.emitChange();
280
+ }
281
+ setApiUser(user) {
282
+ this.$session.setKey("apiUser", user);
283
+ this.emitChange();
284
+ }
277
285
  setFrameworkConfig(integration, config) {
278
286
  this.$session.setKey("integration", integration);
279
287
  this.$session.setKey("frameworkConfig", config);
@@ -466,6 +474,10 @@ var WizardStore = class {
466
474
  });
467
475
  this.emitChange();
468
476
  }
477
+ setMcpSuggestedPromptsDismissed() {
478
+ this.$session.setKey("mcpSuggestedPromptsDismissed", true);
479
+ this.emitChange();
480
+ }
469
481
  setOutroDismissed() {
470
482
  this.$session.setKey("outroDismissed", true);
471
483
  this.emitChange();
@@ -475,9 +487,15 @@ var WizardStore = class {
475
487
  this.emitChange();
476
488
  }
477
489
  setDashboardUrl(url) {
490
+ logToFile(`store.setDashboardUrl: ${url}`);
478
491
  this.$session.setKey("dashboardUrl", url);
479
492
  this.emitChange();
480
493
  }
494
+ setNotebookUrl(url) {
495
+ logToFile(`store.setNotebookUrl: ${url}`);
496
+ this.$session.setKey("notebookUrl", url);
497
+ this.emitChange();
498
+ }
481
499
  setFrameworkContext(key, value) {
482
500
  const ctx = {
483
501
  ...this.$session.get().frameworkContext,
@@ -774,22 +792,18 @@ function deduplicateAndSort(hints) {
774
792
  * KeyboardHintsProvider — Context for collecting and displaying keyboard hints.
775
793
  *
776
794
  * Input components register their hints via useKeyBindings. The provider
777
- * flattens, deduplicates, and sorts them. It auto-dismisses 3s after the
778
- * first keypress and resets when the hint set changes (screen navigation).
795
+ * flattens, deduplicates, and sorts them. The hints bar stays visible for as
796
+ * long as a screen has registered hints it never auto-dismisses.
779
797
  */
780
798
  const KeyboardHintsContext = createContext({
781
799
  register: () => void 0,
782
800
  unregister: () => void 0,
783
- hints: [],
784
- visible: false
801
+ hints: []
785
802
  });
786
803
  const useKeyboardHintsContext = () => useContext(KeyboardHintsContext);
787
- const DISMISS_DELAY = 3e3;
788
804
  const KeyboardHintsProvider = ({ children }) => {
789
805
  const registrationsRef = useRef(/* @__PURE__ */ new Map());
790
806
  const [hints, setHints] = useState([]);
791
- const [visible, setVisible] = useState(true);
792
- const timerRef = useRef(null);
793
807
  const prevHintsKeyRef = useRef("");
794
808
  const recompute = useCallback(() => {
795
809
  const all = [];
@@ -799,13 +813,6 @@ const KeyboardHintsProvider = ({ children }) => {
799
813
  if (newKey !== prevHintsKeyRef.current) {
800
814
  prevHintsKeyRef.current = newKey;
801
815
  setHints(deduped);
802
- if (newKey.length > 0) {
803
- setVisible(true);
804
- if (timerRef.current) {
805
- clearTimeout(timerRef.current);
806
- timerRef.current = null;
807
- }
808
- }
809
816
  }
810
817
  }, []);
811
818
  const register = useCallback((id, h) => {
@@ -816,25 +823,11 @@ const KeyboardHintsProvider = ({ children }) => {
816
823
  registrationsRef.current.delete(id);
817
824
  recompute();
818
825
  }, [recompute]);
819
- useInput(() => {
820
- if (!visible) return;
821
- if (timerRef.current) return;
822
- timerRef.current = setTimeout(() => {
823
- setVisible(false);
824
- timerRef.current = null;
825
- }, DISMISS_DELAY);
826
- });
827
- useEffect(() => {
828
- return () => {
829
- if (timerRef.current) clearTimeout(timerRef.current);
830
- };
831
- }, []);
832
826
  return /* @__PURE__ */ jsx(KeyboardHintsContext.Provider, {
833
827
  value: {
834
828
  register,
835
829
  unregister,
836
- hints,
837
- visible
830
+ hints
838
831
  },
839
832
  children
840
833
  });
@@ -888,12 +881,13 @@ function useKeyBindings(id, bindings) {
888
881
  * Key bindings are declared via useKeyBindings, which auto-registers
889
882
  * hints in the KeyboardHintsBar.
890
883
  */
891
- const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, onSelect }) => {
884
+ const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
892
885
  if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
893
886
  message,
894
887
  options,
895
888
  centered,
896
889
  columns,
890
+ optionMarginBottom,
897
891
  onSelect
898
892
  });
899
893
  return /* @__PURE__ */ jsx(SinglePickerMenu, {
@@ -901,11 +895,12 @@ const PickerMenu = ({ message, options, mode = "single", centered = false, colum
901
895
  options,
902
896
  centered,
903
897
  columns,
898
+ optionMarginBottom,
904
899
  onSelect
905
900
  });
906
901
  };
907
902
  /** Custom single-select with triangle indicator and accent highlight. */
908
- const SinglePickerMenu = ({ message, options, centered = false, columns = 1, onSelect }) => {
903
+ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
909
904
  const [focused, setFocused] = useState(0);
910
905
  const rows = Math.ceil(options.length / columns);
911
906
  const bindings = [{
@@ -966,6 +961,7 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, onS
966
961
  const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
967
962
  return /* @__PURE__ */ jsxs(Box, {
968
963
  gap: 1,
964
+ marginBottom: optionMarginBottom,
969
965
  children: [/* @__PURE__ */ jsx(Text, {
970
966
  color: isFocused ? Colors.accent : void 0,
971
967
  dimColor: !isFocused,
@@ -983,7 +979,7 @@ const SinglePickerMenu = ({ message, options, centered = false, columns = 1, onS
983
979
  });
984
980
  };
985
981
  /** Custom multi-select with checkbox glyphs and accent highlight. */
986
- const MultiPickerMenu = ({ message, options, centered = false, columns = 1, onSelect }) => {
982
+ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, optionMarginBottom = 0, onSelect }) => {
987
983
  const [focused, setFocused] = useState(0);
988
984
  const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
989
985
  const rows = Math.ceil(options.length / columns);
@@ -1067,6 +1063,7 @@ const MultiPickerMenu = ({ message, options, centered = false, columns = 1, onSe
1067
1063
  const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
1068
1064
  return /* @__PURE__ */ jsxs(Box, {
1069
1065
  gap: 1,
1066
+ marginBottom: optionMarginBottom,
1070
1067
  children: [/* @__PURE__ */ jsx(Text, {
1071
1068
  color: isSelected ? "white" : Colors.muted,
1072
1069
  dimColor: !isFocused && !isSelected,
@@ -1441,40 +1438,92 @@ const ModalOverlay = ({ borderColor, title, titleColor, width = 68, children, fe
1441
1438
  /**
1442
1439
  * LogViewer — Real-time log tail, pinned to available terminal height.
1443
1440
  * Only renders the last N lines that fit on screen.
1441
+ *
1442
+ * Reads only the last TAIL_BYTES of the file on each refresh and throttles
1443
+ * fs.watch callbacks to one refresh per WATCH_THROTTLE_MS. Without this,
1444
+ * fs.readFileSync allocates a string the size of the entire log on every
1445
+ * append — during subagent fan-out that's tens of writes per second against
1446
+ * a log that grows into the hundreds of MB, producing OOM-grade allocation
1447
+ * pressure on V8's heap.
1444
1448
  */
1445
1449
  /** Rows consumed by TitleBar + spacer + ScreenContainer padding + status bar + tab bar */
1446
1450
  const CHROME_ROWS$1 = 8;
1451
+ /** Bytes read from the end of the log per refresh — large enough to contain
1452
+ * any practical visible window of lines, small enough to allocate cheaply. */
1453
+ const TAIL_BYTES = 256 * 1024;
1454
+ /** Minimum gap between watch-triggered refreshes. fs.watch fires on every
1455
+ * append */
1456
+ const WATCH_THROTTLE_MS = 250;
1457
+ function readTailLines(filePath, visibleLines) {
1458
+ const stat = fs$1.statSync(filePath);
1459
+ if (stat.size === 0) return [];
1460
+ const start = Math.max(0, stat.size - TAIL_BYTES);
1461
+ const length = stat.size - start;
1462
+ const buf = Buffer.alloc(length);
1463
+ const fd = fs$1.openSync(filePath, "r");
1464
+ try {
1465
+ fs$1.readSync(fd, buf, 0, length, start);
1466
+ } finally {
1467
+ fs$1.closeSync(fd);
1468
+ }
1469
+ let text = buf.toString("utf-8");
1470
+ if (start > 0) {
1471
+ const firstNewline = text.indexOf("\n");
1472
+ if (firstNewline >= 0) text = text.slice(firstNewline + 1);
1473
+ }
1474
+ return text.split("\n").slice(-visibleLines);
1475
+ }
1447
1476
  const LogViewer = ({ filePath, height }) => {
1448
1477
  const [, rows] = useStdoutDimensions();
1449
1478
  const visibleLines = height ?? Math.max(5, rows - CHROME_ROWS$1);
1450
1479
  const [lines, setLines] = useState([]);
1451
1480
  useEffect(() => {
1481
+ let lastReadAt = 0;
1482
+ let pendingTimer;
1452
1483
  const readTail = () => {
1453
1484
  try {
1454
- setLines(fs$1.readFileSync(filePath, "utf-8").split("\n").slice(-visibleLines));
1485
+ setLines(readTailLines(filePath, visibleLines));
1455
1486
  } catch {
1456
1487
  setLines(["(No log file found)"]);
1457
1488
  }
1458
1489
  };
1490
+ const scheduleRead = () => {
1491
+ const now = Date.now();
1492
+ const elapsed = now - lastReadAt;
1493
+ if (elapsed >= WATCH_THROTTLE_MS) {
1494
+ lastReadAt = now;
1495
+ readTail();
1496
+ return;
1497
+ }
1498
+ if (pendingTimer) return;
1499
+ pendingTimer = setTimeout(() => {
1500
+ pendingTimer = void 0;
1501
+ lastReadAt = Date.now();
1502
+ readTail();
1503
+ }, WATCH_THROTTLE_MS - elapsed);
1504
+ };
1459
1505
  readTail();
1506
+ lastReadAt = Date.now();
1460
1507
  let watcher;
1461
1508
  try {
1462
- watcher = fs$1.watch(filePath, () => {
1463
- readTail();
1464
- });
1509
+ watcher = fs$1.watch(filePath, () => scheduleRead());
1465
1510
  } catch {
1466
1511
  const interval = setInterval(() => {
1467
1512
  try {
1468
1513
  fs$1.accessSync(filePath);
1469
1514
  readTail();
1470
1515
  clearInterval(interval);
1471
- watcher = fs$1.watch(filePath, () => readTail());
1516
+ watcher = fs$1.watch(filePath, () => scheduleRead());
1472
1517
  } catch {}
1473
1518
  }, 1e3);
1474
- return () => clearInterval(interval);
1519
+ return () => {
1520
+ clearInterval(interval);
1521
+ if (pendingTimer) clearTimeout(pendingTimer);
1522
+ };
1475
1523
  }
1476
1524
  return () => {
1477
1525
  watcher?.close();
1526
+ if (pendingTimer) clearTimeout(pendingTimer);
1478
1527
  };
1479
1528
  }, [filePath, visibleLines]);
1480
1529
  return /* @__PURE__ */ jsx(Box, {
@@ -1656,16 +1705,15 @@ const DissolveTransition = ({ transitionKey, width, height, children, direction
1656
1705
  /**
1657
1706
  * KeyboardHintsBar — Row showing active keyboard shortcuts.
1658
1707
  *
1659
- * Always reserves its row to prevent layout shift. When hints are
1660
- * visible, renders them in dimmed grey text. When dismissed, renders
1661
- * an empty reserved row.
1708
+ * Always reserves its row to prevent layout shift, and always renders the
1709
+ * active hints (in dimmed grey text) while a screen has registered them.
1662
1710
  */
1663
1711
  const KeyboardHintsBar = () => {
1664
- const { hints, visible } = useKeyboardHintsContext();
1712
+ const { hints } = useKeyboardHintsContext();
1665
1713
  return /* @__PURE__ */ jsx(Box, {
1666
1714
  height: 1,
1667
1715
  paddingX: 1,
1668
- children: visible && hints.length > 0 && hints.map((hint, i) => /* @__PURE__ */ jsxs(Box, {
1716
+ children: hints.map((hint, i) => /* @__PURE__ */ jsxs(Box, {
1669
1717
  marginRight: i < hints.length - 1 ? 2 : 0,
1670
1718
  children: [/* @__PURE__ */ jsx(Text, {
1671
1719
  bold: true,
@@ -2184,7 +2232,8 @@ const BlockRenderer = ({ block, active, completed, onComplete, mode, bullet, ani
2184
2232
  animationInterval: block.animationInterval ?? animationInterval,
2185
2233
  sentenceInterval: block.sentenceInterval ?? sentenceInterval,
2186
2234
  maxHeight,
2187
- availableWidth
2235
+ availableWidth,
2236
+ dimWhenComplete: block.dimWhenComplete ?? true
2188
2237
  });
2189
2238
  return /* @__PURE__ */ jsx(NodeBlock, {
2190
2239
  content: block.content,
@@ -2622,10 +2671,10 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2622
2671
  return /* @__PURE__ */ jsxs(Box, {
2623
2672
  flexDirection: "column",
2624
2673
  flexGrow: 1,
2625
- children: [/* @__PURE__ */ jsxs(Text, {
2674
+ children: [/* @__PURE__ */ jsx(Text, {
2626
2675
  bold: true,
2627
2676
  color: Colors.accent,
2628
- children: ["MCP Server ", isRemove ? "Removal" : "Setup"]
2677
+ children: isRemove ? "Remove the PostHog MCP" : "Install the MCP so you can chat to your data"
2629
2678
  }), /* @__PURE__ */ jsxs(Box, {
2630
2679
  marginTop: 1,
2631
2680
  flexDirection: "column",
@@ -2642,19 +2691,38 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2642
2691
  " MCP clients detected. Skipping..."
2643
2692
  ]
2644
2693
  }),
2645
- phase === "ask" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Text, {
2646
- dimColor: true,
2647
- children: ["Detected: ", clients.map((c) => c.name).join(", ")]
2648
- }), /* @__PURE__ */ jsx(Box, {
2649
- marginTop: 1,
2650
- children: /* @__PURE__ */ jsx(ConfirmationInput, {
2651
- message: `${isRemove ? "Remove" : "Install"} the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
2652
- confirmLabel: isRemove ? "Remove" : "Install",
2653
- cancelLabel: "No thanks",
2654
- onConfirm: handleConfirm,
2655
- onCancel: handleSkip
2694
+ phase === "ask" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
2695
+ !isRemove && /* @__PURE__ */ jsx(Box, {
2696
+ flexDirection: "column",
2697
+ marginBottom: 1,
2698
+ children: [
2699
+ "Ask your agent: \"List my feature flags\" — and it does.",
2700
+ "Run SQL, build dashboards, ship flags, all from your IDE.",
2701
+ "No copy-pasting tokens or context. Your agent has the keys."
2702
+ ].map((bullet) => /* @__PURE__ */ jsxs(Text, {
2703
+ dimColor: true,
2704
+ children: [
2705
+ "•",
2706
+ " ",
2707
+ bullet
2708
+ ]
2709
+ }, bullet))
2710
+ }),
2711
+ /* @__PURE__ */ jsxs(Text, {
2712
+ dimColor: true,
2713
+ children: ["Detected: ", clients.map((c) => c.name).join(", ")]
2714
+ }),
2715
+ /* @__PURE__ */ jsx(Box, {
2716
+ marginTop: 1,
2717
+ children: /* @__PURE__ */ jsx(ConfirmationInput, {
2718
+ message: `${isRemove ? "Remove" : "Install"} the PostHog MCP server${clients.some((c) => c.supportsPlugin) ? " and plugin" : ""}?`,
2719
+ confirmLabel: isRemove ? "Remove" : "Install",
2720
+ cancelLabel: "No thanks",
2721
+ onConfirm: handleConfirm,
2722
+ onCancel: handleSkip
2723
+ })
2656
2724
  })
2657
- })] }),
2725
+ ] }),
2658
2726
  phase === "pick" && /* @__PURE__ */ jsx(PickerMenu, {
2659
2727
  message: "Select editor to install MCP server",
2660
2728
  options: clients.map((c) => ({
@@ -2706,6 +2774,490 @@ const McpScreen = ({ store, installer, mode = "install" }) => {
2706
2774
  });
2707
2775
  };
2708
2776
  //#endregion
2777
+ //#region src/lib/mcp-role-prompts.ts
2778
+ /**
2779
+ * Roles that ship from `role_at_organization` on the PostHog user object.
2780
+ * `security` isn't in the enum upstream — the engineering kit covers that audience.
2781
+ */
2782
+ const TAILORED_ROLES = [
2783
+ "founder",
2784
+ "product",
2785
+ "leadership",
2786
+ "marketing",
2787
+ "engineering",
2788
+ "data"
2789
+ ];
2790
+ const STOCK_MCP_SUGGESTED_PROMPTS = [
2791
+ {
2792
+ prompt: "What are my busiest 10 events and when did each last fire?",
2793
+ description: "Inventories your project’s event stream so you can see what’s being captured at a glance."
2794
+ },
2795
+ {
2796
+ prompt: "Show me daily event volume for the last 30 days.",
2797
+ description: "Charts your event count day by day — a quick read on volume and trend."
2798
+ },
2799
+ {
2800
+ prompt: "Create a dashboard with my top 10 events broken down by day for the last 7 days.",
2801
+ description: "Builds a saved dashboard you can pin and share — written back to your project."
2802
+ }
2803
+ ];
2804
+ //#endregion
2805
+ //#region src/ui/tui/screens/McpSuggestedPromptsScreen.tsx
2806
+ /**
2807
+ * McpSuggestedPromptsScreen — shown after MCP install succeeds in the
2808
+ * standalone `wizard mcp add` program.
2809
+ *
2810
+ * Phases:
2811
+ * 1. Choose — opens with a Log in / Exit picker, framed by a
2812
+ * hardcoded teaser of three example prompts.
2813
+ * 2. Authenticating — runs `services.performLogin()` (OAuth in
2814
+ * production, canned values in the playground).
2815
+ * Renders a spinner + login URL inline while the
2816
+ * promise is pending. Errors return to Choose
2817
+ * with an inline error line.
2818
+ * 3. PromptPicker — lists the role-tailored kit; user picks one to
2819
+ * run. The picker has its own "Exit" entry so
2820
+ * dismissal is discoverable without a hidden
2821
+ * hotkey.
2822
+ * 4. Running — streams the agent's response inline via
2823
+ * `services.runPromptStreaming`. `[esc]` aborts
2824
+ * and returns to the picker; `[enter]` after
2825
+ * completion goes back to the picker so the user
2826
+ * can run another or exit.
2827
+ *
2828
+ * Credentials are guaranteed non-null once PromptPicker / Running are
2829
+ * reached (the Choose → Authenticating gate forces a successful login
2830
+ * before getting there). A defensive throw protects the Running
2831
+ * useEffect against a state-machine bug.
2832
+ */
2833
+ const MAX_PROMPT_RUNS = 3;
2834
+ const McpSuggestedPromptsScreen = ({ store, services }) => {
2835
+ useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
2836
+ const session = store.session;
2837
+ const kit = STOCK_MCP_SUGGESTED_PROMPTS;
2838
+ const [phase, setPhase] = useState("choose");
2839
+ const [loginError, setLoginError] = useState(null);
2840
+ const [runningPrompt, setRunningPrompt] = useState(null);
2841
+ const [runChunks, setRunChunks] = useState([]);
2842
+ const [runStartedAt, setRunStartedAt] = useState(null);
2843
+ const [runCount, setRunCount] = useState(0);
2844
+ const canPickAnother = runCount < MAX_PROMPT_RUNS;
2845
+ const runAbortRef = useRef(null);
2846
+ useEffect(() => {
2847
+ if (phase !== "authenticating") return;
2848
+ let cancelled = false;
2849
+ (async () => {
2850
+ try {
2851
+ const { credentials, roleAtOrganization, user } = await services.performLogin();
2852
+ if (cancelled) return;
2853
+ store.setCredentials(credentials);
2854
+ store.setRoleAtOrganization(roleAtOrganization);
2855
+ store.setApiUser(user);
2856
+ store.setLoginUrl(null);
2857
+ setPhase("prompt-picker");
2858
+ } catch (err) {
2859
+ if (cancelled) return;
2860
+ const message = err instanceof Error ? err.message : String(err);
2861
+ logToFile(`[McpSuggestedPromptsScreen] login failed: ${message}`);
2862
+ store.setLoginUrl(null);
2863
+ setLoginError(message);
2864
+ setPhase("choose");
2865
+ }
2866
+ })();
2867
+ return () => {
2868
+ cancelled = true;
2869
+ };
2870
+ }, [
2871
+ phase,
2872
+ services,
2873
+ store
2874
+ ]);
2875
+ useEffect(() => {
2876
+ if (phase !== "running") return;
2877
+ if (!runningPrompt) return;
2878
+ if (!session.credentials) throw new Error("[McpSuggestedPromptsScreen] Running phase reached without credentials. The Choose gate should have prevented this.");
2879
+ const controller = new AbortController();
2880
+ runAbortRef.current = controller;
2881
+ const startedAt = Date.now();
2882
+ setRunStartedAt(startedAt);
2883
+ setRunChunks([]);
2884
+ (async () => {
2885
+ const credentials = session.credentials;
2886
+ if (!credentials) return;
2887
+ try {
2888
+ for await (const chunk of services.runPromptStreaming({
2889
+ prompt: runningPrompt,
2890
+ credentials,
2891
+ signal: controller.signal
2892
+ })) {
2893
+ if (controller.signal.aborted) return;
2894
+ setRunChunks((prev) => [...prev, chunk]);
2895
+ if (chunk.kind === "done") {
2896
+ analytics.wizardCapture("mcp suggested prompts run", {
2897
+ prompt: runningPrompt,
2898
+ durationMs: Date.now() - startedAt
2899
+ });
2900
+ return;
2901
+ }
2902
+ if (chunk.kind === "error") {
2903
+ analytics.wizardCapture("mcp suggested prompts run failed", {
2904
+ prompt: runningPrompt,
2905
+ error: chunk.text
2906
+ });
2907
+ return;
2908
+ }
2909
+ }
2910
+ } catch (err) {
2911
+ if (controller.signal.aborted) return;
2912
+ const text = err instanceof Error ? err.message : String(err);
2913
+ setRunChunks((prev) => [...prev, {
2914
+ kind: "error",
2915
+ text
2916
+ }]);
2917
+ analytics.wizardCapture("mcp suggested prompts run failed", {
2918
+ prompt: runningPrompt,
2919
+ error: text
2920
+ });
2921
+ }
2922
+ })();
2923
+ return () => {
2924
+ controller.abort();
2925
+ if (runAbortRef.current === controller) runAbortRef.current = null;
2926
+ };
2927
+ }, [
2928
+ phase,
2929
+ runningPrompt,
2930
+ services,
2931
+ session.credentials
2932
+ ]);
2933
+ const dismiss = () => {
2934
+ setPhase("done");
2935
+ setTimeout(() => {
2936
+ store.setMcpSuggestedPromptsDismissed();
2937
+ }, 0);
2938
+ };
2939
+ const handleChoice = (value) => {
2940
+ const choice = Array.isArray(value) ? value[0] : value;
2941
+ setLoginError(null);
2942
+ if (choice === "login") {
2943
+ analytics.wizardCapture("mcp suggested prompts choose", { choice: "login" });
2944
+ setPhase("authenticating");
2945
+ } else {
2946
+ analytics.wizardCapture("mcp suggested prompts choose", { choice: "exit" });
2947
+ dismiss();
2948
+ }
2949
+ };
2950
+ const handlePromptPick = (value) => {
2951
+ setRunningPrompt(Array.isArray(value) ? value[0] : value);
2952
+ setRunCount((c) => c + 1);
2953
+ setPhase("running");
2954
+ };
2955
+ useKeyBindings("mcp-suggested-prompts", [{
2956
+ match: "escape",
2957
+ label: "esc",
2958
+ action: phase === "prompt-picker" ? "exit" : "exit",
2959
+ handler: () => {
2960
+ if (phase === "running") {
2961
+ runAbortRef.current?.abort();
2962
+ dismiss();
2963
+ } else if (phase === "prompt-picker") dismiss();
2964
+ }
2965
+ }, {
2966
+ match: "p",
2967
+ label: "p",
2968
+ action: canPickAnother ? "pick new prompt" : "cap reached",
2969
+ handler: () => {
2970
+ if (phase !== "running") return;
2971
+ if (!canPickAnother) return;
2972
+ runAbortRef.current?.abort();
2973
+ setPhase("prompt-picker");
2974
+ }
2975
+ }]);
2976
+ return /* @__PURE__ */ jsx(Box, {
2977
+ flexDirection: "column",
2978
+ flexGrow: 1,
2979
+ children: /* @__PURE__ */ jsxs(Box, {
2980
+ marginTop: 1,
2981
+ flexDirection: "column",
2982
+ children: [
2983
+ phase === "choose" && /* @__PURE__ */ jsx(ChoosePhase, {
2984
+ error: loginError,
2985
+ onSelect: handleChoice
2986
+ }),
2987
+ phase === "authenticating" && /* @__PURE__ */ jsx(AuthenticatingPhase, { loginUrl: session.loginUrl }),
2988
+ phase === "prompt-picker" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Box, {
2989
+ marginBottom: 1,
2990
+ children: /* @__PURE__ */ jsx(Text, {
2991
+ bold: true,
2992
+ color: Colors.accent,
2993
+ children: "MCP tutorial"
2994
+ })
2995
+ }), /* @__PURE__ */ jsx(PromptPickerPhase, {
2996
+ promptKit: kit,
2997
+ userDisplayName: session.apiUser?.first_name || null,
2998
+ onSelect: handlePromptPick
2999
+ })] }),
3000
+ phase === "running" && runningPrompt && /* @__PURE__ */ jsx(RunningPhase, {
3001
+ prompt: runningPrompt,
3002
+ chunks: runChunks,
3003
+ startedAt: runStartedAt,
3004
+ canPickAnother,
3005
+ runCount,
3006
+ maxRuns: MAX_PROMPT_RUNS
3007
+ })
3008
+ ]
3009
+ })
3010
+ });
3011
+ };
3012
+ const ChoosePhase = ({ error, onSelect }) => /* @__PURE__ */ jsxs(Box, {
3013
+ flexDirection: "column",
3014
+ children: [
3015
+ /* @__PURE__ */ jsx(Text, {
3016
+ bold: true,
3017
+ color: Colors.accent,
3018
+ children: "PostHog MCP"
3019
+ }),
3020
+ /* @__PURE__ */ jsx(Box, {
3021
+ marginTop: 1,
3022
+ children: /* @__PURE__ */ jsx(Text, { children: "With MCP your agent works directly with the PostHog platform. You can prompt it to:" })
3023
+ }),
3024
+ /* @__PURE__ */ jsxs(Box, {
3025
+ marginTop: 1,
3026
+ flexDirection: "column",
3027
+ children: [
3028
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3029
+ color: "cyan",
3030
+ children: Icons.diamond
3031
+ }), " Build dashboards"] }),
3032
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3033
+ color: "cyan",
3034
+ children: Icons.diamond
3035
+ }), " Run SQL queries"] }),
3036
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3037
+ color: "cyan",
3038
+ children: Icons.diamond
3039
+ }), " Deploy feature flags"] }),
3040
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3041
+ color: "cyan",
3042
+ children: Icons.diamond
3043
+ }), " Debug exceptions and errors"] }),
3044
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3045
+ color: "cyan",
3046
+ children: Icons.diamond
3047
+ }), " And lots more..."] })
3048
+ ]
3049
+ }),
3050
+ /* @__PURE__ */ jsx(Box, {
3051
+ marginTop: 1,
3052
+ children: /* @__PURE__ */ jsx(Text, { children: "Want a live demo using real data from your project?" })
3053
+ }),
3054
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(PickerMenu, {
3055
+ options: [{
3056
+ label: "Start MCP tutorial",
3057
+ value: "login"
3058
+ }, {
3059
+ label: "Exit",
3060
+ value: "exit"
3061
+ }],
3062
+ onSelect
3063
+ }) }),
3064
+ error && /* @__PURE__ */ jsx(Box, {
3065
+ marginTop: 1,
3066
+ children: /* @__PURE__ */ jsxs(Text, {
3067
+ color: "red",
3068
+ children: [
3069
+ "Login failed: ",
3070
+ error,
3071
+ ". Try again or exit."
3072
+ ]
3073
+ })
3074
+ })
3075
+ ]
3076
+ });
3077
+ const AuthenticatingPhase = ({ loginUrl }) => /* @__PURE__ */ jsxs(Box, {
3078
+ flexDirection: "column",
3079
+ children: [/* @__PURE__ */ jsx(LoadingBox, { message: "Waiting for authentication..." }), loginUrl && /* @__PURE__ */ jsx(Box, {
3080
+ marginTop: 1,
3081
+ marginBottom: 1,
3082
+ flexDirection: "column",
3083
+ children: /* @__PURE__ */ jsxs(Text, { children: [
3084
+ /* @__PURE__ */ jsx(Text, {
3085
+ dimColor: true,
3086
+ children: "If the browser didn't open, copy and paste:"
3087
+ }),
3088
+ "\n\n",
3089
+ /* @__PURE__ */ jsx(Text, {
3090
+ color: "cyan",
3091
+ children: loginUrl
3092
+ })
3093
+ ] })
3094
+ })]
3095
+ });
3096
+ const PromptPickerPhase = ({ promptKit, userDisplayName, onSelect }) => {
3097
+ const options = promptKit.map((p) => ({
3098
+ label: p.prompt,
3099
+ value: p.prompt
3100
+ }));
3101
+ return /* @__PURE__ */ jsx(Box, {
3102
+ flexDirection: "column",
3103
+ children: /* @__PURE__ */ jsx(ContentSequencer, {
3104
+ blocks: [
3105
+ {
3106
+ content: `Hello there, ${userDisplayName || "there"}!`,
3107
+ mode: 0,
3108
+ animationInterval: 100,
3109
+ pause: 1200,
3110
+ dimWhenComplete: false
3111
+ },
3112
+ {
3113
+ content: "Pick a prompt to see the PostHog MCP in action.",
3114
+ mode: 0,
3115
+ animationInterval: 50,
3116
+ pause: 1e3,
3117
+ dimWhenComplete: false
3118
+ },
3119
+ {
3120
+ content: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PickerMenu, {
3121
+ options,
3122
+ optionMarginBottom: 1,
3123
+ onSelect
3124
+ }), /* @__PURE__ */ jsx(Box, {
3125
+ marginTop: 2,
3126
+ children: /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3127
+ bold: true,
3128
+ children: "[esc]"
3129
+ }), /* @__PURE__ */ jsx(Text, { children: " to exit" })] })
3130
+ })] }),
3131
+ persist: true
3132
+ }
3133
+ ],
3134
+ mode: 0,
3135
+ blockInterval: 350
3136
+ })
3137
+ });
3138
+ };
3139
+ const RunningPhase = ({ prompt, chunks, startedAt, canPickAnother, runCount, maxRuns }) => {
3140
+ const isDone = chunks.some((c) => c.kind === "done");
3141
+ const errorChunk = chunks.find((c) => c.kind === "error");
3142
+ const finished = isDone || !!errorChunk;
3143
+ const elapsed = startedAt ? Math.round((Date.now() - startedAt) / 1e3) : 0;
3144
+ const visibleChunks = finished ? chunks.filter((c) => c.kind === "text" || c.kind === "error") : chunks;
3145
+ const cappedChunks = finished ? capTextChunks(visibleChunks) : visibleChunks;
3146
+ return /* @__PURE__ */ jsxs(Box, {
3147
+ flexDirection: "column",
3148
+ children: [
3149
+ /* @__PURE__ */ jsxs(Text, { children: [
3150
+ /* @__PURE__ */ jsx(Text, {
3151
+ bold: true,
3152
+ children: "Prompt:"
3153
+ }),
3154
+ " ",
3155
+ /* @__PURE__ */ jsx(Text, {
3156
+ color: Colors.accent,
3157
+ children: prompt
3158
+ })
3159
+ ] }),
3160
+ /* @__PURE__ */ jsxs(Box, {
3161
+ marginTop: 1,
3162
+ gap: 1,
3163
+ children: [
3164
+ !finished && /* @__PURE__ */ jsx(Spinner, {}),
3165
+ /* @__PURE__ */ jsx(Text, {
3166
+ bold: finished,
3167
+ children: finished ? errorChunk ? `Failed after ${elapsed}s.` : `Done in ${elapsed}s.` : "Streaming from PostHog MCP"
3168
+ }),
3169
+ /* @__PURE__ */ jsxs(Text, {
3170
+ dimColor: true,
3171
+ children: [
3172
+ "(",
3173
+ runCount,
3174
+ "/",
3175
+ maxRuns,
3176
+ " prompts)"
3177
+ ]
3178
+ })
3179
+ ]
3180
+ }),
3181
+ /* @__PURE__ */ jsx(Box, {
3182
+ marginTop: 1,
3183
+ flexDirection: "column",
3184
+ children: cappedChunks.map((chunk, idx) => /* @__PURE__ */ jsx(ChunkLine, { chunk }, idx))
3185
+ }),
3186
+ finished && !canPickAnother && /* @__PURE__ */ jsx(Box, {
3187
+ marginTop: 1,
3188
+ children: /* @__PURE__ */ jsxs(Text, { children: [
3189
+ /* @__PURE__ */ jsxs(Text, {
3190
+ dimColor: true,
3191
+ children: [
3192
+ "You've hit the ",
3193
+ maxRuns,
3194
+ "-prompt tutorial cap. Press",
3195
+ " "
3196
+ ]
3197
+ }),
3198
+ /* @__PURE__ */ jsx(Text, {
3199
+ bold: true,
3200
+ dimColor: true,
3201
+ children: "[esc]"
3202
+ }),
3203
+ /* @__PURE__ */ jsx(Text, {
3204
+ dimColor: true,
3205
+ children: " to exit."
3206
+ })
3207
+ ] })
3208
+ })
3209
+ ]
3210
+ });
3211
+ };
3212
+ /**
3213
+ * Belt-and-suspenders fallback for runs where Claude ignored the
3214
+ * terminal-fit system prompt and produced an overlong response. Joins
3215
+ * all text chunks, slices to the last N lines that fit in the current
3216
+ * terminal, and prepends an indicator showing how many lines got cut.
3217
+ * Errors are preserved separately so failures don't disappear into the
3218
+ * truncation.
3219
+ */
3220
+ function capTextChunks(chunks) {
3221
+ const rows = process.stdout.rows ?? 24;
3222
+ const maxMessageRows = Math.max(6, rows - 8);
3223
+ const textChunks = chunks.filter((c) => c.kind === "text");
3224
+ const errors = chunks.filter((c) => c.kind === "error");
3225
+ if (textChunks.length === 0) return chunks;
3226
+ const lines = textChunks.map((c) => c.text).join("").split("\n");
3227
+ if (lines.length <= maxMessageRows) return chunks;
3228
+ const hidden = lines.length - maxMessageRows;
3229
+ const tail = lines.slice(-maxMessageRows).join("\n");
3230
+ return [{
3231
+ kind: "text",
3232
+ text: `[${hidden} line${hidden === 1 ? "" : "s"} above — expand terminal to see more]\n\n${tail}`
3233
+ }, ...errors];
3234
+ }
3235
+ const ChunkLine = ({ chunk }) => {
3236
+ if (chunk.kind === "text") return /* @__PURE__ */ jsx(Text, { children: chunk.text });
3237
+ if (chunk.kind === "tool-call") return /* @__PURE__ */ jsxs(Text, { children: [
3238
+ " ",
3239
+ /* @__PURE__ */ jsxs(Text, {
3240
+ color: "cyan",
3241
+ children: ["↳ ", chunk.toolName]
3242
+ }),
3243
+ chunk.detail ? ` ${chunk.detail}` : ""
3244
+ ] });
3245
+ if (chunk.kind === "tool-result") return /* @__PURE__ */ jsxs(Text, { children: [
3246
+ " ",
3247
+ /* @__PURE__ */ jsx(Text, {
3248
+ color: "green",
3249
+ children: "✓"
3250
+ }),
3251
+ " ",
3252
+ chunk.detail
3253
+ ] });
3254
+ if (chunk.kind === "error") return /* @__PURE__ */ jsxs(Text, {
3255
+ color: "red",
3256
+ children: ["Error: ", chunk.text]
3257
+ });
3258
+ return null;
3259
+ };
3260
+ //#endregion
2709
3261
  //#region src/ui/tui/screens/audit/AuditChecksViewer/AreaHeaderRow.tsx
2710
3262
  /** Sub-header row inside the scrollable body — one per area group. */
2711
3263
  const AreaHeaderRow = ({ area, resolved, total }) => /* @__PURE__ */ jsxs(Box, {
@@ -3317,11 +3869,83 @@ const EventCaptureSlide = {
3317
3869
  docsUrl: "https://posthog.com/docs/product-analytics/capture-events"
3318
3870
  };
3319
3871
  //#endregion
3872
+ //#region src/ui/tui/screens/audit/slides/writeReport.tsx
3873
+ const ReportVisual = () => /* @__PURE__ */ jsxs(VisualBox, { children: [
3874
+ /* @__PURE__ */ jsx(Text, {
3875
+ dimColor: true,
3876
+ children: "posthog-audit-report.md"
3877
+ }),
3878
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3879
+ dimColor: true,
3880
+ children: " # "
3881
+ }), /* @__PURE__ */ jsx(Text, { children: "Summary" })] }),
3882
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3883
+ dimColor: true,
3884
+ children: " # "
3885
+ }), /* @__PURE__ */ jsx(Text, { children: "Recommended actions" })] }),
3886
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3887
+ dimColor: true,
3888
+ children: " # "
3889
+ }), /* @__PURE__ */ jsx(Text, { children: "Full audit" })] })
3890
+ ] });
3891
+ const WriteReportSlide = {
3892
+ area: "Write report",
3893
+ intro: [
3894
+ "Now we write an audit report at ./posthog-audit-report.md. that summarizes our findings.",
3895
+ "The report leads with a summary, then a prioritized list of fixes with file:line citations, then every check we ran grouped by area so nothing is hidden.",
3896
+ "We will upload the report into a PostHog notebook in the next step."
3897
+ ],
3898
+ visual: /* @__PURE__ */ jsx(ReportVisual, {}),
3899
+ docsUrl: "https://posthog.com/docs/product-analytics/best-practices"
3900
+ };
3901
+ //#endregion
3902
+ //#region src/ui/tui/screens/audit/slides/uploadNotebook.tsx
3903
+ const NotebookVisual = () => /* @__PURE__ */ jsxs(VisualBox, { children: [
3904
+ /* @__PURE__ */ jsx(Text, {
3905
+ dimColor: true,
3906
+ children: "posthog-audit-report.md"
3907
+ }),
3908
+ /* @__PURE__ */ jsx(Text, {
3909
+ dimColor: true,
3910
+ children: " │"
3911
+ }),
3912
+ /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
3913
+ dimColor: true,
3914
+ children: " ▼ "
3915
+ }), /* @__PURE__ */ jsx(Text, {
3916
+ color: "cyan",
3917
+ children: "PostHog notebook"
3918
+ })] }),
3919
+ /* @__PURE__ */ jsx(Text, {
3920
+ dimColor: true,
3921
+ children: " # Summary"
3922
+ }),
3923
+ /* @__PURE__ */ jsx(Text, {
3924
+ dimColor: true,
3925
+ children: " # Recommended actions"
3926
+ }),
3927
+ /* @__PURE__ */ jsx(Text, {
3928
+ dimColor: true,
3929
+ children: " # Full audit"
3930
+ })
3931
+ ] });
3932
+ //#endregion
3320
3933
  //#region src/ui/tui/screens/audit/slides/index.ts
3321
3934
  const AUDIT_AREA_SLIDES = [
3322
3935
  InstallationSlide,
3323
3936
  IdentificationSlide,
3324
- EventCaptureSlide
3937
+ EventCaptureSlide,
3938
+ WriteReportSlide,
3939
+ {
3940
+ area: "Upload notebook",
3941
+ intro: [
3942
+ "Next we upload the report into a PostHog notebook so you can share it with your team as a URL.",
3943
+ "Hang tight.",
3944
+ "The markdown file on disk is still there for you to read locally."
3945
+ ],
3946
+ visual: /* @__PURE__ */ jsx(NotebookVisual, {}),
3947
+ docsUrl: "https://posthog.com/docs/notebooks"
3948
+ }
3325
3949
  ];
3326
3950
  //#endregion
3327
3951
  //#region src/ui/tui/screens/audit-3000/slides/eventQuality.tsx
@@ -3494,6 +4118,6 @@ const AUDIT_3000_AREA_SLIDES = [
3494
4118
  }
3495
4119
  ];
3496
4120
  //#endregion
3497
- export { ProgressList as C, WizardStore as D, CardLayout as E, useKeyBindings as S, SplitView as T, ModalOverlay as _, IssueTable as a, useStdoutDimensions as b, ServiceHealthList as c, ContentSequencer as d, HNViewer as f, LogViewer as g, EventPlanViewer as h, McpScreen as i, TipsCard as l, ScreenContainer as m, AUDIT_AREA_SLIDES as n, SEVERITY_LABEL as o, TabContainer as p, AuditChecksViewer as r, SEVERITY_ORDER as s, AUDIT_3000_AREA_SLIDES as t, LearnCard as u, ConfirmationInput as v, LoadingBox as w, PickerMenu as x, GroupedPickerMenu as y };
4121
+ export { WizardStore as A, useStdoutDimensions as C, LoadingBox as D, ProgressList as E, SplitView as O, GroupedPickerMenu as S, useKeyBindings as T, ScreenContainer as _, McpSuggestedPromptsScreen as a, ModalOverlay as b, IssueTable as c, ServiceHealthList as d, TipsCard as f, TabContainer as g, HNViewer as h, AuditChecksViewer as i, CardLayout as k, SEVERITY_LABEL as l, ContentSequencer as m, AUDIT_AREA_SLIDES as n, TAILORED_ROLES as o, LearnCard as p, VisualBox as r, McpScreen as s, AUDIT_3000_AREA_SLIDES as t, SEVERITY_ORDER as u, EventPlanViewer as v, PickerMenu as w, ConfirmationInput as x, LogViewer as y };
3498
4122
 
3499
- //# sourceMappingURL=slides-D3I6JzlG.js.map
4123
+ //# sourceMappingURL=slides-CL1mv_Kq.js.map