@posthog/wizard 2.25.0 → 2.27.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 (65) hide show
  1. package/README.md +14 -1
  2. package/dist/{add-mcp-server-to-clients-t0xe8gn1.js → add-mcp-server-to-clients-D2XNlVgw.js} +4 -4
  3. package/dist/{add-mcp-server-to-clients-t0xe8gn1.js.map → add-mcp-server-to-clients-D2XNlVgw.js.map} +1 -1
  4. package/dist/{agent-interface-BsuUUPle.js → agent-interface-DpkR1mbC.js} +39 -12
  5. package/dist/agent-interface-DpkR1mbC.js.map +1 -0
  6. package/dist/{agent-runner-L_-kJ3y3.js → agent-runner-D7hIITUf.js} +176 -167
  7. package/dist/agent-runner-D7hIITUf.js.map +1 -0
  8. package/dist/{analytics-CDOujOSQ.js → analytics-B7-uRKIJ.js} +2 -2
  9. package/dist/{analytics-CDOujOSQ.js.map → analytics-B7-uRKIJ.js.map} +1 -1
  10. package/dist/{api-DNS-L-1U.js → api-2zPZQONC.js} +9 -5
  11. package/dist/api-2zPZQONC.js.map +1 -0
  12. package/dist/bin.js +1160 -120
  13. package/dist/bin.js.map +1 -1
  14. package/dist/{ci-install-_9A7tL36.js → ci-install-CpGSFNDi.js} +5 -5
  15. package/dist/{ci-install-_9A7tL36.js.map → ci-install-CpGSFNDi.js.map} +1 -1
  16. package/dist/{debug-BwC7UkGH.js → debug-Br_xCc9s.js} +3 -2
  17. package/dist/{debug-BwC7UkGH.js.map → debug-Br_xCc9s.js.map} +1 -1
  18. package/dist/{debug-CZQcMAJT.js → debug-CDLYQOQh.js} +1 -1
  19. package/dist/{environment-DQPoj9sU.js → environment-CFXsie0G.js} +3 -3
  20. package/dist/{environment-DQPoj9sU.js.map → environment-CFXsie0G.js.map} +1 -1
  21. package/dist/file-utils-CHAj73KM.js +116 -0
  22. package/dist/file-utils-CHAj73KM.js.map +1 -0
  23. package/dist/{interactive-DT5dLd7N.js → interactive-lfAs6vF7.js} +3 -3
  24. package/dist/{interactive-DT5dLd7N.js.map → interactive-lfAs6vF7.js.map} +1 -1
  25. package/dist/{mcp-prompt-streaming-CBMr458Q.js → mcp-prompt-streaming-BHdAwwob.js} +4 -4
  26. package/dist/{mcp-prompt-streaming-CBMr458Q.js.map → mcp-prompt-streaming-BHdAwwob.js.map} +1 -1
  27. package/dist/{non-interactive-csP4yGdA.js → non-interactive--4CK1bkn.js} +2 -2
  28. package/dist/{non-interactive-csP4yGdA.js.map → non-interactive--4CK1bkn.js.map} +1 -1
  29. package/dist/{package-manager-CB4c2euf.js → package-manager-BlogZvIK.js} +2 -2
  30. package/dist/{package-manager-CB4c2euf.js.map → package-manager-BlogZvIK.js.map} +1 -1
  31. package/dist/{playground-C-lpKoKC.js → playground-De_BxaCh.js} +145 -48
  32. package/dist/playground-De_BxaCh.js.map +1 -0
  33. package/dist/{posthog-integration-BL8-vC0V.js → posthog-integration-DWs8JM8J.js} +12 -12
  34. package/dist/{posthog-integration-BL8-vC0V.js.map → posthog-integration-DWs8JM8J.js.map} +1 -1
  35. package/dist/{provisioning-DLOiFSM9.js → provisioning-CUwxxByi.js} +10 -6
  36. package/dist/{provisioning-DLOiFSM9.js.map → provisioning-CUwxxByi.js.map} +1 -1
  37. package/dist/{registry-BbRzCV5l.js → registry-CIjJsxDE.js} +4 -4
  38. package/dist/{registry-BbRzCV5l.js.map → registry-CIjJsxDE.js.map} +1 -1
  39. package/dist/{setup-utils-D87CyNkw.js → setup-utils-CjKjaKcG.js} +81 -16
  40. package/dist/setup-utils-CjKjaKcG.js.map +1 -0
  41. package/dist/{start-tui-DnAG57vY.js → start-tui-Cbw0kVr3.js} +471 -54
  42. package/dist/start-tui-Cbw0kVr3.js.map +1 -0
  43. package/dist/{steps-JaxH6u0f.js → steps-DUz5lHWu.js} +7 -6
  44. package/dist/{steps-JaxH6u0f.js.map → steps-DUz5lHWu.js.map} +1 -1
  45. package/dist/{telemetry-DL28cCwY.js → telemetry-D3CnLknq.js} +3 -3
  46. package/dist/{telemetry-DL28cCwY.js.map → telemetry-D3CnLknq.js.map} +1 -1
  47. package/dist/{AiOptInRequiredScreen-C-D9tN6r.js → terminal-DwAdsRPX.js} +1047 -85
  48. package/dist/terminal-DwAdsRPX.js.map +1 -0
  49. package/dist/{urls-vkJ5c0ix.js → urls-JN8mo6lU.js} +2 -2
  50. package/dist/{urls-vkJ5c0ix.js.map → urls-JN8mo6lU.js.map} +1 -1
  51. package/dist/{wizard-abort-BRXKRL4F.js → wizard-abort-BPr0xo7i.js} +1 -1
  52. package/dist/{wizard-abort-CLGgMAEe.js → wizard-abort-gHZ7kHYo.js} +3 -3
  53. package/dist/{wizard-abort-CLGgMAEe.js.map → wizard-abort-gHZ7kHYo.js.map} +1 -1
  54. package/dist/wizard-session-G3VWD6hv.js.map +1 -1
  55. package/dist/wizard-ui-WZ48rUgr.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/AiOptInRequiredScreen-C-D9tN6r.js.map +0 -1
  58. package/dist/agent-interface-BsuUUPle.js.map +0 -1
  59. package/dist/agent-runner-L_-kJ3y3.js.map +0 -1
  60. package/dist/api-DNS-L-1U.js.map +0 -1
  61. package/dist/file-utils-VAXoyXVA.js +0 -38
  62. package/dist/file-utils-VAXoyXVA.js.map +0 -1
  63. package/dist/playground-C-lpKoKC.js.map +0 -1
  64. package/dist/setup-utils-D87CyNkw.js.map +0 -1
  65. package/dist/start-tui-DnAG57vY.js.map +0 -1
@@ -1,14 +1,14 @@
1
- import { $ as getSkillsBaseUrl, M as POSTHOG_APP_URL, g as SERVICE_LABELS, s as logToFile, y as getBlockingServiceKeys } from "./debug-BwC7UkGH.js";
1
+ import { $ as getSkillsBaseUrl, M as POSTHOG_APP_URL, g as SERVICE_LABELS, s as logToFile, y as getBlockingServiceKeys } from "./debug-Br_xCc9s.js";
2
2
  import { n as isTaskStatus } from "./wizard-ui-WZ48rUgr.js";
3
- import { r as sessionProperties, t as analytics } from "./analytics-CDOujOSQ.js";
4
- import { i as withUtm, n as openTrackedLink } from "./telemetry-DL28cCwY.js";
5
- import { t as getOrAskForProjectData } from "./setup-utils-D87CyNkw.js";
6
- import { n as getCloudUrlFromRegion } from "./urls-vkJ5c0ix.js";
7
- import { a as fetchUserData, i as fetchSlackConnected } from "./api-DNS-L-1U.js";
3
+ import { r as sessionProperties, t as analytics } from "./analytics-B7-uRKIJ.js";
4
+ import { i as withUtm, n as openTrackedLink } from "./telemetry-D3CnLknq.js";
5
+ import { t as getOrAskForProjectData } from "./setup-utils-CjKjaKcG.js";
6
+ import { n as getCloudUrlFromRegion } from "./urls-JN8mo6lU.js";
7
+ import { a as fetchUserData, i as fetchSlackConnected } from "./api-2zPZQONC.js";
8
8
  import { i as buildSession } from "./wizard-session-G3VWD6hv.js";
9
- import { C as AUDIT_SEVERITY_STYLE, h as fetchSkillMenu } from "./agent-interface-BsuUUPle.js";
10
- import { c as computeVisibleRange, d as isObjectBlock, f as Colors, l as isClearBlock, p as Icons, s as TextBlock, u as isLinesBlock } from "./posthog-integration-BL8-vC0V.js";
11
- import { a as PromptLabel, c as PROGRAM_REGISTRY, i as useKeyboardHintsContext, l as Program, m as getKindMeta, n as useKeyBindings, r as KeyboardHintsProvider, t as PickerMenu, u as getProgramConfig } from "./bin.js";
9
+ import { C as AUDIT_SEVERITY_STYLE, h as fetchSkillMenu } from "./agent-interface-DpkR1mbC.js";
10
+ import { c as computeVisibleRange, d as isObjectBlock, f as Colors, l as isClearBlock, p as Icons, s as TextBlock, u as isLinesBlock } from "./posthog-integration-DWs8JM8J.js";
11
+ import { a as ConfirmButton, d as getProgramConfig, i as useKeyboardHintsContext, l as PROGRAM_REGISTRY, m as getKindMeta, n as useKeyBindings, o as PromptLabel, r as KeyboardHintsProvider, t as PickerMenu, u as Program } from "./bin.js";
12
12
  import { n as AVAILABLE_FEATURES, o as isAllFeaturesSelected, t as ALL_FEATURE_VALUES } from "./defaults-BNWIWzjc.js";
13
13
  import opn from "opn";
14
14
  import * as fs$1 from "fs";
@@ -205,6 +205,7 @@ var WizardStore = class {
205
205
  $learnCardBlockIdx = atom(0);
206
206
  $learnCardComplete = atom(false);
207
207
  $version = atom(0);
208
+ $currentStage = atom(null);
208
209
  _onTasksChanged = null;
209
210
  /** Last screen seen — used to detect screen transitions for analytics. */
210
211
  _lastScreen = null;
@@ -337,6 +338,19 @@ var WizardStore = class {
337
338
  get eventPlan() {
338
339
  return this.$eventPlan.get();
339
340
  }
341
+ get currentStage() {
342
+ return this.$currentStage.get();
343
+ }
344
+ /** No-op when the stage hasn't changed, so `startedAt` survives across
345
+ * re-renders and tab switches and measures real stage time. */
346
+ setCurrentStage(stage) {
347
+ if (this.$currentStage.get()?.stage === stage) return;
348
+ this.$currentStage.set({
349
+ stage,
350
+ startedAt: Date.now()
351
+ });
352
+ this.emitChange();
353
+ }
340
354
  get statusExpanded() {
341
355
  return this.$statusExpanded.get();
342
356
  }
@@ -874,8 +888,11 @@ function useStdoutDimensions() {
874
888
  * GroupedPickerMenu — Multi-select with category headers.
875
889
  *
876
890
  * Renders groups of options with bold category labels.
877
- * Arrow keys navigate selectable items (headers are skipped),
878
- * space toggles, "a" toggles all, enter submits.
891
+ * Arrow keys navigate selectable items (headers are skipped) and the Confirm
892
+ * button below them; enter toggles the focused option, "a" toggles all, and
893
+ * moving onto the Confirm button and pressing enter submits. Space is kept as
894
+ * an undocumented alias for enter. Shares the interaction model with
895
+ * PickerMenu mode="multi".
879
896
  *
880
897
  * When content exceeds available terminal height, the list scrolls
881
898
  * to keep the focused item visible with up/down indicators.
@@ -890,8 +907,12 @@ function truncateWithEllipsis(text, maxWidth) {
890
907
  }
891
908
  /** Rows consumed by chrome outside this component (title bar, screen padding, etc.) */
892
909
  const CHROME_OVERHEAD = 10;
893
- /** Rows used by the prompt label + marginTop before content (hint text moved to KeyboardHintsBar). */
894
- const MENU_CHROME = 2;
910
+ /**
911
+ * Rows used by the prompt label + marginTop before content (hint text moved to
912
+ * KeyboardHintsBar) plus the Confirm button below the list: marginTop (1) +
913
+ * single border top/bottom (2) + button text (1).
914
+ */
915
+ const MENU_CHROME = 6;
895
916
  /** Count the visual rows occupied by rows[start..end), accounting for header margins. */
896
917
  function countVisualRows(rows, start, end) {
897
918
  let count = 0;
@@ -949,6 +970,7 @@ const GroupedPickerMenu = ({ message, groups, initialSelected, onSelect }) => {
949
970
  const selectableIndices = useMemo(() => rows.map((r, i) => r.kind === "option" ? i : -1).filter((i) => i >= 0), [rows]);
950
971
  const allValues = useMemo(() => rows.filter((r) => r.kind === "option").map((r) => r.value), [rows]);
951
972
  const [focusedSelectable, setFocusedSelectable] = useState(0);
973
+ const [onButton, setOnButton] = useState(false);
952
974
  const [selected, setSelected] = useState(() => new Set(initialSelected ?? allValues));
953
975
  const [scrollOffset, setScrollOffset] = useState(0);
954
976
  const focusedRowIdx = selectableIndices[focusedSelectable] ?? 0;
@@ -961,23 +983,32 @@ const GroupedPickerMenu = ({ message, groups, initialSelected, onSelect }) => {
961
983
  label: "↑↓",
962
984
  action: "navigate",
963
985
  handler: (_input, key) => {
964
- let newFocused = focusedSelectable;
965
- if (key.upArrow) newFocused = focusedSelectable > 0 ? focusedSelectable - 1 : selectableIndices.length - 1;
966
- if (key.downArrow) newFocused = focusedSelectable < selectableIndices.length - 1 ? focusedSelectable + 1 : 0;
967
- if (newFocused !== focusedSelectable) {
968
- setFocusedSelectable(newFocused);
986
+ const last = selectableIndices.length - 1;
987
+ const moveTo = (target) => {
988
+ setOnButton(false);
989
+ setFocusedSelectable(target);
969
990
  if (needsScroll) {
970
- const newFocusedRowIdx = selectableIndices[newFocused] ?? 0;
971
- setScrollOffset((prev) => adjustScrollOffset(prev, newFocusedRowIdx, rows, effectiveBudget));
991
+ const rowIdx = selectableIndices[target] ?? 0;
992
+ setScrollOffset((prev) => adjustScrollOffset(prev, rowIdx, rows, effectiveBudget));
972
993
  }
973
- }
994
+ };
995
+ if (key.upArrow) if (onButton) moveTo(last);
996
+ else if (focusedSelectable > 0) moveTo(focusedSelectable - 1);
997
+ else setOnButton(true);
998
+ if (key.downArrow) if (onButton) moveTo(0);
999
+ else if (focusedSelectable < last) moveTo(focusedSelectable + 1);
1000
+ else setOnButton(true);
974
1001
  }
975
1002
  },
976
1003
  {
977
- match: "space",
978
- label: "space",
979
- action: "toggle",
1004
+ match: ["space", "return"],
1005
+ label: "enter",
1006
+ action: "select",
980
1007
  handler: () => {
1008
+ if (onButton) {
1009
+ onSelect([...selected]);
1010
+ return;
1011
+ }
981
1012
  const row = rows[selectableIndices[focusedSelectable] ?? 0];
982
1013
  if (row?.kind === "option") setSelected((prev) => {
983
1014
  const next = new Set(prev);
@@ -998,14 +1029,6 @@ const GroupedPickerMenu = ({ message, groups, initialSelected, onSelect }) => {
998
1029
  return new Set(allValues);
999
1030
  });
1000
1031
  }
1001
- },
1002
- {
1003
- match: "return",
1004
- label: "enter",
1005
- action: "confirm",
1006
- handler: () => {
1007
- onSelect([...selected]);
1008
- }
1009
1032
  }
1010
1033
  ]);
1011
1034
  const visibleStart = needsScroll ? scrollOffset : 0;
@@ -1015,56 +1038,67 @@ const GroupedPickerMenu = ({ message, groups, initialSelected, onSelect }) => {
1015
1038
  const hiddenBelow = needsScroll ? selectableIndices.filter((s) => s >= visibleEnd).length : 0;
1016
1039
  return /* @__PURE__ */ jsxs(Box, {
1017
1040
  flexDirection: "column",
1018
- children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsxs(Box, {
1019
- flexDirection: "column",
1020
- marginTop: 1,
1021
- marginLeft: 2,
1022
- children: [
1023
- needsScroll && /* @__PURE__ */ jsx(Text, {
1024
- dimColor: true,
1025
- children: hiddenAbove > 0 ? `\u2191 ${hiddenAbove} more` : " "
1026
- }),
1027
- visibleRows.map((row, relIdx) => {
1028
- const absIdx = visibleStart + relIdx;
1029
- if (row.kind === "header") return /* @__PURE__ */ jsx(Box, {
1030
- marginTop: relIdx > 0 && absIdx > 0 ? 1 : 0,
1031
- children: /* @__PURE__ */ jsx(Text, {
1032
- bold: true,
1033
- dimColor: true,
1034
- children: row.label
1035
- })
1036
- }, `h-${absIdx}`);
1037
- const isFocused = focusedRowIdx === absIdx;
1038
- const isSelected = selected.has(row.value);
1039
- const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
1040
- const label = truncateWithEllipsis(row.hint ? `${row.label} (${row.hint})` : row.label, labelWidth);
1041
- return /* @__PURE__ */ jsxs(Box, {
1042
- gap: 1,
1043
- marginLeft: 1,
1044
- children: [/* @__PURE__ */ jsx(Text, {
1045
- color: isSelected ? "white" : Colors.muted,
1046
- dimColor: !isFocused && !isSelected,
1047
- children: checkbox
1048
- }), /* @__PURE__ */ jsx(Box, {
1049
- flexGrow: 1,
1050
- flexShrink: 1,
1051
- overflow: "hidden",
1041
+ children: [
1042
+ /* @__PURE__ */ jsx(PromptLabel, { message }),
1043
+ /* @__PURE__ */ jsxs(Box, {
1044
+ flexDirection: "column",
1045
+ marginTop: 1,
1046
+ marginLeft: 2,
1047
+ children: [
1048
+ needsScroll && /* @__PURE__ */ jsx(Text, {
1049
+ dimColor: true,
1050
+ children: hiddenAbove > 0 ? `\u2191 ${hiddenAbove} more` : " "
1051
+ }),
1052
+ visibleRows.map((row, relIdx) => {
1053
+ const absIdx = visibleStart + relIdx;
1054
+ if (row.kind === "header") return /* @__PURE__ */ jsx(Box, {
1055
+ marginTop: relIdx > 0 && absIdx > 0 ? 1 : 0,
1052
1056
  children: /* @__PURE__ */ jsx(Text, {
1053
- color: isFocused ? Colors.accent : void 0,
1054
- bold: isFocused,
1055
- dimColor: !isFocused,
1056
- wrap: "truncate",
1057
- children: label
1057
+ bold: true,
1058
+ dimColor: true,
1059
+ children: row.label
1058
1060
  })
1059
- })]
1060
- }, row.value);
1061
- }),
1062
- needsScroll && /* @__PURE__ */ jsx(Text, {
1063
- dimColor: true,
1064
- children: hiddenBelow > 0 ? `\u2193 ${hiddenBelow} more` : " "
1061
+ }, `h-${absIdx}`);
1062
+ const isFocused = !onButton && focusedRowIdx === absIdx;
1063
+ const isSelected = selected.has(row.value);
1064
+ const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
1065
+ const label = truncateWithEllipsis(row.hint ? `${row.label} (${row.hint})` : row.label, labelWidth);
1066
+ return /* @__PURE__ */ jsxs(Box, {
1067
+ gap: 1,
1068
+ marginLeft: 1,
1069
+ children: [/* @__PURE__ */ jsx(Text, {
1070
+ color: isSelected ? "white" : Colors.muted,
1071
+ dimColor: !isFocused && !isSelected,
1072
+ children: checkbox
1073
+ }), /* @__PURE__ */ jsx(Box, {
1074
+ flexGrow: 1,
1075
+ flexShrink: 1,
1076
+ overflow: "hidden",
1077
+ children: /* @__PURE__ */ jsx(Text, {
1078
+ color: isFocused ? Colors.accent : void 0,
1079
+ bold: isFocused,
1080
+ dimColor: !isFocused,
1081
+ wrap: "truncate",
1082
+ children: label
1083
+ })
1084
+ })]
1085
+ }, row.value);
1086
+ }),
1087
+ needsScroll && /* @__PURE__ */ jsx(Text, {
1088
+ dimColor: true,
1089
+ children: hiddenBelow > 0 ? `\u2193 ${hiddenBelow} more` : " "
1090
+ })
1091
+ ]
1092
+ }),
1093
+ /* @__PURE__ */ jsx(Box, {
1094
+ marginTop: 1,
1095
+ marginLeft: 2,
1096
+ children: /* @__PURE__ */ jsx(ConfirmButton, {
1097
+ focused: onButton,
1098
+ count: selected.size
1065
1099
  })
1066
- ]
1067
- })]
1100
+ })
1101
+ ]
1068
1102
  });
1069
1103
  };
1070
1104
  //#endregion
@@ -1193,6 +1227,107 @@ const ModalOverlay = ({ borderColor, title, titleColor, width = 68, children, fe
1193
1227
  });
1194
1228
  };
1195
1229
  //#endregion
1230
+ //#region src/ui/tui/primitives/link-helpers.ts
1231
+ /**
1232
+ * Link-rendering helpers for terminal prompts.
1233
+ *
1234
+ * Terminals that auto-linkify text scan *visual* lines, so a URL the TUI wraps
1235
+ * across lines — or pads with box-border characters — gets a wrong click
1236
+ * target: the terminal opens half a URL, or one stitched back together with
1237
+ * border glyphs and padding.
1238
+ *
1239
+ * The fix is an explicit OSC 8 hyperlink: the escape carries the exact target
1240
+ * out of band, independent of the visible layout, and Ink's wrap re-emits it on
1241
+ * every wrapped line — so the click target stays correct even when the URL
1242
+ * wraps to fit the overlay. Each standalone URL gets its own line so the escape
1243
+ * brackets exactly one URL. Terminals without OSC 8 support ignore the escape
1244
+ * and show the visible text.
1245
+ */
1246
+ const ESC = String.fromCharCode(27);
1247
+ const BEL = String.fromCharCode(7);
1248
+ const OSC_8 = `${ESC}]8;;`;
1249
+ /** Matches an http(s) URL run (no surrounding whitespace). */
1250
+ const URL_RUN = /https?:\/\/[^\s]+/g;
1251
+ /** A line whose entire (trimmed) content is a single URL. */
1252
+ const STANDALONE_URL_LINE = /^https?:\/\/\S+$/;
1253
+ /** Trailing characters that are punctuation around a URL, not part of it. */
1254
+ const TRAILING_PUNCTUATION = /[.,;:!?)\]}>'"]+$/;
1255
+ function trimTrailingPunctuation(url) {
1256
+ return url.replace(TRAILING_PUNCTUATION, "");
1257
+ }
1258
+ /**
1259
+ * Wrap `label` (defaults to the URL) in an OSC 8 hyperlink escape pointing
1260
+ * at `url`. Terminals that support OSC 8 make the whole run clickable to the
1261
+ * exact `url`; terminals that don't render `label` as plain text.
1262
+ */
1263
+ function osc8Hyperlink(url, label = url) {
1264
+ return `${OSC_8}${url}${BEL}${label}${OSC_8}${BEL}`;
1265
+ }
1266
+ /** Extract every http(s) URL in `text`, trailing punctuation removed. */
1267
+ function extractUrls(text) {
1268
+ return (text.match(URL_RUN) ?? []).map(trimTrailingPunctuation);
1269
+ }
1270
+ /**
1271
+ * Split prompt text into renderable segments, breaking out any line that is
1272
+ * *solely* a URL into its own `url` segment. Consecutive non-URL lines stay
1273
+ * grouped in one `text` segment (newlines preserved) so paragraph spacing is
1274
+ * unchanged. URLs that appear inline within prose are left in the text
1275
+ * segment — only standalone-line URLs are special-cased, which is how the
1276
+ * wizard's prompts present links a user is meant to open.
1277
+ */
1278
+ function splitPromptIntoSegments(text) {
1279
+ const segments = [];
1280
+ let buffer = [];
1281
+ const flush = () => {
1282
+ if (buffer.length > 0) {
1283
+ segments.push({
1284
+ type: "text",
1285
+ value: buffer.join("\n")
1286
+ });
1287
+ buffer = [];
1288
+ }
1289
+ };
1290
+ for (const line of text.split("\n")) {
1291
+ const trimmed = line.trim();
1292
+ if (STANDALONE_URL_LINE.test(trimmed)) {
1293
+ flush();
1294
+ segments.push({
1295
+ type: "url",
1296
+ value: trimTrailingPunctuation(trimmed)
1297
+ });
1298
+ } else buffer.push(line);
1299
+ }
1300
+ flush();
1301
+ return segments;
1302
+ }
1303
+ //#endregion
1304
+ //#region src/ui/tui/primitives/LinkText.tsx
1305
+ /**
1306
+ * LinkText — renders prompt text with standalone URLs as OSC 8 hyperlinks.
1307
+ *
1308
+ * Each URL gets its own line, wrapped (`wrap="wrap"`) so the full URL is shown
1309
+ * within the overlay instead of truncated — the user can read and select all of
1310
+ * it. Ink's wrap (wrap-ansi) re-emits the OSC 8 escape on every wrapped visual
1311
+ * line, so the click target stays the exact, full URL no matter where the
1312
+ * visible text breaks. A manual selection of a wrapped line still picks up the
1313
+ * line break, so for a clean copy `WizardAskScreen` auto-copies a sole URL to
1314
+ * the clipboard. Prose renders unchanged.
1315
+ *
1316
+ * Used by the `wizard_ask` overlay only for programs that opt into rich link
1317
+ * rendering (see `PendingQuestion.richLinks`). Other flows are untouched.
1318
+ */
1319
+ const LinkText = ({ text }) => {
1320
+ return /* @__PURE__ */ jsx(Box, {
1321
+ flexDirection: "column",
1322
+ children: splitPromptIntoSegments(text).map((segment, i) => segment.type === "url" ? /* @__PURE__ */ jsx(Text, {
1323
+ color: Colors.accent,
1324
+ underline: true,
1325
+ wrap: "wrap",
1326
+ children: osc8Hyperlink(segment.value)
1327
+ }, i) : /* @__PURE__ */ jsx(Text, { children: segment.value }, i))
1328
+ });
1329
+ };
1330
+ //#endregion
1196
1331
  //#region src/ui/tui/primitives/LogViewer.tsx
1197
1332
  /**
1198
1333
  * LogViewer — Real-time log tail, pinned to available terminal height.
@@ -2067,7 +2202,14 @@ const LearnCard = ({ store, blocks, onComplete }) => {
2067
2202
  * Reactively shows/hides tips based on discovered features.
2068
2203
  * Supports toggling additional features via key bindings.
2069
2204
  */
2070
- const TIPS = [
2205
+ /**
2206
+ * The default deck — generic PostHog onboarding tips, shown for any
2207
+ * program that doesn't supply its own via `ProgramConfig.getTips`.
2208
+ * Program-specific copy (e.g. the self-driving scout/source
2209
+ * explainers) lives in that program's content, not here — this stays the
2210
+ * neutral fallback.
2211
+ */
2212
+ const DEFAULT_TIPS = [
2071
2213
  {
2072
2214
  id: "persons",
2073
2215
  title: "You can also track people and groups with PostHog",
@@ -2105,9 +2247,9 @@ const TIPS = [
2105
2247
  }
2106
2248
  }
2107
2249
  ];
2108
- const TipsCard = ({ store }) => {
2250
+ const TipsCard = ({ store, tips = DEFAULT_TIPS }) => {
2109
2251
  useInput((input) => {
2110
- for (const tip of TIPS) if (tip.toggle && input.toLowerCase() === tip.toggle.key && (!tip.visible || tip.visible(store)) && !tip.toggle.isEnabled(store)) store.enableFeature(tip.toggle.feature);
2252
+ for (const tip of tips) if (tip.toggle && input.toLowerCase() === tip.toggle.key && (!tip.visible || tip.visible(store)) && !tip.toggle.isEnabled(store)) store.enableFeature(tip.toggle.feature);
2111
2253
  });
2112
2254
  return /* @__PURE__ */ jsxs(Box, {
2113
2255
  flexDirection: "column",
@@ -2119,7 +2261,7 @@ const TipsCard = ({ store }) => {
2119
2261
  children: "Tips"
2120
2262
  }),
2121
2263
  /* @__PURE__ */ jsx(Box, { height: 1 }),
2122
- TIPS.filter((tip) => !tip.visible || tip.visible(store)).map((tip) => /* @__PURE__ */ jsxs(Box, {
2264
+ tips.filter((tip) => !tip.visible || tip.visible(store)).map((tip) => /* @__PURE__ */ jsxs(Box, {
2123
2265
  flexDirection: "column",
2124
2266
  marginBottom: 1,
2125
2267
  children: [/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsxs(Text, {
@@ -2161,6 +2303,782 @@ const TipsCard = ({ store }) => {
2161
2303
  });
2162
2304
  };
2163
2305
  //#endregion
2306
+ //#region src/ui/tui/hooks/useTick.ts
2307
+ /**
2308
+ * useTick — forces a re-render every `intervalMs`. Returns a monotonically
2309
+ * increasing counter, which is rarely needed — most callers just want the
2310
+ * re-render side effect.
2311
+ */
2312
+ function useTick(intervalMs) {
2313
+ const [tick, setTick] = useState(0);
2314
+ useEffect(() => {
2315
+ const id = setInterval(() => setTick((t) => t + 1), intervalMs);
2316
+ return () => clearInterval(id);
2317
+ }, [intervalMs]);
2318
+ return tick;
2319
+ }
2320
+ //#endregion
2321
+ //#region src/ui/tui/components/visualizer/palette.ts
2322
+ const VISUALIZER_PALETTE = {
2323
+ fade: "#0E7A0E",
2324
+ bright: "#7CFF7C",
2325
+ mid: "#22D622",
2326
+ head: "#E6FFE6",
2327
+ book: [
2328
+ "#22D622",
2329
+ "#7CFF7C",
2330
+ "#5BE05B",
2331
+ "#A0F0A0",
2332
+ "#36B536"
2333
+ ],
2334
+ deleteRed: "#D63B22",
2335
+ upGreen: "#7CFF7C"
2336
+ };
2337
+ //#endregion
2338
+ //#region src/ui/tui/components/visualizer/panel.tsx
2339
+ /**
2340
+ * Shared scaffolding for the phase visuals — Matrix-green color, common
2341
+ * VisualProps shape, and the rounded `Panel` shell every visual sits in.
2342
+ *
2343
+ * Visuals each render their own grid then wrap it in `<Panel>` so phase
2344
+ * transitions stay visually continuous.
2345
+ */
2346
+ const MATRIX_FADE = VISUALIZER_PALETTE.fade;
2347
+ const Panel = ({ children }) => /* @__PURE__ */ jsx(Box, {
2348
+ flexDirection: "column",
2349
+ borderStyle: "round",
2350
+ borderColor: MATRIX_FADE,
2351
+ children
2352
+ });
2353
+ //#endregion
2354
+ //#region src/ui/tui/components/visualizer/MatrixRain.tsx
2355
+ /**
2356
+ * MatrixRain — code-rain visual used for the codebase-scan phase.
2357
+ *
2358
+ * Independent of the phase orchestrator so it can be reused elsewhere
2359
+ * (e.g. standalone in a demo). `bordered` toggles the rounded green frame.
2360
+ */
2361
+ const { head: MATRIX_HEAD, bright: MATRIX_BRIGHT, mid: MATRIX_MID } = VISUALIZER_PALETTE;
2362
+ const DEFAULT_TICK_MS = 110;
2363
+ const DEFAULT_MAX_TAIL = 7;
2364
+ const RAIN_GLYPHS = "ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン0123456789Z<>=*+:.";
2365
+ function pickGlyph() {
2366
+ return RAIN_GLYPHS[Math.floor(Math.random() * 73)];
2367
+ }
2368
+ function makeRainColumn(height, maxTail) {
2369
+ return {
2370
+ headY: -Math.random() * height,
2371
+ speed: .3 + Math.random() * .9,
2372
+ tail: 3 + Math.floor(Math.random() * (maxTail - 2)),
2373
+ glyphs: /* @__PURE__ */ new Map(),
2374
+ dormant: Math.floor(Math.random() * 18)
2375
+ };
2376
+ }
2377
+ function tickRainColumn(col, height, maxTail) {
2378
+ if (col.dormant > 0) return {
2379
+ ...col,
2380
+ dormant: col.dormant - 1
2381
+ };
2382
+ const next = col.headY + col.speed;
2383
+ if (next > height + col.tail) return makeRainColumn(height, maxTail);
2384
+ const glyphs = new Map(col.glyphs);
2385
+ for (let y = Math.max(0, Math.ceil(col.headY)); y <= Math.min(Math.floor(next), height - 1); y++) glyphs.set(y, pickGlyph());
2386
+ if (glyphs.size > 0 && Math.random() < .22) {
2387
+ const keys = [...glyphs.keys()];
2388
+ const k = keys[Math.floor(Math.random() * keys.length)];
2389
+ glyphs.set(k, pickGlyph());
2390
+ }
2391
+ return {
2392
+ ...col,
2393
+ headY: next,
2394
+ glyphs
2395
+ };
2396
+ }
2397
+ const MatrixRain = ({ width, height, tickMs = DEFAULT_TICK_MS, maxTail = DEFAULT_MAX_TAIL, bordered = true }) => {
2398
+ const columnsRef = useRef(Array.from({ length: width }, () => makeRainColumn(height, maxTail)));
2399
+ const [, setTick] = useState(0);
2400
+ useEffect(() => {
2401
+ const interval = setInterval(() => {
2402
+ columnsRef.current = columnsRef.current.map((c) => tickRainColumn(c, height, maxTail));
2403
+ setTick((t) => t + 1);
2404
+ }, tickMs);
2405
+ return () => clearInterval(interval);
2406
+ }, [
2407
+ height,
2408
+ maxTail,
2409
+ tickMs
2410
+ ]);
2411
+ const columns = columnsRef.current;
2412
+ const body = Array.from({ length: height }).map((_, y) => /* @__PURE__ */ jsx(Box, { children: columns.map((col, x) => {
2413
+ const glyph = col.glyphs.get(y);
2414
+ if (!glyph) return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2415
+ const dist = col.headY - y;
2416
+ if (dist < 0 || dist > col.tail) return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2417
+ if (dist < 1) return /* @__PURE__ */ jsx(Text, {
2418
+ bold: true,
2419
+ color: MATRIX_HEAD,
2420
+ children: glyph
2421
+ }, x);
2422
+ if (dist < 2) return /* @__PURE__ */ jsx(Text, {
2423
+ color: MATRIX_BRIGHT,
2424
+ children: glyph
2425
+ }, x);
2426
+ if (dist < col.tail * .55) return /* @__PURE__ */ jsx(Text, {
2427
+ color: MATRIX_MID,
2428
+ children: glyph
2429
+ }, x);
2430
+ return /* @__PURE__ */ jsx(Text, {
2431
+ color: MATRIX_FADE,
2432
+ dimColor: true,
2433
+ children: glyph
2434
+ }, x);
2435
+ }) }, y));
2436
+ if (!bordered) return /* @__PURE__ */ jsx(Box, {
2437
+ flexDirection: "column",
2438
+ children: body
2439
+ });
2440
+ return /* @__PURE__ */ jsx(Box, {
2441
+ flexDirection: "column",
2442
+ borderStyle: "round",
2443
+ borderColor: MATRIX_FADE,
2444
+ children: body
2445
+ });
2446
+ };
2447
+ //#endregion
2448
+ //#region src/ui/tui/components/visualizer/LibraryShelf.tsx
2449
+ /**
2450
+ * LibraryShelf — skill-selection phase.
2451
+ *
2452
+ * A row of book spines. One spine peels forward each cycle, the chosen
2453
+ * title hovers next to it, then it slides home and the next one is picked.
2454
+ */
2455
+ const BOOK_LABELS = [
2456
+ "nx",
2457
+ "rt",
2458
+ "sv",
2459
+ "fl",
2460
+ "jg",
2461
+ "rb",
2462
+ "go",
2463
+ "dj",
2464
+ "fa",
2465
+ "lv",
2466
+ "ts",
2467
+ "py"
2468
+ ];
2469
+ const BOOK_COLORS = VISUALIZER_PALETTE.book;
2470
+ const LibraryShelf = ({ width, height }) => {
2471
+ const tick = useTick(380);
2472
+ const bookCount = Math.min(Math.floor((width - 2) / 2), BOOK_LABELS.length);
2473
+ const cyclePos = tick % (bookCount * 4);
2474
+ const selectedIdx = Math.floor(cyclePos / 4);
2475
+ const phase = cyclePos % 4;
2476
+ const offset = phase === 0 ? 0 : phase === 1 ? 1 : phase === 2 ? 2 : 1;
2477
+ const wobble = phase === 2 && tick % 2 === 0 ? 1 : 0;
2478
+ const shelfY = Math.floor(height / 2) - 1;
2479
+ const rows = Array.from({ length: height }, () => new Array(width).fill(" "));
2480
+ for (let i = 0; i < bookCount; i++) {
2481
+ const x = 1 + i * 2;
2482
+ const isSelected = i === selectedIdx;
2483
+ const shift = isSelected ? offset : 0;
2484
+ const wob = isSelected && wobble ? -1 : 0;
2485
+ if (x + shift >= width) continue;
2486
+ rows[shelfY - 1][x + shift] = "█";
2487
+ rows[shelfY][x + shift] = BOOK_LABELS[i][0];
2488
+ rows[shelfY + 1][x + shift] = BOOK_LABELS[i][1];
2489
+ rows[shelfY + 2 + wob]?.[x + shift] !== void 0 && (rows[shelfY + 2 + wob][x + shift] = "█");
2490
+ }
2491
+ const boardY = shelfY + 3;
2492
+ if (boardY < height) for (let x = 0; x < width; x++) rows[boardY][x] = "─";
2493
+ if (phase === 2) {
2494
+ const labelStartX = 1 + selectedIdx * 2 + 4;
2495
+ const labelText = BOOK_LABELS[selectedIdx] + "-app";
2496
+ for (let c = 0; c < labelText.length && labelStartX + c < width; c++) rows[shelfY][labelStartX + c] = labelText[c];
2497
+ if (labelStartX - 1 < width && labelStartX - 1 >= 0) rows[shelfY][labelStartX - 1] = "▶";
2498
+ }
2499
+ return /* @__PURE__ */ jsx(Panel, { children: rows.map((row, y) => /* @__PURE__ */ jsx(Box, { children: row.map((ch, x) => {
2500
+ if (ch === " ") return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2501
+ if (ch === "─") return /* @__PURE__ */ jsx(Text, {
2502
+ color: MATRIX_FADE,
2503
+ dimColor: true,
2504
+ children: "─"
2505
+ }, x);
2506
+ if (ch === "▶") return /* @__PURE__ */ jsx(Text, {
2507
+ bold: true,
2508
+ color: VISUALIZER_PALETTE.head,
2509
+ children: "▶"
2510
+ }, x);
2511
+ const booksColor = BOOK_COLORS[(Math.floor((x - 1) / 2) + 17 * y) % BOOK_COLORS.length];
2512
+ const isSel = x === 1 + selectedIdx * 2 + offset;
2513
+ return /* @__PURE__ */ jsx(Text, {
2514
+ bold: isSel,
2515
+ color: isSel ? VISUALIZER_PALETTE.head : booksColor,
2516
+ children: ch
2517
+ }, x);
2518
+ }) }, y)) });
2519
+ };
2520
+ //#endregion
2521
+ //#region src/ui/tui/components/visualizer/CrateStack.tsx
2522
+ /**
2523
+ * CrateStack — dependency-install phase.
2524
+ *
2525
+ * Boxes drop from the top and stack at the bottom. Each new arrival lands
2526
+ * with a tiny shake.
2527
+ */
2528
+ const PACKAGE_NAMES = [
2529
+ "posthog-js",
2530
+ "posthog-py",
2531
+ "posthog-rb",
2532
+ "posthog-go",
2533
+ "posthog-node",
2534
+ "react-ph",
2535
+ "next-ph",
2536
+ "svelte-ph",
2537
+ "ph-mcp",
2538
+ "ph-ai"
2539
+ ];
2540
+ const CRATE_W = Math.max(...PACKAGE_NAMES.map((n) => n.length)) + 2;
2541
+ const CrateStack = ({ width, height }) => {
2542
+ const tick = useTick(95);
2543
+ const state = useRef({
2544
+ stack: [],
2545
+ falling: null,
2546
+ spawnCooldown: 0
2547
+ }).current;
2548
+ const crateW = CRATE_W;
2549
+ const crateH = 1;
2550
+ const floorY = height - 1;
2551
+ if (state.falling) {
2552
+ state.falling.y += 1;
2553
+ const collisionStackHeight = state.stack.filter((c) => Math.abs(c.x - state.falling.x) < crateW - 1).length * crateH;
2554
+ if (state.falling.y >= floorY - collisionStackHeight) {
2555
+ state.stack.push({
2556
+ label: state.falling.label,
2557
+ x: state.falling.x,
2558
+ landedAt: tick
2559
+ });
2560
+ state.falling = null;
2561
+ state.spawnCooldown = 3;
2562
+ }
2563
+ } else if (state.spawnCooldown > 0) state.spawnCooldown -= 1;
2564
+ else if (state.stack.length < Math.floor(height / crateH) - 1) state.falling = {
2565
+ label: PACKAGE_NAMES[Math.floor(Math.random() * PACKAGE_NAMES.length)],
2566
+ x: 2 + Math.floor(Math.random() * Math.max(1, width - crateW - 4)),
2567
+ y: -1
2568
+ };
2569
+ else if (tick % 20 === 0) state.stack = [];
2570
+ const grid = Array.from({ length: height }, () => new Array(width).fill(" "));
2571
+ const drawCrate = (cx, cy, label, shake) => {
2572
+ const x = cx + shake;
2573
+ if (cy < 0 || cy >= height) return;
2574
+ const padded = `[${label.slice(0, crateW - 2)}]`.padEnd(crateW, " ");
2575
+ for (let i = 0; i < crateW && x + i < width; i++) if (x + i >= 0) grid[cy][x + i] = padded[i];
2576
+ };
2577
+ state.stack.forEach((c, idx) => {
2578
+ const cy = floorY - idx;
2579
+ const shake = tick - c.landedAt === 0 ? 1 : tick - c.landedAt === 1 ? -1 : 0;
2580
+ drawCrate(c.x, cy, c.label, shake);
2581
+ });
2582
+ if (state.falling) drawCrate(state.falling.x, state.falling.y, state.falling.label, 0);
2583
+ return /* @__PURE__ */ jsx(Panel, { children: grid.map((row, y) => /* @__PURE__ */ jsx(Box, { children: row.map((ch, x) => {
2584
+ if (ch === " ") return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2585
+ const isFalling = state.falling && y === state.falling.y && Math.abs(x - state.falling.x) < crateW;
2586
+ return /* @__PURE__ */ jsx(Text, {
2587
+ bold: isFalling,
2588
+ color: isFalling ? VISUALIZER_PALETTE.head : VISUALIZER_PALETTE.mid,
2589
+ children: ch
2590
+ }, x);
2591
+ }) }, y)) });
2592
+ };
2593
+ //#endregion
2594
+ //#region src/ui/tui/components/visualizer/DiffCascade.tsx
2595
+ /**
2596
+ * DiffCascade — code-edits phase.
2597
+ *
2598
+ * + and - code lines scroll upward continuously. Occasional comment-rewrite
2599
+ * gag: a "// hmm…" line appears, gets - struck out, then a + replaces it.
2600
+ */
2601
+ const CODE_SNIPPETS = [
2602
+ "import posthog from 'posthog-js'",
2603
+ "posthog.init(KEY, { host: HOST })",
2604
+ "<PostHogProvider client={posthog}>",
2605
+ " {children}",
2606
+ "</PostHogProvider>",
2607
+ "posthog.capture('page_viewed')",
2608
+ "posthog.capture('signup_started')",
2609
+ "posthog.identify(user.id, { email })",
2610
+ "if (process.env.NODE_ENV !== 'test') posthog.init(KEY)",
2611
+ "export const posthog = new PostHog(KEY)",
2612
+ "// TODO: enable replay",
2613
+ "window.posthog = posthog"
2614
+ ];
2615
+ const WHIMSY_COMMENTS = [
2616
+ "// hmm — that should be a hook",
2617
+ "// wait, refactor incoming",
2618
+ "// posthog says hi"
2619
+ ];
2620
+ const DiffCascade = ({ width, height }) => {
2621
+ const tick = useTick(280);
2622
+ const linesRef = useRef([]);
2623
+ if (linesRef.current.length === 0) for (let i = 0; i < height; i++) linesRef.current.push({
2624
+ sign: Math.random() < .75 ? "+" : "-",
2625
+ text: CODE_SNIPPETS[Math.floor(Math.random() * CODE_SNIPPETS.length)]
2626
+ });
2627
+ linesRef.current.shift();
2628
+ const whimsy = tick % 23 === 0;
2629
+ linesRef.current.push({
2630
+ sign: whimsy ? "-" : Math.random() < .78 ? "+" : "-",
2631
+ text: whimsy ? WHIMSY_COMMENTS[Math.floor(Math.random() * WHIMSY_COMMENTS.length)] : CODE_SNIPPETS[Math.floor(Math.random() * CODE_SNIPPETS.length)]
2632
+ });
2633
+ const lines = linesRef.current;
2634
+ return /* @__PURE__ */ jsx(Panel, { children: Array.from({ length: height }).map((_, y) => {
2635
+ const line = lines[y];
2636
+ const cap = width - 2;
2637
+ const text = `${line.sign} ${line.text}`.slice(0, cap);
2638
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, {
2639
+ color: line.sign === "+" ? VISUALIZER_PALETTE.mid : line.sign === "-" ? VISUALIZER_PALETTE.deleteRed : MATRIX_FADE,
2640
+ children: text.padEnd(cap, " ")
2641
+ }) }, y);
2642
+ }) });
2643
+ };
2644
+ //#endregion
2645
+ //#region src/ui/tui/components/visualizer/Tumblers.tsx
2646
+ /**
2647
+ * Tumblers — env-setup phase.
2648
+ *
2649
+ * Pins fall into a lock cylinder one by one; when all six align the bolt
2650
+ * pulses green for a beat, then the cycle restarts.
2651
+ */
2652
+ const Tumblers = ({ width, height }) => {
2653
+ const tick = useTick(80);
2654
+ const pinCount = Math.min(Math.floor((width - 2) / 2), 6);
2655
+ const state = useRef({
2656
+ heights: new Array(pinCount).fill(0),
2657
+ current: 0,
2658
+ fallY: 0,
2659
+ pulse: 0
2660
+ }).current;
2661
+ const cylinderTop = 1;
2662
+ const cylinderBottom = height - 2;
2663
+ const targetForPin = (i) => cylinderBottom - 1 - i % 3 - Math.floor(i / 2);
2664
+ if (state.pulse > 0) {
2665
+ state.pulse -= 1;
2666
+ if (state.pulse === 0) {
2667
+ state.heights = new Array(pinCount).fill(0);
2668
+ state.current = 0;
2669
+ state.fallY = 0;
2670
+ }
2671
+ } else if (state.current < pinCount) {
2672
+ state.fallY += 1;
2673
+ if (state.fallY >= targetForPin(state.current)) {
2674
+ state.heights[state.current] = targetForPin(state.current);
2675
+ state.current += 1;
2676
+ state.fallY = cylinderTop;
2677
+ }
2678
+ } else state.pulse = 14;
2679
+ const grid = Array.from({ length: height }, () => new Array(width).fill(" "));
2680
+ for (let y = 0; y < height; y++) {
2681
+ grid[y][0] = "│";
2682
+ grid[y][width - 1] = "│";
2683
+ }
2684
+ for (let i = 0; i < pinCount; i++) {
2685
+ const x = 1 + i * 2 + 1;
2686
+ if (x < width) grid[0][x] = "▼";
2687
+ }
2688
+ for (let x = 1; x < width - 1; x++) grid[height - 1][x] = "─";
2689
+ for (let i = 0; i < pinCount; i++) {
2690
+ const pinX = 1 + i * 2 + 1;
2691
+ if (pinX >= width) continue;
2692
+ const top = state.heights[i] || cylinderTop;
2693
+ for (let y = top; y <= cylinderBottom; y++) grid[y][pinX] = "█";
2694
+ }
2695
+ if (state.pulse === 0 && state.current < pinCount) {
2696
+ const pinX = 1 + state.current * 2 + 1;
2697
+ if (pinX < width) grid[state.fallY][pinX] = "█";
2698
+ }
2699
+ const pulsing = state.pulse > 0;
2700
+ const pulseBright = pulsing && tick % 2 === 0;
2701
+ return /* @__PURE__ */ jsx(Panel, { children: grid.map((row, y) => /* @__PURE__ */ jsx(Box, { children: row.map((ch, x) => {
2702
+ if (ch === " ") return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2703
+ if (ch === "│" || ch === "─" || ch === "▼") return /* @__PURE__ */ jsx(Text, {
2704
+ color: pulsing ? pulseBright ? VISUALIZER_PALETTE.bright : MATRIX_FADE : MATRIX_FADE,
2705
+ dimColor: !pulsing,
2706
+ children: ch
2707
+ }, x);
2708
+ const isFalling = state.pulse === 0 && state.current < pinCount && y === state.fallY && x === 1 + state.current * 2 + 1;
2709
+ const color = pulsing ? pulseBright ? VISUALIZER_PALETTE.head : VISUALIZER_PALETTE.bright : isFalling ? VISUALIZER_PALETTE.head : VISUALIZER_PALETTE.mid;
2710
+ return /* @__PURE__ */ jsx(Text, {
2711
+ bold: pulsing || isFalling,
2712
+ color,
2713
+ children: ch
2714
+ }, x);
2715
+ }) }, y)) });
2716
+ };
2717
+ //#endregion
2718
+ //#region src/ui/tui/components/visualizer/DashboardGrid.tsx
2719
+ /**
2720
+ * DashboardGrid — dashboards phase.
2721
+ *
2722
+ * 2×2 grid of mini-charts to evoke a real PostHog dashboard. When the area
2723
+ * is too small, the layout collapses to 1×2 or 1×1 so something always
2724
+ * renders.
2725
+ */
2726
+ const DASHBOARD_TILES = [
2727
+ {
2728
+ title: "Visitors",
2729
+ kind: "bars"
2730
+ },
2731
+ {
2732
+ title: "Sessions",
2733
+ kind: "line"
2734
+ },
2735
+ {
2736
+ title: "Revenue",
2737
+ kind: "gauge"
2738
+ },
2739
+ {
2740
+ title: "Errors",
2741
+ kind: "pulse"
2742
+ }
2743
+ ];
2744
+ const SPARK_GLYPHS = "▁▂▃▄▅▆▇█";
2745
+ const DashboardGrid = ({ width, height }) => {
2746
+ const tick = useTick(220);
2747
+ const grid = Array.from({ length: height }, () => new Array(width).fill(" "));
2748
+ const cols = width >= 22 ? 2 : 1;
2749
+ const rows = height >= 7 ? 2 : 1;
2750
+ const vSplit = cols === 2 ? Math.floor(width / 2) : -1;
2751
+ const hSplit = rows === 2 ? Math.floor(height / 2) : -1;
2752
+ if (vSplit >= 0) for (let y = 0; y < height; y++) grid[y][vSplit] = "│";
2753
+ if (hSplit >= 0) {
2754
+ for (let x = 0; x < width; x++) grid[hSplit][x] = "─";
2755
+ if (vSplit >= 0) grid[hSplit][vSplit] = "┼";
2756
+ }
2757
+ const writeText = (x0, y0, maxW, text) => {
2758
+ if (y0 < 0 || y0 >= height) return;
2759
+ const slice = text.slice(0, Math.max(0, maxW));
2760
+ for (let i = 0; i < slice.length; i++) if (x0 + i >= 0 && x0 + i < width) grid[y0][x0 + i] = slice[i];
2761
+ };
2762
+ for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) {
2763
+ const tile = DASHBOARD_TILES[r * cols + c];
2764
+ const tileX = c === 0 ? 0 : vSplit + 1;
2765
+ const tileY = r === 0 ? 0 : hSplit + 1;
2766
+ const tileW = cols === 2 ? c === 0 ? vSplit : width - vSplit - 1 : width;
2767
+ const tileH = rows === 2 ? r === 0 ? hSplit : height - hSplit - 1 : height;
2768
+ const innerX = tileX;
2769
+ const innerW = Math.max(1, tileW);
2770
+ const titleY = tileY;
2771
+ const valueY = tileY + tileH - 1;
2772
+ const chartY0 = tileY + 1;
2773
+ const chartY1 = Math.max(chartY0, valueY - 1);
2774
+ const chartH = Math.max(1, chartY1 - chartY0 + 1);
2775
+ const seed = (r * cols + c) * 13;
2776
+ writeText(innerX, titleY, innerW, tile.title);
2777
+ renderTile(grid, tile.kind, innerX, chartY0, innerW, chartH, tick, seed, width);
2778
+ writeText(innerX, valueY, innerW, tileValue(tile.kind, tick, seed));
2779
+ }
2780
+ return /* @__PURE__ */ jsx(Panel, { children: grid.map((row, y) => /* @__PURE__ */ jsx(Box, { children: row.map((ch, x) => {
2781
+ if (ch === " ") return /* @__PURE__ */ jsx(Text, { children: " " }, x);
2782
+ if (ch === "│" || ch === "─" || ch === "┼") return /* @__PURE__ */ jsx(Text, {
2783
+ color: MATRIX_FADE,
2784
+ dimColor: true,
2785
+ children: ch
2786
+ }, x);
2787
+ if (SPARK_GLYPHS.includes(ch) || ch === "█") return /* @__PURE__ */ jsx(Text, {
2788
+ color: VISUALIZER_PALETTE.mid,
2789
+ children: ch
2790
+ }, x);
2791
+ if (ch === "●" || ch === "∙" || ch === "·") {
2792
+ const color = ch === "●" ? VISUALIZER_PALETTE.head : ch === "∙" ? VISUALIZER_PALETTE.bright : VISUALIZER_PALETTE.mid;
2793
+ return /* @__PURE__ */ jsx(Text, {
2794
+ bold: ch === "●",
2795
+ color,
2796
+ children: ch
2797
+ }, x);
2798
+ }
2799
+ if (ch === "▲" || ch === "▼") return /* @__PURE__ */ jsx(Text, {
2800
+ bold: true,
2801
+ color: ch === "▲" ? VISUALIZER_PALETTE.upGreen : VISUALIZER_PALETTE.deleteRed,
2802
+ children: ch
2803
+ }, x);
2804
+ return /* @__PURE__ */ jsx(Text, {
2805
+ color: VISUALIZER_PALETTE.bright,
2806
+ children: ch
2807
+ }, x);
2808
+ }) }, y)) });
2809
+ };
2810
+ function renderTile(grid, kind, x0, y0, w, h, tick, seed, gridW) {
2811
+ const set = (x, y, ch) => {
2812
+ if (y < 0 || y >= grid.length) return;
2813
+ if (x < 0 || x >= gridW) return;
2814
+ grid[y][x] = ch;
2815
+ };
2816
+ if (kind === "bars") for (let i = 0; i < w; i++) {
2817
+ const t = tick * .18 + (i + seed) * .6;
2818
+ const level = .55 + .35 * Math.sin(t) + .1 * Math.sin(t * 2.7);
2819
+ const filled = Math.max(0, Math.min(h, Math.round(level * h)));
2820
+ for (let j = 0; j < filled; j++) set(x0 + i, y0 + h - 1 - j, "█");
2821
+ if (filled > 0 && filled < h) {
2822
+ const frac = level * h - Math.floor(level * h);
2823
+ const glyph = SPARK_GLYPHS[Math.floor(frac * 7)];
2824
+ set(x0 + i, y0 + h - filled, glyph);
2825
+ }
2826
+ }
2827
+ else if (kind === "line") {
2828
+ const spikeAt = ((tick / 12 | 0) + seed) % Math.max(1, w);
2829
+ for (let i = 0; i < w; i++) {
2830
+ const norm = i / Math.max(1, w - 1);
2831
+ const base = h - 1 - norm * (h - 1);
2832
+ let yf = base + .6 * Math.sin(i * .7 + seed + tick * .05);
2833
+ if (i === spikeAt) yf = Math.max(0, base - 1.5);
2834
+ const y = Math.max(0, Math.min(h - 1, Math.round(yf)));
2835
+ const dist = Math.abs(i - (w - 1));
2836
+ const ch = dist === 0 ? "●" : dist < 3 ? "∙" : "·";
2837
+ set(x0 + i, y0 + y, ch);
2838
+ }
2839
+ } else if (kind === "gauge") {
2840
+ const cycle = (tick + seed) % 40;
2841
+ const pct = cycle < 30 ? cycle / 30 : 1 - (cycle - 30) / 10;
2842
+ const midY = y0 + Math.floor(h / 2);
2843
+ const filled = Math.max(0, Math.min(w, Math.round(pct * w)));
2844
+ for (let i = 0; i < w; i++) set(x0 + i, midY, i < filled ? "█" : "·");
2845
+ } else if (kind === "pulse") for (let i = 0; i < w; i++) {
2846
+ const t = (i + seed + Math.floor(tick / 3)) % 17;
2847
+ let level;
2848
+ if (t === 0) level = .9;
2849
+ else if (t === 1 || t === 16) level = .5;
2850
+ else level = .12 + .06 * Math.sin((i + tick) * .5);
2851
+ const filled = Math.max(1, Math.min(h, Math.round(level * h)));
2852
+ for (let j = 0; j < filled; j++) set(x0 + i, y0 + h - 1 - j, "█");
2853
+ }
2854
+ }
2855
+ function tileValue(kind, tick, seed) {
2856
+ const wave = .5 + .5 * Math.sin(tick * .05 + seed);
2857
+ if (kind === "bars") return `${(Math.round(8e3 + wave * 6e3) / 1e3).toFixed(1)}k ▲`;
2858
+ if (kind === "line") return `${(2 + wave * 4).toFixed(1)}% ▲`;
2859
+ if (kind === "gauge") return `$${Math.round(20 + wave * 60)}k`;
2860
+ return `${Math.round(1 + wave * 5)} ▼`;
2861
+ }
2862
+ //#endregion
2863
+ //#region src/ui/tui/components/PhaseVisuals.tsx
2864
+ /**
2865
+ * Phase visuals — ambient ASCII visualizations of what the agent is currently
2866
+ * doing. The active phase is derived from the in-progress task labels (best-
2867
+ * effort heuristic). Each phase has its own component (under ./visualizer);
2868
+ * the orchestrator `PhaseVisual` picks which one to render.
2869
+ *
2870
+ * Width and height are passed in from the parent so visuals adapt to the
2871
+ * column they're placed in.
2872
+ */
2873
+ const PHASE_LABELS = {
2874
+ ["codebase-scan"]: "Reading the Code",
2875
+ ["skill-install"]: "Picking the Right Skill",
2876
+ ["dep-install"]: "Installing Packages",
2877
+ ["code-edits"]: "Editing Source",
2878
+ ["env-setup"]: "Wiring Up Secrets",
2879
+ ["dashboards"]: "Building Dashboards"
2880
+ };
2881
+ /** Reads the active phase from the store. The agent loop pushes it in via
2882
+ * `getUI().setStage(...)` whenever a new tool fires. */
2883
+ function useAgentPhase(store) {
2884
+ useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
2885
+ return store.currentStage?.stage ?? "codebase-scan";
2886
+ }
2887
+ /**
2888
+ * VisualizerTab — Winamp-style fullscreen take on the phase visual.
2889
+ * "NOW PLAYING" header, centered visual, transport bar with elapsed time.
2890
+ *
2891
+ * Sizes itself off the *measured* container rather than the raw terminal so
2892
+ * the tab bar / hints / status panel above don't get pushed off-screen on
2893
+ * short terminals. When height runs out, chrome rows drop in order:
2894
+ * transport bar first, then track title, then the NOW PLAYING header.
2895
+ */
2896
+ const VisualizerTab = ({ store }) => {
2897
+ const phase = useAgentPhase(store);
2898
+ const containerRef = useRef(null);
2899
+ const [size, setSize] = useState({
2900
+ width: 0,
2901
+ height: 0
2902
+ });
2903
+ useEffect(() => {
2904
+ if (!containerRef.current) return;
2905
+ const m = measureElement(containerRef.current);
2906
+ if (m.width !== size.width || m.height !== size.height) setSize({
2907
+ width: m.width,
2908
+ height: m.height
2909
+ });
2910
+ }, [
2911
+ useTick(EQ_TICK_MS),
2912
+ size.width,
2913
+ size.height
2914
+ ]);
2915
+ const HEADER_ROWS = 2;
2916
+ const TITLE_ROWS = 2;
2917
+ const TRANSPORT_ROWS = 2;
2918
+ const BORDER_ROWS = 2;
2919
+ const MIN_VISUAL_H = 5;
2920
+ const availH = size.height;
2921
+ const availW = size.width;
2922
+ let chromeBudget = BORDER_ROWS + HEADER_ROWS + TITLE_ROWS + TRANSPORT_ROWS;
2923
+ let showHeader = true;
2924
+ let showTitle = true;
2925
+ let showTransport = true;
2926
+ if (availH > 0 && availH - chromeBudget < MIN_VISUAL_H) {
2927
+ showTransport = false;
2928
+ chromeBudget -= TRANSPORT_ROWS;
2929
+ }
2930
+ if (availH > 0 && availH - chromeBudget < MIN_VISUAL_H) {
2931
+ showTitle = false;
2932
+ chromeBudget -= TITLE_ROWS;
2933
+ }
2934
+ if (availH > 0 && availH - chromeBudget < MIN_VISUAL_H) {
2935
+ showHeader = false;
2936
+ chromeBudget -= HEADER_ROWS;
2937
+ }
2938
+ const visualH = availH > 0 ? Math.max(MIN_VISUAL_H, Math.min(18, availH - chromeBudget)) : MIN_VISUAL_H;
2939
+ const visualW = availW > 0 ? Math.max(20, Math.min(64, availW - 12)) : 40;
2940
+ const now = Date.now();
2941
+ const beatTimeMs = now - (store.currentStage?.startedAt ?? now);
2942
+ const timeStr = formatElapsed(Math.max(0, Math.floor(beatTimeMs / 1e3)));
2943
+ const equalizer = renderMiniEqualizer(beatTimeMs, useRef(new Array(EQ_BARS).fill(0)).current);
2944
+ return /* @__PURE__ */ jsxs(Box, {
2945
+ ref: containerRef,
2946
+ flexDirection: "column",
2947
+ flexGrow: 1,
2948
+ alignItems: "center",
2949
+ justifyContent: "center",
2950
+ overflow: "hidden",
2951
+ children: [
2952
+ showHeader && /* @__PURE__ */ jsxs(Box, {
2953
+ flexDirection: "row",
2954
+ marginBottom: 1,
2955
+ children: [
2956
+ /* @__PURE__ */ jsx(Text, {
2957
+ color: MATRIX_FADE,
2958
+ children: "┌─"
2959
+ }),
2960
+ /* @__PURE__ */ jsx(Text, {
2961
+ bold: true,
2962
+ color: VISUALIZER_PALETTE.bright,
2963
+ children: " ► NOW PLAYING "
2964
+ }),
2965
+ /* @__PURE__ */ jsx(Text, {
2966
+ color: MATRIX_FADE,
2967
+ children: "─┐"
2968
+ })
2969
+ ]
2970
+ }),
2971
+ showTitle && /* @__PURE__ */ jsx(Box, {
2972
+ marginBottom: 1,
2973
+ children: /* @__PURE__ */ jsxs(Text, {
2974
+ bold: true,
2975
+ color: VISUALIZER_PALETTE.head,
2976
+ children: [PHASE_LABELS[phase], " - PostHog"]
2977
+ })
2978
+ }),
2979
+ /* @__PURE__ */ jsx(PhaseBody, {
2980
+ phase,
2981
+ width: visualW,
2982
+ height: visualH
2983
+ }),
2984
+ showTransport && /* @__PURE__ */ jsxs(Box, {
2985
+ flexDirection: "row",
2986
+ marginTop: 1,
2987
+ gap: 2,
2988
+ children: [
2989
+ /* @__PURE__ */ jsxs(Text, {
2990
+ color: VISUALIZER_PALETTE.mid,
2991
+ children: [
2992
+ "[",
2993
+ timeStr,
2994
+ "]"
2995
+ ]
2996
+ }),
2997
+ /* @__PURE__ */ jsx(Text, {
2998
+ color: MATRIX_FADE,
2999
+ children: equalizer
3000
+ }),
3001
+ /* @__PURE__ */ jsx(Text, {
3002
+ color: VISUALIZER_PALETTE.mid,
3003
+ children: "WizardAmp"
3004
+ })
3005
+ ]
3006
+ })
3007
+ ]
3008
+ });
3009
+ };
3010
+ function formatElapsed(totalSec) {
3011
+ const m = Math.floor(totalSec / 60);
3012
+ const s = totalSec % 60;
3013
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
3014
+ }
3015
+ const BEAT_MS = 60 / 120 * 1e3;
3016
+ const EQ_BARS = 12;
3017
+ const EQ_TICK_MS = 67;
3018
+ const EQ_FALL_PER_FRAME = .08;
3019
+ function pulse(t, periodMs, decay, phaseMs = 0) {
3020
+ const since = (t - phaseMs + periodMs * 100) % periodMs;
3021
+ return Math.exp(-decay * since / periodMs);
3022
+ }
3023
+ const EQ_GLYPHS = "▁▂▃▄▅▆▇█";
3024
+ function renderMiniEqualizer(beatTimeMs, levels) {
3025
+ const kick = pulse(beatTimeMs, BEAT_MS, 4);
3026
+ const bass = pulse(beatTimeMs, BEAT_MS, 1.5);
3027
+ const clap = pulse(beatTimeMs, BEAT_MS * 2, 5, BEAT_MS);
3028
+ const hat8 = pulse(beatTimeMs, BEAT_MS / 2, 8);
3029
+ const hat16 = pulse(beatTimeMs, BEAT_MS / 4, 12);
3030
+ const pad = .2 + .15 * Math.sin(2 * Math.PI * beatTimeMs / 4e3);
3031
+ const mix = [
3032
+ bass * .7 + kick * .5 + pad,
3033
+ bass * .6 + kick * .6 + pad * .9,
3034
+ bass * .4 + kick * .7 + pad * .8,
3035
+ kick * .6 + clap * .3 + pad * .7,
3036
+ kick * .4 + clap * .5 + pad * .7,
3037
+ clap * .7 + hat8 * .3 + pad * .6,
3038
+ clap * .6 + hat8 * .5 + pad * .5,
3039
+ hat8 * .7 + clap * .3 + pad * .5,
3040
+ hat8 * .5 + hat16 * .4 + pad * .4,
3041
+ hat8 * .4 + hat16 * .5 + pad * .4,
3042
+ hat16 * .6 + hat8 * .3 + pad * .3,
3043
+ hat16 * .7 + pad * .3
3044
+ ];
3045
+ let out = "";
3046
+ for (let i = 0; i < EQ_BARS; i++) {
3047
+ const target = Math.min(1, mix[i]);
3048
+ levels[i] = Math.max(target, levels[i] - EQ_FALL_PER_FRAME);
3049
+ out += EQ_GLYPHS[Math.floor(levels[i] * 7)];
3050
+ }
3051
+ return out;
3052
+ }
3053
+ const PhaseBody = ({ phase, width, height }) => {
3054
+ switch (phase) {
3055
+ case "codebase-scan": return /* @__PURE__ */ jsx(MatrixRain, {
3056
+ width,
3057
+ height
3058
+ });
3059
+ case "skill-install": return /* @__PURE__ */ jsx(LibraryShelf, {
3060
+ width,
3061
+ height
3062
+ });
3063
+ case "dep-install": return /* @__PURE__ */ jsx(CrateStack, {
3064
+ width,
3065
+ height
3066
+ });
3067
+ case "code-edits": return /* @__PURE__ */ jsx(DiffCascade, {
3068
+ width,
3069
+ height
3070
+ });
3071
+ case "env-setup": return /* @__PURE__ */ jsx(Tumblers, {
3072
+ width,
3073
+ height
3074
+ });
3075
+ case "dashboards": return /* @__PURE__ */ jsx(DashboardGrid, {
3076
+ width,
3077
+ height
3078
+ });
3079
+ }
3080
+ };
3081
+ //#endregion
2164
3082
  //#region src/ui/tui/components/ServiceHealthList.tsx
2165
3083
  /**
2166
3084
  * ServiceHealthList — Shared component for displaying service health status.
@@ -5409,6 +6327,27 @@ const OutroScreen = ({ store }) => {
5409
6327
  bold: true,
5410
6328
  children: ["✔ ", outroData.message || "Done!"]
5411
6329
  }),
6330
+ outroData.primaryLink && /* @__PURE__ */ jsx(Box, {
6331
+ marginTop: 1,
6332
+ children: /* @__PURE__ */ jsxs(Text, { children: [
6333
+ outroData.primaryLink.label,
6334
+ ":",
6335
+ " ",
6336
+ /* @__PURE__ */ jsx(Text, {
6337
+ color: "cyan",
6338
+ children: outroData.primaryLink.url
6339
+ })
6340
+ ] })
6341
+ }),
6342
+ outroData.nextSteps && outroData.nextSteps.items.length > 0 && /* @__PURE__ */ jsxs(Box, {
6343
+ flexDirection: "column",
6344
+ marginTop: 1,
6345
+ children: [/* @__PURE__ */ jsx(Text, {
6346
+ color: "cyan",
6347
+ bold: true,
6348
+ children: outroData.nextSteps.heading
6349
+ }), outroData.nextSteps.items.map((item, i) => /* @__PURE__ */ jsxs(Text, { children: ["• ", item] }, i))]
6350
+ }),
5412
6351
  outroData.dashboardUrl && /* @__PURE__ */ jsx(Box, {
5413
6352
  marginTop: 1,
5414
6353
  children: /* @__PURE__ */ jsxs(Text, { children: [
@@ -5854,6 +6793,29 @@ const AiOptInRequiredScreen = ({ store }) => {
5854
6793
  });
5855
6794
  };
5856
6795
  //#endregion
5857
- export { SplitView as A, LogViewer as C, useStdoutDimensions as D, GroupedPickerMenu as E, WizardStore as M, ProgressList as O, EventPlanViewer as S, ConfirmationInput as T, LearnCard as _, SlackConnectScreen as a, TabContainer as b, AuditChecksViewer as c, McpScreen as d, IssueTable as f, TipsCard as g, ServiceHealthList as h, OutroScreen as i, CardLayout as j, LoadingBox as k, McpSuggestedPromptsScreen as l, SEVERITY_ORDER as m, SkillSourceInfo as n, AUDIT_AREA_SLIDES as o, SEVERITY_LABEL as p, useSkillEntry as r, VisualBox as s, AiOptInRequiredScreen as t, TAILORED_ROLES as u, ContentSequencer as v, ModalOverlay as w, ScreenContainer as x, HNViewer as y };
6796
+ //#region src/ui/tui/terminal.ts
6797
+ /**
6798
+ * Terminal setup shared by the wizard TUI and the primitives playground.
6799
+ *
6800
+ * Both enter the alternate screen buffer and paint a black background so
6801
+ * Ink renders on a consistent dark canvas regardless of the user's terminal
6802
+ * theme (light mode profiles included).
6803
+ */
6804
+ const RESET_ATTRS = "\x1B[0m";
6805
+ const CLEAR_SCREEN = "\x1B[2J";
6806
+ const CURSOR_HOME = "\x1B[H";
6807
+ const BG_BLACK = "\x1B[48;2;0;0;0m";
6808
+ const ENTER_ALT_SCREEN = "\x1B[?1049h";
6809
+ const LEAVE_ALT_SCREEN = "\x1B[?1049l";
6810
+ /** Enter alt screen and paint a black background. */
6811
+ function enterDarkTerminal() {
6812
+ process.stdout.write(ENTER_ALT_SCREEN + BG_BLACK + CLEAR_SCREEN + CURSOR_HOME);
6813
+ }
6814
+ /** Leave alt screen and reset attributes. */
6815
+ function releaseTerminal() {
6816
+ process.stdout.write(RESET_ATTRS + LEAVE_ALT_SCREEN);
6817
+ }
6818
+ //#endregion
6819
+ export { ConfirmationInput as A, TabContainer as C, LinkText as D, LogViewer as E, SplitView as F, CardLayout as I, WizardStore as L, useStdoutDimensions as M, ProgressList as N, extractUrls as O, LoadingBox as P, HNViewer as S, EventPlanViewer as T, ServiceHealthList as _, useSkillEntry as a, LearnCard as b, AUDIT_AREA_SLIDES as c, McpSuggestedPromptsScreen as d, TAILORED_ROLES as f, SEVERITY_ORDER as g, SEVERITY_LABEL as h, SkillSourceInfo as i, GroupedPickerMenu as j, ModalOverlay as k, VisualBox as l, IssueTable as m, releaseTerminal as n, OutroScreen as o, McpScreen as p, AiOptInRequiredScreen as r, SlackConnectScreen as s, enterDarkTerminal as t, AuditChecksViewer as u, VisualizerTab as v, ScreenContainer as w, ContentSequencer as x, TipsCard as y };
5858
6820
 
5859
- //# sourceMappingURL=AiOptInRequiredScreen-C-D9tN6r.js.map
6821
+ //# sourceMappingURL=terminal-DwAdsRPX.js.map