@algolia/wizard 0.46.0-rc.146.324 → 0.47.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 (2) hide show
  1. package/dist/main.js +618 -541
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
- import { Box as Box19, Text as Text19, useApp, useInput as useInput7, useWindowSize as useWindowSize8 } from "ink";
8
- import Spinner2 from "ink-spinner";
7
+ import { Box as Box20, Text as Text20, useApp, useInput as useInput7, useWindowSize as useWindowSize7 } from "ink";
8
+ import Spinner3 from "ink-spinner";
9
9
 
10
10
  // src/core/store.ts
11
11
  import { create } from "zustand";
@@ -259,10 +259,14 @@ var useWizard = create((set, get) => ({
259
259
  error: null,
260
260
  inputReq: null,
261
261
  _resolve: null,
262
+ settingUpAppId: null,
263
+ setSettingUpApp: (appId) => set({ settingUpAppId: appId }),
262
264
  // `endAuth` lands on 'preflight', not 'idle': sign-in happens after the
263
265
  // welcome screen, so going back would gate the run a second time.
264
266
  beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
265
- endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
267
+ endAuth: () => set(
268
+ (s) => s.phase === "authenticating" ? { phase: "preflight", cliOutput: [] } : {}
269
+ ),
266
270
  confirmStart: () => set(
267
271
  (s) => s.phase === "idle" ? { phase: "preflight", homeScreen: "home" } : {}
268
272
  ),
@@ -409,7 +413,8 @@ var useWizard = create((set, get) => ({
409
413
  logs: [],
410
414
  error: null,
411
415
  inputReq: null,
412
- _resolve: null
416
+ _resolve: null,
417
+ settingUpAppId: null
413
418
  });
414
419
  }
415
420
  }));
@@ -499,112 +504,23 @@ function CliOutput() {
499
504
  }
500
505
 
501
506
  // src/ui/Notices.tsx
502
- import { Box as Box4, Text as Text4, useWindowSize as useWindowSize4 } from "ink";
503
- import { useEffect as useEffect2, useState as useState3 } from "react";
504
-
505
- // src/ui/ScrollView.tsx
506
- import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
507
- import { useCallback, useLayoutEffect, useRef, useState } from "react";
508
- import { jsxs as jsxs2 } from "react/jsx-runtime";
509
- var INDICATOR_ROWS = 2;
510
- function fittedWidth(node, columns) {
511
- let left = 0;
512
- for (let n = node; n; n = n.parentNode) {
513
- left += n.yogaNode?.getComputedLeft() ?? 0;
514
- }
515
- return Math.max(Math.min(measureElement(node).width, columns - left), 0);
516
- }
517
- function useScrollWindow({
518
- itemCount,
519
- rowHeight = 1,
520
- followBottom = false
521
- }) {
522
- const viewportRef = useRef(null);
523
- const { columns } = useWindowSize2();
524
- const [size, setSize] = useState(
525
- null
526
- );
527
- useLayoutEffect(() => {
528
- if (!viewportRef.current) return;
529
- const width = fittedWidth(viewportRef.current, columns);
530
- const { height } = measureElement(viewportRef.current);
531
- setSize(
532
- (prev) => prev?.width === width && prev.height === height ? prev : { width, height }
533
- );
534
- });
535
- const capacity = size === null || itemCount * rowHeight <= size.height ? itemCount : Math.max(Math.floor((size.height - INDICATOR_ROWS) / rowHeight), 1);
536
- const maxOffset = Math.max(itemCount - capacity, 0);
537
- const [offset, setOffset] = useState(0);
538
- const prevMaxOffsetRef = useRef(0);
539
- useLayoutEffect(() => {
540
- const wasAtBottom = offset >= prevMaxOffsetRef.current;
541
- prevMaxOffsetRef.current = maxOffset;
542
- setOffset(
543
- (o) => followBottom && wasAtBottom ? maxOffset : Math.min(o, maxOffset)
544
- );
545
- }, [maxOffset, followBottom]);
546
- const scrollBy = useCallback(
547
- (delta) => {
548
- setOffset((o) => Math.min(Math.max(o + delta, 0), maxOffset));
549
- },
550
- [maxOffset]
551
- );
552
- const revealIndex = useCallback(
553
- (index) => {
554
- setOffset((o) => {
555
- if (index < o) return index;
556
- if (index >= o + capacity) {
557
- return Math.min(index - capacity + 1, maxOffset);
558
- }
559
- return o;
560
- });
561
- },
562
- [capacity, maxOffset]
563
- );
564
- const visibleCount = Math.min(capacity, Math.max(itemCount - offset, 0));
565
- return {
566
- viewportRef,
567
- measured: size !== null,
568
- width: size?.width ?? columns,
569
- offset,
570
- capacity,
571
- maxOffset,
572
- hiddenAbove: Math.min(offset, itemCount),
573
- hiddenBelow: Math.max(itemCount - offset - visibleCount, 0),
574
- scrollBy,
575
- revealIndex
576
- };
577
- }
578
- function ScrollView({ scroll, children }) {
579
- return /* @__PURE__ */ jsxs2(Box2, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
580
- scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs2(Text2, { color: COLORS.dim, children: [
581
- "\u2191 ",
582
- scroll.hiddenAbove,
583
- " more"
584
- ] }),
585
- children,
586
- scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs2(Text2, { color: COLORS.dim, children: [
587
- "\u2193 ",
588
- scroll.hiddenBelow,
589
- " more"
590
- ] })
591
- ] });
592
- }
507
+ import { Box as Box3, Text as Text3 } from "ink";
508
+ import { useEffect as useEffect2, useState as useState2 } from "react";
593
509
 
594
510
  // src/ui/Table.tsx
595
- import { Box as Box3, Text as Text3, measureElement as measureElement2, useWindowSize as useWindowSize3 } from "ink";
596
- import { useEffect, useRef as useRef2, useState as useState2 } from "react";
511
+ import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
512
+ import { useEffect, useRef, useState } from "react";
597
513
  import { jsx } from "react/jsx-runtime";
598
514
  function Table({ columns, rows }) {
599
- const { columns: termCols } = useWindowSize3();
600
- const ref = useRef2(null);
601
- const [width, setWidth] = useState2(0);
515
+ const { columns: termCols } = useWindowSize2();
516
+ const ref = useRef(null);
517
+ const [width, setWidth] = useState(0);
602
518
  useEffect(() => {
603
- if (ref.current) setWidth(measureElement2(ref.current).width);
519
+ if (ref.current) setWidth(measureElement(ref.current).width);
604
520
  }, [termCols, columns, rows]);
605
521
  if (rows.length === 0) return null;
606
522
  const lines = formatTable(columns, rows, width || void 0);
607
- return /* @__PURE__ */ jsx(Box3, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text3, { color: COLORS.primary, wrap: "truncate", children: line }, `tbl-${i}`)) });
523
+ return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { color: COLORS.primary, wrap: "truncate", children: line }, `tbl-${i}`)) });
608
524
  }
609
525
  function formatTable(columns, rows, width) {
610
526
  const natural = columns.map(
@@ -645,7 +561,7 @@ function resize(widths, budget) {
645
561
  var truncate = (s, width) => s.length <= width ? s : width <= 1 ? s.slice(0, width) : `${s.slice(0, width - 1)}\u2026`;
646
562
 
647
563
  // src/ui/Notices.tsx
648
- import { jsx as jsx2 } from "react/jsx-runtime";
564
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
649
565
  var AGENT_MARKER = "\u2726";
650
566
  var PULSE_STEPS = 12;
651
567
  var PULSE_STEP_MS = 150;
@@ -667,48 +583,14 @@ function parseHex(hex) {
667
583
  b: parseInt(n.slice(4, 6), 16)
668
584
  };
669
585
  }
670
- function flattenRows(notices, width) {
671
- const rows = [];
672
- notices.forEach((notice, noticeIndex) => {
673
- if (noticeIndex > 0 && notice.table) {
674
- rows.push({ noticeIndex, text: " ", plain: true });
675
- }
676
- notice.messages?.forEach((m, j) => {
677
- const line = typeof m === "string" ? { text: m } : m;
678
- rows.push({
679
- noticeIndex,
680
- text: `${j === 0 ? `${AGENT_MARKER} ` : " "}${line.text}`,
681
- color: line.color,
682
- bold: line.bold
683
- });
684
- });
685
- if (notice.table) {
686
- for (const text of formatTable(
687
- notice.table.columns,
688
- notice.table.rows,
689
- width
690
- )) {
691
- rows.push({ noticeIndex, text, plain: true });
692
- }
693
- }
694
- });
695
- return rows;
696
- }
697
586
  function Notices({
698
587
  showAll = false,
699
588
  border = true
700
589
  } = {}) {
701
590
  const notices = useWizard((s) => s.notices);
702
- const { columns } = useWindowSize4();
703
- const latestIndex = notices.length - 1;
704
- const visible = showAll ? notices : latestIndex >= 0 ? [notices[latestIndex]] : [];
705
- const latestRowIndex = showAll ? latestIndex : 0;
706
- const measuredRows = flattenRows(visible, columns);
707
- const scroll = useScrollWindow({
708
- itemCount: measuredRows.length,
709
- followBottom: true
710
- });
711
- const [pulseStep, setPulseStep] = useState3(0);
591
+ const latest = notices[notices.length - 1];
592
+ const visible = showAll ? notices : latest ? [latest] : [];
593
+ const [pulseStep, setPulseStep] = useState2(0);
712
594
  useEffect2(() => {
713
595
  let direction = 1;
714
596
  const id = setInterval(() => {
@@ -723,10 +605,8 @@ function Notices({
723
605
  }, []);
724
606
  if (visible.length === 0) return null;
725
607
  const pulseColor = PULSE_COLORS[pulseStep];
726
- const rows = scroll.width === columns ? measuredRows : flattenRows(visible, scroll.width);
727
- const windowed = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
728
608
  return /* @__PURE__ */ jsx2(
729
- Box4,
609
+ Box3,
730
610
  {
731
611
  borderColor: border ? COLORS.border : void 0,
732
612
  borderStyle: border ? "single" : void 0,
@@ -736,36 +616,45 @@ function Notices({
736
616
  borderBottom: false,
737
617
  paddingTop: border ? 1 : 0,
738
618
  flexDirection: "column",
739
- flexGrow: 1,
740
- minHeight: 0,
741
- children: /* @__PURE__ */ jsx2(ScrollView, { scroll, children: windowed.map((row, i) => /* @__PURE__ */ jsx2(
742
- Text4,
743
- {
744
- color: row.plain ? COLORS.primary : row.color ?? (row.noticeIndex === latestRowIndex ? pulseColor : COLORS.dim),
745
- bold: row.bold,
746
- wrap: "truncate",
747
- children: row.text
748
- },
749
- `notice-row-${scroll.offset + i}`
750
- )) })
619
+ gap: 1,
620
+ children: visible.map((notice, i) => /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
621
+ notice.messages?.map((m, j) => {
622
+ const line = typeof m === "string" ? { text: m } : m;
623
+ const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
624
+ return /* @__PURE__ */ jsxs2(
625
+ Text3,
626
+ {
627
+ color: !line.color ? notice === latest ? pulseColor : COLORS.dim : line.color ?? COLORS.dim,
628
+ bold: line.bold,
629
+ children: [
630
+ prefix,
631
+ line.text
632
+ ]
633
+ },
634
+ `notice-line-${j}`
635
+ );
636
+ }),
637
+ notice.table && /* @__PURE__ */ jsx2(Table, { columns: notice.table.columns, rows: notice.table.rows })
638
+ ] }, `notice-${i}`))
751
639
  }
752
640
  );
753
641
  }
754
642
 
755
643
  // src/ui/PromptInput.tsx
756
644
  import { Box as Box9, Text as Text9 } from "ink";
645
+ import Spinner from "ink-spinner";
757
646
  import TextInput from "ink-text-input";
758
647
  import { useState as useState6 } from "react";
759
648
 
760
649
  // src/ui/CommandApproval.tsx
761
- import { Box as Box7, Text as Text7 } from "ink";
650
+ import { Box as Box6, Text as Text6 } from "ink";
762
651
 
763
652
  // src/ui/DecisionSelect.tsx
764
- import { Box as Box6, Text as Text6, useInput } from "ink";
765
- import { useState as useState4 } from "react";
653
+ import { Box as Box5, Text as Text5, useInput } from "ink";
654
+ import { useState as useState3 } from "react";
766
655
 
767
656
  // src/ui/SelectRow.tsx
768
- import { Box as Box5, Text as Text5 } from "ink";
657
+ import { Box as Box4, Text as Text4 } from "ink";
769
658
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
770
659
  function SelectRow({
771
660
  highlighted,
@@ -774,25 +663,27 @@ function SelectRow({
774
663
  labelWidth,
775
664
  highlightBackground = true,
776
665
  usePadding = false,
666
+ disabled = false,
777
667
  children
778
668
  }) {
779
- const labelColor = highlighted ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
669
+ const active = highlighted && !disabled;
670
+ const labelColor = disabled ? COLORS.dim : active ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
780
671
  return /* @__PURE__ */ jsxs3(
781
- Box5,
672
+ Box4,
782
673
  {
783
674
  width,
784
675
  paddingX: usePadding ? 1 : 0,
785
676
  paddingY: usePadding ? 1 : 0,
786
- backgroundColor: highlighted && highlightBackground ? COLORS.highlight.bg : void 0,
677
+ backgroundColor: active && highlightBackground ? COLORS.highlight.bg : void 0,
787
678
  children: [
788
- /* @__PURE__ */ jsx3(Box5, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
789
- Text5,
679
+ /* @__PURE__ */ jsx3(Box4, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
680
+ Text4,
790
681
  {
791
682
  color: labelColor,
792
- bold: highlighted && !highlightBackground,
683
+ bold: active && !highlightBackground,
793
684
  wrap: "truncate",
794
685
  children: [
795
- highlighted ? "\u276F " : " ",
686
+ active ? "\u276F " : " ",
796
687
  label
797
688
  ]
798
689
  }
@@ -809,7 +700,7 @@ function DecisionSelect({
809
700
  actions,
810
701
  onDecide
811
702
  }) {
812
- const [index, setIndex] = useState4(0);
703
+ const [index, setIndex] = useState3(0);
813
704
  useInput((input, key) => {
814
705
  if (key.upArrow || input === "k") {
815
706
  setIndex((i) => (i - 1 + actions.length) % actions.length);
@@ -821,8 +712,8 @@ function DecisionSelect({
821
712
  onDecide(actions[1].value);
822
713
  }
823
714
  });
824
- return /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", gap: 1, children: [
825
- /* @__PURE__ */ jsx4(Box6, { flexDirection: "column", children: actions.map((action, i) => /* @__PURE__ */ jsx4(
715
+ return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, children: [
716
+ /* @__PURE__ */ jsx4(Box5, { flexDirection: "column", children: actions.map((action, i) => /* @__PURE__ */ jsx4(
826
717
  SelectRow,
827
718
  {
828
719
  highlighted: i === index,
@@ -831,14 +722,14 @@ function DecisionSelect({
831
722
  },
832
723
  action.label
833
724
  )) }),
834
- /* @__PURE__ */ jsxs4(Box6, { gap: 2, children: [
835
- /* @__PURE__ */ jsxs4(Text6, { children: [
836
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
837
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: " move" })
725
+ /* @__PURE__ */ jsxs4(Box5, { gap: 2, children: [
726
+ /* @__PURE__ */ jsxs4(Text5, { children: [
727
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
728
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: " move" })
838
729
  ] }),
839
- /* @__PURE__ */ jsxs4(Text6, { children: [
840
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.primary, children: "[enter]" }),
841
- /* @__PURE__ */ jsx4(Text6, { color: COLORS.dim, children: " confirm" })
730
+ /* @__PURE__ */ jsxs4(Text5, { children: [
731
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: "[enter]" }),
732
+ /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: " confirm" })
842
733
  ] })
843
734
  ] })
844
735
  ] });
@@ -850,10 +741,10 @@ function CommandApproval({
850
741
  command,
851
742
  onDecide
852
743
  }) {
853
- return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", gap: 1, children: [
854
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.primary, bold: true, children: "Run this command?" }),
744
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
745
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, bold: true, children: "Run this command?" }),
855
746
  /* @__PURE__ */ jsxs5(
856
- Box7,
747
+ Box6,
857
748
  {
858
749
  flexDirection: "column",
859
750
  paddingLeft: 2,
@@ -864,17 +755,17 @@ function CommandApproval({
864
755
  borderRight: false,
865
756
  gap: 1,
866
757
  children: [
867
- /* @__PURE__ */ jsxs5(Box7, { children: [
868
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: "$ " }),
869
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.strong, wrap: "wrap", children: command.command })
758
+ /* @__PURE__ */ jsxs5(Box6, { children: [
759
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "$ " }),
760
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.strong, wrap: "wrap", children: command.command })
870
761
  ] }),
871
- /* @__PURE__ */ jsxs5(Box7, { gap: 1, children: [
872
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: "in:" }),
873
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
762
+ /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
763
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "in:" }),
764
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, wrap: "wrap", children: command.cwd })
874
765
  ] }),
875
- command.explanation && /* @__PURE__ */ jsxs5(Box7, { gap: 1, children: [
876
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.muted, children: "why:" }),
877
- /* @__PURE__ */ jsx5(Text7, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
766
+ command.explanation && /* @__PURE__ */ jsxs5(Box6, { gap: 1, children: [
767
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: "why:" }),
768
+ /* @__PURE__ */ jsx5(Text6, { color: COLORS.accent, wrap: "wrap", children: command.explanation })
878
769
  ] })
879
770
  ]
880
771
  }
@@ -893,14 +784,113 @@ function CommandApproval({
893
784
  }
894
785
 
895
786
  // src/ui/SelectPrompt.tsx
896
- import { Box as Box8, Text as Text8, useInput as useInput2, useWindowSize as useWindowSize5 } from "ink";
787
+ import { Box as Box8, Text as Text8, useInput as useInput2, useWindowSize as useWindowSize4 } from "ink";
897
788
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
898
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
789
+
790
+ // src/ui/ScrollView.tsx
791
+ import { Box as Box7, Text as Text7, measureElement as measureElement2, useWindowSize as useWindowSize3 } from "ink";
792
+ import { useCallback, useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
793
+ import { jsxs as jsxs6 } from "react/jsx-runtime";
794
+ var INDICATOR_ROWS = 2;
795
+ function fittedWidth(node, columns) {
796
+ let left = 0;
797
+ for (let n = node; n; n = n.parentNode) {
798
+ left += n.yogaNode?.getComputedLeft() ?? 0;
799
+ }
800
+ return Math.max(Math.min(measureElement2(node).width, columns - left), 0);
801
+ }
802
+ function useScrollWindow({
803
+ itemCount,
804
+ rowHeight = 1,
805
+ followBottom = false
806
+ }) {
807
+ const viewportRef = useRef2(null);
808
+ const { columns } = useWindowSize3();
809
+ const [size, setSize] = useState4(
810
+ null
811
+ );
812
+ useLayoutEffect(() => {
813
+ if (!viewportRef.current) return;
814
+ const width = fittedWidth(viewportRef.current, columns);
815
+ const { height } = measureElement2(viewportRef.current);
816
+ setSize(
817
+ (prev) => prev?.width === width && prev.height === height ? prev : { width, height }
818
+ );
819
+ });
820
+ const capacity = size === null || itemCount * rowHeight <= size.height ? itemCount : Math.max(Math.floor((size.height - INDICATOR_ROWS) / rowHeight), 1);
821
+ const maxOffset = Math.max(itemCount - capacity, 0);
822
+ const [offset, setOffset] = useState4(0);
823
+ const prevMaxOffsetRef = useRef2(0);
824
+ useLayoutEffect(() => {
825
+ const wasAtBottom = offset >= prevMaxOffsetRef.current;
826
+ prevMaxOffsetRef.current = maxOffset;
827
+ setOffset(
828
+ (o) => followBottom && wasAtBottom ? maxOffset : Math.min(o, maxOffset)
829
+ );
830
+ }, [maxOffset, followBottom]);
831
+ const scrollBy = useCallback(
832
+ (delta) => {
833
+ setOffset((o) => Math.min(Math.max(o + delta, 0), maxOffset));
834
+ },
835
+ [maxOffset]
836
+ );
837
+ const revealIndex = useCallback(
838
+ (index) => {
839
+ setOffset((o) => {
840
+ if (index < o) return index;
841
+ if (index >= o + capacity) {
842
+ return Math.min(index - capacity + 1, maxOffset);
843
+ }
844
+ return o;
845
+ });
846
+ },
847
+ [capacity, maxOffset]
848
+ );
849
+ const visibleCount = Math.min(capacity, Math.max(itemCount - offset, 0));
850
+ return {
851
+ viewportRef,
852
+ measured: size !== null,
853
+ width: size?.width ?? columns,
854
+ offset,
855
+ capacity,
856
+ maxOffset,
857
+ hiddenAbove: Math.min(offset, itemCount),
858
+ hiddenBelow: Math.max(itemCount - offset - visibleCount, 0),
859
+ scrollBy,
860
+ revealIndex
861
+ };
862
+ }
863
+ function ScrollView({ scroll, children }) {
864
+ return /* @__PURE__ */ jsxs6(Box7, { ref: scroll.viewportRef, flexDirection: "column", flexGrow: 1, children: [
865
+ scroll.hiddenAbove > 0 && /* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
866
+ "\u2191 ",
867
+ scroll.hiddenAbove,
868
+ " more"
869
+ ] }),
870
+ children,
871
+ scroll.hiddenBelow > 0 && /* @__PURE__ */ jsxs6(Text7, { color: COLORS.dim, children: [
872
+ "\u2193 ",
873
+ scroll.hiddenBelow,
874
+ " more"
875
+ ] })
876
+ ] });
877
+ }
878
+
879
+ // src/ui/SelectPrompt.tsx
880
+ import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
899
881
  var CANCEL = "cancel";
900
882
  var ARROW_WIDTH = 4;
901
883
  var COLUMN_GAP = 2;
902
884
  var BAR_PADDING = 2;
903
885
  var ROW_HEIGHT = 3;
886
+ function nextSelectableIndex(start, dir, rowCount, isDisabled) {
887
+ let i = start;
888
+ for (let step = 0; step < rowCount; step++) {
889
+ i = (i + dir + rowCount) % rowCount;
890
+ if (!isDisabled(i)) return i;
891
+ }
892
+ return start;
893
+ }
904
894
  function SelectPrompt({
905
895
  options,
906
896
  onSelect,
@@ -912,21 +902,24 @@ function SelectPrompt({
912
902
  multi,
913
903
  cancelable,
914
904
  secondary,
905
+ disabled,
915
906
  defaultSelectedIndex = 0
916
907
  }) {
917
- const [index, setIndex] = useState5(
918
- () => defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0
919
- );
920
- const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
921
908
  const hasCancel = Boolean(multi || cancelable);
922
909
  const rows = hasCancel ? [...options, "Cancel"] : options;
923
910
  const cancelIndex = hasCancel ? options.length : -1;
911
+ const isDisabled = (i) => i !== cancelIndex && Boolean(disabled?.[i]);
912
+ const [index, setIndex] = useState5(() => {
913
+ const start = defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0;
914
+ return isDisabled(start) ? nextSelectableIndex(start, 1, rows.length, isDisabled) : start;
915
+ });
916
+ const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
924
917
  const hints = [];
925
918
  if (rows.length > 1) hints.push({ key: "[\u2191] [\u2193]", label: "move" });
926
919
  if (multi) hints.push({ key: "[space]", label: "select" });
927
920
  hints.push({ key: "[enter]", label: "confirm" });
928
921
  const containerRef = useRef3(null);
929
- const { columns } = useWindowSize5();
922
+ const { columns } = useWindowSize4();
930
923
  const [width, setWidth] = useState5(columns);
931
924
  useLayoutEffect2(() => {
932
925
  if (!containerRef.current) return;
@@ -960,10 +953,10 @@ function SelectPrompt({
960
953
  useInput2((input, key) => {
961
954
  if (rows.length === 0) return;
962
955
  if (key.upArrow || input === "k") {
963
- setIndex((i) => (i - 1 + rows.length) % rows.length);
956
+ setIndex((i) => nextSelectableIndex(i, -1, rows.length, isDisabled));
964
957
  } else if (key.downArrow || input === "j") {
965
- setIndex((i) => (i + 1) % rows.length);
966
- } else if (multi && input === " " && index !== cancelIndex) {
958
+ setIndex((i) => nextSelectableIndex(i, 1, rows.length, isDisabled));
959
+ } else if (multi && input === " " && index !== cancelIndex && !isDisabled(index)) {
967
960
  setChecked((prev) => {
968
961
  const next = new Set(prev);
969
962
  if (next.has(index)) next.delete(index);
@@ -973,19 +966,21 @@ function SelectPrompt({
973
966
  } else if (key.return) {
974
967
  if (index === cancelIndex) {
975
968
  onSelect(CANCEL);
969
+ } else if (isDisabled(index)) {
976
970
  } else if (!multi) {
977
971
  onSelect(options[index]);
978
- } else if (checked.size > 0) {
979
- onSelect(options.filter((_, i) => checked.has(i)));
972
+ } else {
973
+ const picked = options.filter((_, i) => checked.has(i) && !isDisabled(i));
974
+ if (picked.length > 0) onSelect(picked);
980
975
  }
981
976
  }
982
977
  });
983
- return /* @__PURE__ */ jsx6(Box8, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", gap: 1, width, children: [
984
- /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
978
+ return /* @__PURE__ */ jsx6(Box8, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, width, children: [
979
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
985
980
  error && /* @__PURE__ */ jsx6(Text8, { color: COLORS.danger, children: error }),
986
981
  messages?.map((m, i) => /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: m }, `msg-${i}`)),
987
982
  table && /* @__PURE__ */ jsx6(Table, { columns: table.columns, rows: table.rows }),
988
- /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", children: [
983
+ /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
989
984
  question && /* @__PURE__ */ jsx6(Text8, { color: COLORS.muted, children: question }),
990
985
  helpText && /* @__PURE__ */ jsx6(Text8, { color: COLORS.dim, children: helpText })
991
986
  ] })
@@ -997,10 +992,12 @@ function SelectPrompt({
997
992
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
998
993
  const sec = isCancel ? void 0 : secondary?.[i];
999
994
  const isText = sec?.kind === "text";
1000
- return /* @__PURE__ */ jsxs6(
995
+ const rowDisabled = isDisabled(i);
996
+ return /* @__PURE__ */ jsxs7(
1001
997
  SelectRow,
1002
998
  {
1003
999
  highlighted,
1000
+ disabled: rowDisabled,
1004
1001
  width: isText ? "100%" : barWidth,
1005
1002
  labelWidth: isText ? labelWidth : barLabelWidth,
1006
1003
  label: `${bullet}${option}`,
@@ -1010,7 +1007,7 @@ function SelectPrompt({
1010
1007
  Text8,
1011
1008
  {
1012
1009
  wrap: "truncate",
1013
- color: highlighted ? COLORS.primary : COLORS.muted,
1010
+ color: rowDisabled ? COLORS.dim : highlighted ? COLORS.primary : COLORS.muted,
1014
1011
  children: sec.value
1015
1012
  }
1016
1013
  ) }),
@@ -1020,10 +1017,10 @@ function SelectPrompt({
1020
1017
  `row-${i}`
1021
1018
  );
1022
1019
  }) }),
1023
- /* @__PURE__ */ jsx6(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx6(Text8, { color: COLORS.dim, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs6(Text8, { color: COLORS.dim, children: [
1020
+ /* @__PURE__ */ jsx6(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx6(Text8, { color: COLORS.dim, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
1024
1021
  i > 0 ? " " : "",
1025
1022
  /* @__PURE__ */ jsx6(Text8, { color: COLORS.primary, children: key }),
1026
- /* @__PURE__ */ jsxs6(Text8, { color: COLORS.dim, children: [
1023
+ /* @__PURE__ */ jsxs7(Text8, { color: COLORS.dim, children: [
1027
1024
  " ",
1028
1025
  label
1029
1026
  ] })
@@ -1032,14 +1029,14 @@ function SelectPrompt({
1032
1029
  }
1033
1030
 
1034
1031
  // src/ui/PromptInput.tsx
1035
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1032
+ import { jsx as jsx7, jsxs as jsxs8 } from "react/jsx-runtime";
1036
1033
  var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
1037
1034
  function EnterToContinuePrompt({
1038
1035
  question,
1039
1036
  messages,
1040
1037
  onDecide
1041
1038
  }) {
1042
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", gap: 1, children: [
1039
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 1, children: [
1043
1040
  messages?.map((m, i) => /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: m }, `msg-${i}`)),
1044
1041
  question && /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: question }),
1045
1042
  /* @__PURE__ */ jsx7(
@@ -1057,11 +1054,23 @@ function EnterToContinuePrompt({
1057
1054
  function PromptInput() {
1058
1055
  const { phase, inputReq, submitInput } = useWizard();
1059
1056
  const [draft, setDraft] = useState6("");
1057
+ const [submittedReq, setSubmittedReq] = useState6(null);
1060
1058
  const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1059
+ const submitting = inputReq != null && submittedReq === inputReq;
1061
1060
  if (phase === "done" || phase === "error") {
1062
1061
  return /* @__PURE__ */ jsx7(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text9, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1063
1062
  }
1064
1063
  if (phase !== "awaitingInput" || !inputReq) return null;
1064
+ if (submitting) {
1065
+ return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.strong, bold: true, children: [
1066
+ /* @__PURE__ */ jsx7(Spinner, { type: "dots" }),
1067
+ " Continuing\u2026"
1068
+ ] }) });
1069
+ }
1070
+ const handleSubmit = (value) => {
1071
+ setSubmittedReq(inputReq);
1072
+ submitInput(value);
1073
+ };
1065
1074
  if (inputReq.promptType === "multipleChoice") {
1066
1075
  return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
1067
1076
  SelectPrompt,
@@ -1072,10 +1081,11 @@ function PromptInput() {
1072
1081
  table: inputReq.table,
1073
1082
  options: inputReq.options,
1074
1083
  secondary: inputReq.secondary,
1084
+ disabled: inputReq.disabled,
1075
1085
  defaultSelectedIndex: inputReq.defaultSelectedIndex,
1076
1086
  cancelable: inputReq.cancelable,
1077
1087
  error: inputReq.error,
1078
- onSelect: submitInput
1088
+ onSelect: handleSubmit
1079
1089
  },
1080
1090
  promptKey
1081
1091
  ) });
@@ -1091,7 +1101,7 @@ function PromptInput() {
1091
1101
  table: inputReq.table,
1092
1102
  options: inputReq.options,
1093
1103
  error: inputReq.error,
1094
- onSelect: submitInput
1104
+ onSelect: handleSubmit
1095
1105
  },
1096
1106
  promptKey
1097
1107
  ) });
@@ -1103,7 +1113,7 @@ function PromptInput() {
1103
1113
  question: inputReq.prompt,
1104
1114
  messages: inputReq.messages,
1105
1115
  options: ["Continue"],
1106
- onSelect: submitInput
1116
+ onSelect: handleSubmit
1107
1117
  },
1108
1118
  promptKey
1109
1119
  ) });
@@ -1130,16 +1140,16 @@ function PromptInput() {
1130
1140
  messages: inputReq.messages,
1131
1141
  options: labels,
1132
1142
  secondary: inputReq.secondary,
1133
- onSelect: (value) => submitInput(value === labels[0])
1143
+ onSelect: (value) => handleSubmit(value === labels[0])
1134
1144
  },
1135
1145
  promptKey
1136
1146
  ) });
1137
1147
  }
1138
- return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", children: [
1148
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
1139
1149
  inputReq.error && /* @__PURE__ */ jsx7(Text9, { color: COLORS.danger, children: inputReq.error }),
1140
1150
  inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx7(Text9, { color: COLORS.muted, children: m }, `msg-${i}`)),
1141
- /* @__PURE__ */ jsxs7(Box9, { children: [
1142
- /* @__PURE__ */ jsxs7(Text9, { color: COLORS.primary, children: [
1151
+ /* @__PURE__ */ jsxs8(Box9, { children: [
1152
+ /* @__PURE__ */ jsxs8(Text9, { color: COLORS.primary, children: [
1143
1153
  inputReq.prompt,
1144
1154
  " "
1145
1155
  ] }),
@@ -1161,7 +1171,7 @@ function PromptInput() {
1161
1171
  // src/ui/Review.tsx
1162
1172
  import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
1163
1173
  import { useLayoutEffect as useLayoutEffect3, useState as useState7 } from "react";
1164
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1174
+ import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1165
1175
  var INGEST_SCRIPT = ".algolia-wizard/ingest.sh";
1166
1176
  function wrapText(text, width) {
1167
1177
  if (width <= 0) return [text];
@@ -1259,8 +1269,8 @@ function ReviewPanel({ review: review2 }) {
1259
1269
  { isActive: scrollable }
1260
1270
  );
1261
1271
  const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
1262
- return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", flexGrow: 1, children: [
1263
- /* @__PURE__ */ jsx8(ScrollView, { scroll, children: visible.map((row, i) => /* @__PURE__ */ jsxs8(Text10, { wrap: "truncate", children: [
1272
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", flexGrow: 1, children: [
1273
+ /* @__PURE__ */ jsx8(ScrollView, { scroll, children: visible.map((row, i) => /* @__PURE__ */ jsxs9(Text10, { wrap: "truncate", children: [
1264
1274
  row.prefix && /* @__PURE__ */ jsx8(Text10, { color: row.prefixColor ?? row.color, bold: row.bold, children: row.prefix }),
1265
1275
  /* @__PURE__ */ jsx8(Text10, { color: row.color, bold: row.bold, children: row.text })
1266
1276
  ] }, `review-row-${i}`)) }),
@@ -1311,6 +1321,10 @@ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, rm } f
1311
1321
  import { join as join4 } from "node:path";
1312
1322
  var isStepVisible = (s) => s.visible !== false;
1313
1323
  var stateFile = (workflowId) => join4(stateDir(), `state-${workflowId}.json`);
1324
+ function runAppId(state) {
1325
+ const appId = state.algoliaState.appId;
1326
+ return typeof appId === "string" && appId ? appId : void 0;
1327
+ }
1314
1328
  async function loadWorkflowState(workflowId) {
1315
1329
  try {
1316
1330
  const raw = await readFile2(stateFile(workflowId), "utf8");
@@ -1590,7 +1604,11 @@ function reconcileWorkflowState(state, workflow) {
1590
1604
  }
1591
1605
  return state;
1592
1606
  }
1593
- function initWorkflowState(workflow, now) {
1607
+ async function loadResumableState(workflow) {
1608
+ const persisted = await loadWorkflowState(workflow.id);
1609
+ return persisted ? reconcileWorkflowState(persisted, workflow) : null;
1610
+ }
1611
+ function initWorkflowState(workflow, now, appId) {
1594
1612
  return {
1595
1613
  workflowId: workflow.id,
1596
1614
  startedAt: now,
@@ -1602,7 +1620,7 @@ function initWorkflowState(workflow, now) {
1602
1620
  visible: s.visible,
1603
1621
  status: "pending"
1604
1622
  })),
1605
- algoliaState: {},
1623
+ algoliaState: appId ? { appId } : {},
1606
1624
  userInputs: {}
1607
1625
  };
1608
1626
  }
@@ -1692,11 +1710,11 @@ async function runStep(state, index, step, appId) {
1692
1710
  durationMs: Date.now() - startedAt
1693
1711
  });
1694
1712
  }
1695
- async function runWorkflow(workflow, appId) {
1713
+ async function runWorkflow(workflow, appId, resumableState) {
1696
1714
  const store = useWizard.getState();
1697
1715
  try {
1698
- const persisted = await loadWorkflowState(workflow.id);
1699
- const state = (persisted && reconcileWorkflowState(persisted, workflow)) ?? initWorkflowState(workflow, nowIso());
1716
+ const resolved2 = resumableState !== void 0 ? resumableState : await loadResumableState(workflow);
1717
+ const state = resolved2 ?? initWorkflowState(workflow, nowIso(), appId);
1700
1718
  ensureExecutedStepCount(state);
1701
1719
  store.startWorkflow(
1702
1720
  {
@@ -1788,14 +1806,89 @@ async function listIndices() {
1788
1806
  return items.map((i) => ({ name: i.name, entries: i.entries })).sort((a, b) => a.name.localeCompare(b.name));
1789
1807
  }
1790
1808
 
1809
+ // src/lib/algoliaApp.ts
1810
+ import { z as z5 } from "zod";
1811
+ var applicationSchema = z5.object({
1812
+ id: z5.string().min(1),
1813
+ name: z5.string().default(""),
1814
+ plan: z5.string().optional()
1815
+ });
1816
+ var listSchema = z5.array(
1817
+ z5.object({
1818
+ id: z5.string().min(1),
1819
+ name: z5.string().default(""),
1820
+ plan_label: z5.string().optional(),
1821
+ status: z5.string().optional(),
1822
+ acl: z5.array(z5.string()).optional()
1823
+ }).transform(({ id, name, plan_label, status, acl }) => ({
1824
+ id,
1825
+ name,
1826
+ plan: plan_label,
1827
+ status,
1828
+ acl
1829
+ }))
1830
+ );
1831
+ function canSelectApplication(app) {
1832
+ return app.status === "active" && (app.acl ?? []).includes("keys");
1833
+ }
1834
+ async function currentApplication() {
1835
+ let raw;
1836
+ try {
1837
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1838
+ } catch {
1839
+ return null;
1840
+ }
1841
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1842
+ return parsed.success ? parsed.data : null;
1843
+ }
1844
+ async function requireApplication() {
1845
+ const app = await currentApplication();
1846
+ if (!app) {
1847
+ throw new Error(
1848
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1849
+ );
1850
+ }
1851
+ return app;
1852
+ }
1853
+ async function listApplications() {
1854
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1855
+ const parsed = listSchema.safeParse(parseJson(raw));
1856
+ if (!parsed.success) {
1857
+ throw new Error("Could not read the list of Algolia applications.");
1858
+ }
1859
+ return parsed.data;
1860
+ }
1861
+ async function selectApplication(id) {
1862
+ const raw = await runAlgoliaCli(
1863
+ ["application", "select", "--non-interactive", "--app-id", id],
1864
+ { onOutput: stderrSink }
1865
+ );
1866
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1867
+ if (!parsed.success) {
1868
+ throw new Error(
1869
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1870
+ );
1871
+ }
1872
+ return parsed.data;
1873
+ }
1874
+ function parseJson(text) {
1875
+ try {
1876
+ return JSON.parse(text);
1877
+ } catch {
1878
+ return void 0;
1879
+ }
1880
+ }
1881
+
1791
1882
  // src/actions/selectIndex.ts
1792
1883
  var CREATE_NEW_INDEX = "Create a new index\u2026";
1793
1884
  var selectIndexStep = async (ctx) => {
1885
+ await requireApplication();
1794
1886
  const indices = await listIndices();
1795
1887
  const names = indices.map((i) => i.name);
1796
1888
  const hasIndices = names.length > 0;
1797
1889
  let error;
1798
- for (; ; ) {
1890
+ let chosen;
1891
+ while (chosen === void 0) {
1799
1892
  const selection = hasIndices ? await ctx.requestUserInput({
1800
1893
  prompt: "Which index do you want to ingest into?",
1801
1894
  promptType: "multipleChoice",
@@ -1814,9 +1907,9 @@ var selectIndexStep = async (ctx) => {
1814
1907
  if (typeof selection !== "string") {
1815
1908
  throw new Error("selectIndex received an unexpected non-text result");
1816
1909
  }
1817
- let chosen;
1910
+ let candidate;
1818
1911
  if (!hasIndices) {
1819
- chosen = selection.trim();
1912
+ candidate = selection.trim();
1820
1913
  } else if (selection === CREATE_NEW_INDEX) {
1821
1914
  const name = await ctx.requestUserInput({
1822
1915
  prompt: "Name the new index:",
@@ -1826,17 +1919,18 @@ var selectIndexStep = async (ctx) => {
1826
1919
  if (typeof name !== "string") {
1827
1920
  throw new Error("selectIndex received an unexpected non-text result");
1828
1921
  }
1829
- chosen = name.trim();
1922
+ candidate = name.trim();
1830
1923
  } else {
1831
- chosen = selection;
1924
+ candidate = selection;
1832
1925
  }
1833
- if (!chosen) {
1926
+ if (!candidate) {
1834
1927
  error = "Index name cannot be empty.";
1835
1928
  continue;
1836
1929
  }
1837
- ctx.setUserInput("index", chosen);
1838
- return { selection: chosen };
1930
+ chosen = candidate;
1839
1931
  }
1932
+ ctx.setUserInput("index", chosen);
1933
+ return { selection: chosen };
1840
1934
  };
1841
1935
 
1842
1936
  // src/lib/agent.ts
@@ -1855,7 +1949,7 @@ import "zod";
1855
1949
 
1856
1950
  // src/lib/tools/listFiles.ts
1857
1951
  import { tool } from "ai";
1858
- import z5 from "zod";
1952
+ import z6 from "zod";
1859
1953
  import { readdir } from "node:fs/promises";
1860
1954
 
1861
1955
  // src/lib/tools/path.ts
@@ -1891,8 +1985,8 @@ async function hasSymlinkParent(ctx, target) {
1891
1985
  function listFilesTool(ctx) {
1892
1986
  return tool({
1893
1987
  description: 'List files in a directory (default: the current working directory). Pass path to list a subdirectory directly \u2014 e.g. "packages/api" \u2014 without first changeDirectory-ing into it.',
1894
- inputSchema: z5.object({
1895
- path: z5.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1988
+ inputSchema: z6.object({
1989
+ path: z6.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1896
1990
  }),
1897
1991
  execute: async ({ path = "." }) => {
1898
1992
  logger.info({ path }, "called listFiles tool");
@@ -1913,13 +2007,13 @@ function listFilesTool(ctx) {
1913
2007
 
1914
2008
  // src/lib/tools/changeDirectory.ts
1915
2009
  import { tool as tool2 } from "ai";
1916
- import z6 from "zod";
2010
+ import z7 from "zod";
1917
2011
  import { stat } from "node:fs/promises";
1918
2012
  function changeDirectoryTool(ctx) {
1919
2013
  return tool2({
1920
2014
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1921
- inputSchema: z6.object({
1922
- path: z6.string().describe("Directory to change into")
2015
+ inputSchema: z7.object({
2016
+ path: z7.string().describe("Directory to change into")
1923
2017
  }),
1924
2018
  execute: async ({ path }) => {
1925
2019
  logger.info({ path }, "called changeDirectory tool");
@@ -1941,13 +2035,13 @@ function changeDirectoryTool(ctx) {
1941
2035
 
1942
2036
  // src/lib/tools/reportStatus.ts
1943
2037
  import { tool as tool3 } from "ai";
1944
- import z7 from "zod";
2038
+ import z8 from "zod";
1945
2039
  function reportStatusTool(output) {
1946
2040
  return tool3({
1947
2041
  description: "Report the status of your execution. Return a reason in case of failure.",
1948
- inputSchema: z7.object({
1949
- status: z7.enum(["success", "fail"]),
1950
- reason: z7.string().optional(),
2042
+ inputSchema: z8.object({
2043
+ status: z8.enum(["success", "fail"]),
2044
+ reason: z8.string().optional(),
1951
2045
  output
1952
2046
  }),
1953
2047
  execute: async ({ status, reason, output: output2 }) => {
@@ -1959,7 +2053,7 @@ function reportStatusTool(output) {
1959
2053
 
1960
2054
  // src/lib/tools/readFile.ts
1961
2055
  import { tool as tool4 } from "ai";
1962
- import z8 from "zod";
2056
+ import z9 from "zod";
1963
2057
  import { readFile as readFile3 } from "node:fs/promises";
1964
2058
 
1965
2059
  // src/lib/tools/env.ts
@@ -1987,8 +2081,8 @@ function redactEnvValues(content) {
1987
2081
  function readFileTool(ctx) {
1988
2082
  return tool4({
1989
2083
  description: "Read the contents of a file at the given path",
1990
- inputSchema: z8.object({
1991
- filePath: z8.string().describe("Path to the file to read")
2084
+ inputSchema: z9.object({
2085
+ filePath: z9.string().describe("Path to the file to read")
1992
2086
  }),
1993
2087
  execute: async ({ filePath }) => {
1994
2088
  if (++ctx.counts.read > ctx.limits.read) {
@@ -2009,15 +2103,15 @@ function readFileTool(ctx) {
2009
2103
 
2010
2104
  // src/lib/tools/writeFile.ts
2011
2105
  import { tool as tool5 } from "ai";
2012
- import z9 from "zod";
2106
+ import z10 from "zod";
2013
2107
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
2014
2108
  import { dirname as dirname3 } from "node:path";
2015
2109
  function writeFileTool(ctx) {
2016
2110
  return tool5({
2017
2111
  description: "Write content to a file at the given path, overwriting it. Writes to secret env files (.env, .env.local, etc.) are refused by this tool \u2014 call it anyway and the tool will tell you how to proceed.",
2018
- inputSchema: z9.object({
2019
- filePath: z9.string().describe("Path to the file to write"),
2020
- content: z9.string().describe("Content to write to the file")
2112
+ inputSchema: z10.object({
2113
+ filePath: z10.string().describe("Path to the file to write"),
2114
+ content: z10.string().describe("Content to write to the file")
2021
2115
  }),
2022
2116
  execute: async ({ filePath, content }) => {
2023
2117
  logger.info({ filePath }, "called writeFile tool");
@@ -2047,68 +2141,6 @@ import z13 from "zod";
2047
2141
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
2048
2142
  import { dirname as dirname4, relative as relative3 } from "node:path";
2049
2143
 
2050
- // src/lib/algoliaApp.ts
2051
- import { z as z10 } from "zod";
2052
- var applicationSchema = z10.object({
2053
- id: z10.string().min(1),
2054
- name: z10.string().default(""),
2055
- plan: z10.string().optional()
2056
- });
2057
- var listSchema = z10.array(
2058
- z10.object({
2059
- id: z10.string().min(1),
2060
- name: z10.string().default(""),
2061
- plan_label: z10.string().optional()
2062
- }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
2063
- );
2064
- async function currentApplication() {
2065
- let raw;
2066
- try {
2067
- raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
2068
- } catch {
2069
- return null;
2070
- }
2071
- const parsed = applicationSchema.safeParse(parseJson(raw));
2072
- return parsed.success ? parsed.data : null;
2073
- }
2074
- async function requireApplication() {
2075
- const app = await currentApplication();
2076
- if (!app) {
2077
- throw new Error(
2078
- "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
2079
- );
2080
- }
2081
- return app;
2082
- }
2083
- async function listApplications() {
2084
- const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
2085
- const parsed = listSchema.safeParse(parseJson(raw));
2086
- if (!parsed.success) {
2087
- throw new Error("Could not read the list of Algolia applications.");
2088
- }
2089
- return parsed.data;
2090
- }
2091
- async function selectApplication(id) {
2092
- const raw = await runAlgoliaCli(
2093
- ["application", "select", "--non-interactive", "--app-id", id],
2094
- { onOutput: stderrSink }
2095
- );
2096
- const parsed = applicationSchema.safeParse(parseJson(raw));
2097
- if (!parsed.success) {
2098
- throw new Error(
2099
- `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
2100
- );
2101
- }
2102
- return parsed.data;
2103
- }
2104
- function parseJson(text) {
2105
- try {
2106
- return JSON.parse(text);
2107
- } catch {
2108
- return void 0;
2109
- }
2110
- }
2111
-
2112
2144
  // src/lib/algoliaApiKey.ts
2113
2145
  import { z as z12 } from "zod";
2114
2146
 
@@ -2116,13 +2148,13 @@ import { z as z12 } from "zod";
2116
2148
  import { deletePassword, getPassword, setPassword } from "cross-keychain";
2117
2149
  import { z as z11 } from "zod";
2118
2150
  var SERVICE = "algolia-wizard";
2119
- var ACCOUNT = "api-keys";
2151
+ var account = (userId) => `api-keys:${userId}`;
2120
2152
  var storedKeysSchema = z11.record(z11.string(), z11.string());
2121
2153
  function entryId(kind, index, appId) {
2122
2154
  return `${kind}:${appId}:${index}`;
2123
2155
  }
2124
- async function loadKeys() {
2125
- const raw = await getPassword(SERVICE, ACCOUNT);
2156
+ async function loadKeys(userId) {
2157
+ const raw = await getPassword(SERVICE, account(userId));
2126
2158
  if (!raw) return {};
2127
2159
  let payload;
2128
2160
  try {
@@ -2144,47 +2176,47 @@ function serialized(op) {
2144
2176
  });
2145
2177
  return next;
2146
2178
  }
2147
- async function readStoredKey(kind, index, appId) {
2179
+ async function readStoredKey(kind, userId, index, appId) {
2148
2180
  try {
2149
- return (await loadKeys())[entryId(kind, index, appId)] ?? null;
2181
+ return (await loadKeys(userId))[entryId(kind, index, appId)] ?? null;
2150
2182
  } catch (err) {
2151
2183
  logger.warn(
2152
- { err: err.message, kind, index, appId },
2184
+ { err: err.message, kind, index, appId, userId },
2153
2185
  "could not read the API key from the keychain"
2154
2186
  );
2155
2187
  return null;
2156
2188
  }
2157
2189
  }
2158
- function storeKey(kind, index, appId, value) {
2190
+ function storeKey(kind, userId, index, appId, value) {
2159
2191
  return serialized(async () => {
2160
2192
  const id = entryId(kind, index, appId);
2161
2193
  try {
2162
- const keys = await loadKeys();
2194
+ const keys = await loadKeys(userId);
2163
2195
  await setPassword(
2164
2196
  SERVICE,
2165
- ACCOUNT,
2197
+ account(userId),
2166
2198
  JSON.stringify({ ...keys, [id]: value })
2167
2199
  );
2168
- if ((await loadKeys())[id] !== value) {
2200
+ if ((await loadKeys(userId))[id] !== value) {
2169
2201
  throw new Error("the keychain did not store the value");
2170
2202
  }
2171
2203
  } catch (err) {
2172
2204
  logger.warn(
2173
- { err: err.message, kind, index, appId },
2205
+ { err: err.message, kind, index, appId, userId },
2174
2206
  "could not store the API key in the keychain; the next run will create another"
2175
2207
  );
2176
2208
  }
2177
2209
  });
2178
2210
  }
2179
- function deleteStoredKeys() {
2211
+ function deleteStoredKeys(userId) {
2180
2212
  return serialized(async () => {
2181
2213
  try {
2182
- await deletePassword(SERVICE, ACCOUNT);
2214
+ await deletePassword(SERVICE, account(userId));
2183
2215
  } catch (err) {
2184
2216
  const message = err.message;
2185
2217
  if (/not found/i.test(message)) return;
2186
2218
  logger.warn(
2187
- { err: message },
2219
+ { err: message, userId },
2188
2220
  "could not delete the API keys from the keychain"
2189
2221
  );
2190
2222
  }
@@ -2192,6 +2224,16 @@ function deleteStoredKeys() {
2192
2224
  }
2193
2225
 
2194
2226
  // src/lib/algoliaApiKey.ts
2227
+ function requireUserId() {
2228
+ const userId = useWizard.getState().user?.userId;
2229
+ if (!userId) {
2230
+ throw new Error("No Algolia user is signed in; cannot scope the API key.");
2231
+ }
2232
+ return userId;
2233
+ }
2234
+ async function currentUserId() {
2235
+ return useWizard.getState().user?.userId ?? (await getUser())?.userId ?? null;
2236
+ }
2195
2237
  var WRITE_ACLS = [
2196
2238
  "addObject",
2197
2239
  "deleteObject",
@@ -2238,23 +2280,30 @@ async function keyExists(key) {
2238
2280
  var resolved = /* @__PURE__ */ new Map();
2239
2281
  async function forgetResolvedKeys() {
2240
2282
  resolved.clear();
2241
- await deleteStoredKeys();
2283
+ const userId = await currentUserId();
2284
+ if (userId) await deleteStoredKeys(userId);
2242
2285
  }
2243
2286
  function resolveKey(kind, index, appId, acls, description) {
2244
- const cacheKey = `${kind}:${appId}:${index}`;
2287
+ const userId = requireUserId();
2288
+ const cacheKey = `${userId}:${kind}:${appId}:${index}`;
2245
2289
  const cached = resolved.get(cacheKey);
2246
2290
  if (cached) return cached;
2247
- const pending = provisionKey(kind, index, appId, acls, description).catch(
2248
- (err) => {
2249
- resolved.delete(cacheKey);
2250
- throw err;
2251
- }
2252
- );
2291
+ const pending = provisionKey(
2292
+ kind,
2293
+ userId,
2294
+ index,
2295
+ appId,
2296
+ acls,
2297
+ description
2298
+ ).catch((err) => {
2299
+ resolved.delete(cacheKey);
2300
+ throw err;
2301
+ });
2253
2302
  resolved.set(cacheKey, pending);
2254
2303
  return pending;
2255
2304
  }
2256
- async function provisionKey(kind, index, appId, acls, description) {
2257
- const stored = await readStoredKey(kind, index, appId);
2305
+ async function provisionKey(kind, userId, index, appId, acls, description) {
2306
+ const stored = await readStoredKey(kind, userId, index, appId);
2258
2307
  if (stored) {
2259
2308
  if (await keyExists(stored)) {
2260
2309
  logger.info({ kind, index, appId }, "reusing the stored API key");
@@ -2266,7 +2315,7 @@ async function provisionKey(kind, index, appId, acls, description) {
2266
2315
  );
2267
2316
  }
2268
2317
  const key = await createKey(index, acls, description);
2269
- await storeKey(kind, index, appId, key);
2318
+ await storeKey(kind, userId, index, appId, key);
2270
2319
  return { key, source: "created" };
2271
2320
  }
2272
2321
  function resolveWriteKey(index, appId) {
@@ -3535,7 +3584,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3535
3584
  // package.json
3536
3585
  var package_default = {
3537
3586
  name: "@algolia/wizard",
3538
- version: "0.46.0-rc.146.324",
3587
+ version: "0.47.0",
3539
3588
  description: "Magically implement Algolia functionality in your codebase",
3540
3589
  type: "module",
3541
3590
  engines: {
@@ -4461,6 +4510,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4461
4510
  if (useCases.includes("ingestion")) {
4462
4511
  ingestAppId = appId ?? (await requireApplication()).id;
4463
4512
  }
4513
+ let ingestWriteKey;
4514
+ if (useCases.includes("ingestion") && ingestAppId) {
4515
+ ingestWriteKey = (await resolveWriteKey(targetIndex, ingestAppId)).key;
4516
+ }
4464
4517
  let uploadFilePath;
4465
4518
  let uploadWarning;
4466
4519
  if (ingestionSource === "fileUpload") {
@@ -4523,9 +4576,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4523
4576
  let ingestDurationMs;
4524
4577
  let ingestOutcomeMessage;
4525
4578
  const ingestKeyAppId = ingestAppId;
4526
- const ingestionTools = ingestKeyAppId ? makeToolContext(repoRoot, async () => ({
4579
+ const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, async () => ({
4527
4580
  [APP_ID_VAR]: ingestKeyAppId,
4528
- [API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
4581
+ [API_KEY_VAR]: ingestWriteKey,
4529
4582
  [INDEX_NAME_VAR]: targetIndex
4530
4583
  })) : void 0;
4531
4584
  const searchTools = makeToolContext(repoRoot);
@@ -4823,7 +4876,7 @@ function getWorkflow(id) {
4823
4876
  import { dirname as dirname8, join as join11 } from "node:path";
4824
4877
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4825
4878
  import { useState as useState8 } from "react";
4826
- import { Box as Box11, Spacer, Text as Text11, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
4879
+ import { Box as Box11, Spacer, Text as Text11, useInput as useInput4, useWindowSize as useWindowSize5 } from "ink";
4827
4880
 
4828
4881
  // src/ui/copy/welcome.ts
4829
4882
  var sidebarItems = [
@@ -4851,7 +4904,7 @@ var sidebarItems = [
4851
4904
 
4852
4905
  // src/ui/Welcome.tsx
4853
4906
  import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
4854
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
4907
+ import { jsx as jsx9, jsxs as jsxs10 } from "react/jsx-runtime";
4855
4908
  var IMAGE_PATH = join11(dirname8(fileURLToPath2(import.meta.url)), "algolia.png");
4856
4909
  var TERMINAL_INFO = {
4857
4910
  ...defaultTerminalInfo,
@@ -4862,12 +4915,12 @@ function SidebarItem({
4862
4915
  title,
4863
4916
  description
4864
4917
  }) {
4865
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", children: [
4866
- /* @__PURE__ */ jsxs9(Box11, { gap: 1, children: [
4918
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", children: [
4919
+ /* @__PURE__ */ jsxs10(Box11, { gap: 1, children: [
4867
4920
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.success, children: "\u2192" }),
4868
4921
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.strong, bold: true, children: title })
4869
4922
  ] }),
4870
- /* @__PURE__ */ jsxs9(Box11, { flexDirection: "row", gap: 2, children: [
4923
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 2, children: [
4871
4924
  /* @__PURE__ */ jsx9(Spacer, {}),
4872
4925
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.muted, children: description })
4873
4926
  ] })
@@ -4876,7 +4929,7 @@ function SidebarItem({
4876
4929
  function Welcome() {
4877
4930
  const confirmStart = useWizard((s) => s.confirmStart);
4878
4931
  const openLearnMore = useWizard((s) => s.openLearnMore);
4879
- const { rows } = useWindowSize6();
4932
+ const { rows } = useWindowSize5();
4880
4933
  const actions = [
4881
4934
  { label: "start wizard", run: confirmStart },
4882
4935
  { label: "learn more", run: openLearnMore }
@@ -4905,7 +4958,7 @@ function Welcome() {
4905
4958
  if (rows < 30) {
4906
4959
  layout = scales["small"];
4907
4960
  }
4908
- return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
4961
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
4909
4962
  /* @__PURE__ */ jsx9(
4910
4963
  Box11,
4911
4964
  {
@@ -4913,7 +4966,7 @@ function Welcome() {
4913
4966
  paddingX: layout.main.padding.x,
4914
4967
  flexDirection: "column",
4915
4968
  justifyContent: "center",
4916
- children: /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", gap: 2, children: [
4969
+ children: /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 2, children: [
4917
4970
  /* @__PURE__ */ jsx9(TerminalInfoContext.Provider, { value: TERMINAL_INFO, children: /* @__PURE__ */ jsx9(
4918
4971
  Image,
4919
4972
  {
@@ -4935,12 +4988,12 @@ function Welcome() {
4935
4988
  },
4936
4989
  action.label
4937
4990
  )) }),
4938
- /* @__PURE__ */ jsxs9(Box11, { gap: 2, children: [
4939
- /* @__PURE__ */ jsxs9(Text11, { children: [
4991
+ /* @__PURE__ */ jsxs10(Box11, { gap: 2, children: [
4992
+ /* @__PURE__ */ jsxs10(Text11, { children: [
4940
4993
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.primary, children: "[\u2191] [\u2193]" }),
4941
4994
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.dim, children: " move" })
4942
4995
  ] }),
4943
- /* @__PURE__ */ jsxs9(Text11, { children: [
4996
+ /* @__PURE__ */ jsxs10(Text11, { children: [
4944
4997
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.primary, children: "[enter]" }),
4945
4998
  /* @__PURE__ */ jsx9(Text11, { color: COLORS.dim, children: " confirm" })
4946
4999
  ] })
@@ -4948,7 +5001,7 @@ function Welcome() {
4948
5001
  ] })
4949
5002
  }
4950
5003
  ),
4951
- /* @__PURE__ */ jsxs9(
5004
+ /* @__PURE__ */ jsxs10(
4952
5005
  Box11,
4953
5006
  {
4954
5007
  backgroundColor: COLORS.bg.sidebar,
@@ -4969,7 +5022,7 @@ function Welcome() {
4969
5022
 
4970
5023
  // src/ui/LearnMore.tsx
4971
5024
  import { Fragment } from "react";
4972
- import { Box as Box12, Text as Text12, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
5025
+ import { Box as Box12, Text as Text12, useInput as useInput5, useWindowSize as useWindowSize6 } from "ink";
4973
5026
 
4974
5027
  // src/ui/copy/learn-more.ts
4975
5028
  var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
@@ -5011,7 +5064,7 @@ var policyLinks = [
5011
5064
  ];
5012
5065
 
5013
5066
  // src/ui/LearnMore.tsx
5014
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
5067
+ import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
5015
5068
  var TAG_COLORS = {
5016
5069
  READ: COLORS.success,
5017
5070
  WRITE: COLORS.badge,
@@ -5028,7 +5081,7 @@ function NeverLine({
5028
5081
  }) {
5029
5082
  const used = segments.reduce((n, s) => n + s.text.length, 0);
5030
5083
  const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
5031
- return /* @__PURE__ */ jsxs10(Text12, { color: COLORS.primary, children: [
5084
+ return /* @__PURE__ */ jsxs11(Text12, { color: COLORS.primary, children: [
5032
5085
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.danger, children: "\u2502" }),
5033
5086
  " ".repeat(NEVER_BOX_PAD_X),
5034
5087
  segments.map((s, i) => /* @__PURE__ */ jsx10(Text12, { color: s.color, bold: s.bold, children: s.text }, i)),
@@ -5039,13 +5092,13 @@ function NeverLine({
5039
5092
  function LearnMore() {
5040
5093
  const confirmStart = useWizard((s) => s.confirmStart);
5041
5094
  const backToHome = useWizard((s) => s.backToHome);
5042
- const { columns } = useWindowSize7();
5095
+ const { columns } = useWindowSize6();
5043
5096
  const dividerWidth = Math.max(0, columns - PADDING_X * 2);
5044
5097
  useInput5((_input, key) => {
5045
5098
  if (key.escape) backToHome();
5046
5099
  else if (key.return) confirmStart();
5047
5100
  });
5048
- return /* @__PURE__ */ jsxs10(
5101
+ return /* @__PURE__ */ jsxs11(
5049
5102
  Box12,
5050
5103
  {
5051
5104
  flexDirection: "column",
@@ -5056,17 +5109,17 @@ function LearnMore() {
5056
5109
  children: [
5057
5110
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
5058
5111
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: accessIntro }),
5059
- /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginTop: 1, children: [
5112
+ /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginTop: 1, children: [
5060
5113
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
5061
- /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", gap: 1, marginTop: 1, children: [
5114
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, marginTop: 1, children: [
5062
5115
  /* @__PURE__ */ jsx10(Box12, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text12, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
5063
- /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text12, { color: COLORS.primary, children: [
5116
+ /* @__PURE__ */ jsx10(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text12, { color: COLORS.primary, children: [
5064
5117
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.strong, bold: true, children: item.title }),
5065
5118
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
5066
5119
  ] }) })
5067
5120
  ] })
5068
5121
  ] }, item.tag)) }),
5069
- /* @__PURE__ */ jsxs10(Box12, { marginTop: 1, flexDirection: "column", children: [
5122
+ /* @__PURE__ */ jsxs11(Box12, { marginTop: 1, flexDirection: "column", children: [
5070
5123
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
5071
5124
  /* @__PURE__ */ jsx10(NeverLine, { width: dividerWidth }),
5072
5125
  /* @__PURE__ */ jsx10(
@@ -5076,7 +5129,7 @@ function LearnMore() {
5076
5129
  segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
5077
5130
  }
5078
5131
  ),
5079
- neverItems.map((item) => /* @__PURE__ */ jsxs10(Fragment, { children: [
5132
+ neverItems.map((item) => /* @__PURE__ */ jsxs11(Fragment, { children: [
5080
5133
  /* @__PURE__ */ jsx10(NeverLine, { width: dividerWidth }),
5081
5134
  /* @__PURE__ */ jsx10(
5082
5135
  NeverLine,
@@ -5093,17 +5146,17 @@ function LearnMore() {
5093
5146
  /* @__PURE__ */ jsx10(NeverLine, { width: dividerWidth }),
5094
5147
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
5095
5148
  ] }),
5096
- /* @__PURE__ */ jsx10(Box12, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", gap: 1, children: [
5149
+ /* @__PURE__ */ jsx10(Box12, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
5097
5150
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
5098
5151
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.accent, children: link.url })
5099
5152
  ] }, link.label)) }),
5100
- /* @__PURE__ */ jsxs10(Box12, { marginTop: 1, flexDirection: "row", gap: 3, children: [
5101
- /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", gap: 1, children: [
5153
+ /* @__PURE__ */ jsxs11(Box12, { marginTop: 1, flexDirection: "row", gap: 3, children: [
5154
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
5102
5155
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "[" }),
5103
5156
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "esc" }),
5104
5157
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "] back" })
5105
5158
  ] }),
5106
- /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", gap: 1, children: [
5159
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
5107
5160
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "[" }),
5108
5161
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.primary, children: "enter" }),
5109
5162
  /* @__PURE__ */ jsx10(Text12, { color: COLORS.muted, children: "]" }),
@@ -5120,13 +5173,13 @@ import { Box as Box15, Text as Text15 } from "ink";
5120
5173
 
5121
5174
  // src/ui/Steps.tsx
5122
5175
  import { Box as Box13, Text as Text13 } from "ink";
5123
- import Spinner from "ink-spinner";
5124
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
5176
+ import Spinner2 from "ink-spinner";
5177
+ import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
5125
5178
  function Steps() {
5126
5179
  const { steps } = useWizard();
5127
5180
  const visibleSteps = steps.filter(isStepVisible);
5128
- return /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text13, { color: COLORS.status[s.status], children: [
5129
- s.status === "running" ? /* @__PURE__ */ jsx11(Spinner, { type: "dots" }) : MARKER[s.status],
5181
+ return /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status[s.status], children: [
5182
+ s.status === "running" ? /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) : MARKER[s.status],
5130
5183
  " ",
5131
5184
  s.title
5132
5185
  ] }) }, s.id)) });
@@ -5135,8 +5188,8 @@ function CurrentStep() {
5135
5188
  const { steps } = useWizard();
5136
5189
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
5137
5190
  if (!currentStep) return null;
5138
- return /* @__PURE__ */ jsxs11(Text13, { color: COLORS.status.running, children: [
5139
- /* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
5191
+ return /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.running, children: [
5192
+ /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
5140
5193
  " ",
5141
5194
  ` ${currentStep.title}`
5142
5195
  ] });
@@ -5144,14 +5197,14 @@ function CurrentStep() {
5144
5197
 
5145
5198
  // src/ui/Progress.tsx
5146
5199
  import { Box as Box14, Text as Text14 } from "ink";
5147
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
5200
+ import { jsx as jsx12, jsxs as jsxs13 } from "react/jsx-runtime";
5148
5201
  function Progress() {
5149
5202
  const { steps, currentStepIndex } = useWizard();
5150
5203
  const visibleSteps = steps.filter(isStepVisible);
5151
5204
  if (visibleSteps.length === 0) return null;
5152
5205
  const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
5153
5206
  const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
5154
- return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "row", gap: 1, children: [
5207
+ return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", gap: 1, children: [
5155
5208
  /* @__PURE__ */ jsx12(Text14, { color: COLORS.muted, children: "STEP" }),
5156
5209
  /* @__PURE__ */ jsx12(Text14, { color: COLORS.strong, bold: true, children: activeStepNumber }),
5157
5210
  /* @__PURE__ */ jsx12(Text14, { color: COLORS.strong, bold: true, children: "/" }),
@@ -5166,9 +5219,9 @@ var sidebarCommands = [
5166
5219
  ];
5167
5220
 
5168
5221
  // src/ui/Sidebar.tsx
5169
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
5222
+ import { jsx as jsx13, jsxs as jsxs14 } from "react/jsx-runtime";
5170
5223
  function Sidebar() {
5171
- return /* @__PURE__ */ jsxs13(
5224
+ return /* @__PURE__ */ jsxs14(
5172
5225
  Box15,
5173
5226
  {
5174
5227
  backgroundColor: "#14171E",
@@ -5178,14 +5231,14 @@ function Sidebar() {
5178
5231
  flexDirection: "column",
5179
5232
  justifyContent: "space-between",
5180
5233
  children: [
5181
- /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", gap: 1, children: [
5234
+ /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", gap: 1, children: [
5182
5235
  /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: "PROGRESS" }),
5183
5236
  /* @__PURE__ */ jsx13(Steps, {})
5184
5237
  ] }),
5185
- /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", gap: 1, children: [
5238
+ /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", gap: 1, children: [
5186
5239
  /* @__PURE__ */ jsx13(Progress, {}),
5187
5240
  /* @__PURE__ */ jsx13(Box15, { flexDirection: "column", children: sidebarCommands.map((c) => {
5188
- return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "row", gap: 1, children: [
5241
+ return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", gap: 1, children: [
5189
5242
  /* @__PURE__ */ jsx13(Text15, { color: COLORS.primary, children: `[${c.keyHint}]` }),
5190
5243
  /* @__PURE__ */ jsx13(Text15, { color: COLORS.muted, children: c.description })
5191
5244
  ] }, c.keyHint);
@@ -5198,10 +5251,10 @@ function Sidebar() {
5198
5251
 
5199
5252
  // src/ui/Ribbon.tsx
5200
5253
  import { Box as Box16, Text as Text16 } from "ink";
5201
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
5254
+ import { jsx as jsx14, jsxs as jsxs15 } from "react/jsx-runtime";
5202
5255
  function Ribbon() {
5203
5256
  const firstCommand = sidebarCommands[0];
5204
- return /* @__PURE__ */ jsxs14(
5257
+ return /* @__PURE__ */ jsxs15(
5205
5258
  Box16,
5206
5259
  {
5207
5260
  backgroundColor: "#14171E",
@@ -5212,7 +5265,7 @@ function Ribbon() {
5212
5265
  children: [
5213
5266
  /* @__PURE__ */ jsx14(Progress, {}),
5214
5267
  /* @__PURE__ */ jsx14(CurrentStep, {}),
5215
- /* @__PURE__ */ jsxs14(Box16, { flexDirection: "row", gap: 1, children: [
5268
+ /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", gap: 1, children: [
5216
5269
  /* @__PURE__ */ jsx14(Text16, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
5217
5270
  /* @__PURE__ */ jsx14(Text16, { color: COLORS.muted, children: firstCommand.description })
5218
5271
  ] })
@@ -5226,7 +5279,7 @@ import { useState as useState10 } from "react";
5226
5279
 
5227
5280
  // src/ui/Logs.tsx
5228
5281
  import { Box as Box17, Text as Text17, useInput as useInput6 } from "ink";
5229
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
5282
+ import { jsx as jsx15, jsxs as jsxs16 } from "react/jsx-runtime";
5230
5283
  var KIND_COLOR = {
5231
5284
  tool: COLORS.primary,
5232
5285
  prompt: COLORS.badge
@@ -5262,7 +5315,7 @@ function Logs() {
5262
5315
  else if (key.downArrow) scroll.scrollBy(1);
5263
5316
  });
5264
5317
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
5265
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
5318
+ return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
5266
5319
  logs.length === 0 && /* @__PURE__ */ jsx15(Text17, { color: COLORS.dim, children: "No logs yet." }),
5267
5320
  /* @__PURE__ */ jsx15(ScrollView, { scroll, children: visible.map((entry) => {
5268
5321
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
@@ -5274,7 +5327,7 @@ function Logs() {
5274
5327
  const name = truncate3(entry.name, budget);
5275
5328
  budget -= name.length;
5276
5329
  const preview = rawPreview ? truncate3(rawPreview, budget) : "";
5277
- return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "row", gap: ROW_GAP, children: [
5330
+ return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", gap: ROW_GAP, children: [
5278
5331
  /* @__PURE__ */ jsx15(Text17, { color: COLORS.dim, children: timestamp }),
5279
5332
  /* @__PURE__ */ jsx15(Text17, { color: logNameColor(entry), wrap: "truncate", children: name }),
5280
5333
  preview && /* @__PURE__ */ jsx15(Text17, { color: COLORS.dim, wrap: "truncate", children: preview }),
@@ -5287,9 +5340,38 @@ function Logs() {
5287
5340
 
5288
5341
  // src/ui/Tips.tsx
5289
5342
  import { useEffect as useEffect3, useRef as useRef4, useState as useState9 } from "react";
5290
- import { Box as Box18, Text as Text18 } from "ink";
5343
+ import { Box as Box19, Text as Text19 } from "ink";
5291
5344
  import terminalLink from "terminal-link";
5292
5345
 
5346
+ // src/ui/Code.tsx
5347
+ import { Box as Box18, Text as Text18 } from "ink";
5348
+ import { jsx as jsx16 } from "react/jsx-runtime";
5349
+ var TOKEN_RE = /("(?:\\.|[^"\\])*"|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\]:,])/g;
5350
+ function highlightJson(json) {
5351
+ const parts = json.split(TOKEN_RE);
5352
+ return parts.map((part, i) => {
5353
+ if (!part) return null;
5354
+ if (part[0] === '"') {
5355
+ const next = parts.slice(i + 1).find((p) => p.trim());
5356
+ const isKey = next?.trimStart().startsWith(":");
5357
+ return /* @__PURE__ */ jsx16(Text18, { color: isKey ? "cyan" : "green", children: part }, i);
5358
+ }
5359
+ if (part === "true" || part === "false" || part === "null") {
5360
+ return /* @__PURE__ */ jsx16(Text18, { color: "magenta", children: part }, i);
5361
+ }
5362
+ if (/^-?\d/.test(part)) {
5363
+ return /* @__PURE__ */ jsx16(Text18, { color: "yellow", children: part }, i);
5364
+ }
5365
+ if (/^[{}[\]:,]$/.test(part)) {
5366
+ return /* @__PURE__ */ jsx16(Text18, { color: COLORS.dim, children: part }, i);
5367
+ }
5368
+ return /* @__PURE__ */ jsx16(Text18, { children: part }, i);
5369
+ });
5370
+ }
5371
+ function Code({ children }) {
5372
+ return /* @__PURE__ */ jsx16(Box18, { children: /* @__PURE__ */ jsx16(Text18, { children: highlightJson(children) }) });
5373
+ }
5374
+
5293
5375
  // src/ui/copy/tips.ts
5294
5376
  var tips = [
5295
5377
  {
@@ -5320,7 +5402,23 @@ var tips = [
5320
5402
  },
5321
5403
  {
5322
5404
  type: "text",
5323
- value: "Fields, nesting and types are up to you \u2014 index the shape your app already uses."
5405
+ value: "Everything else is up to you:"
5406
+ },
5407
+ {
5408
+ type: "code",
5409
+ value: JSON.stringify(
5410
+ {
5411
+ objectID: "sku_48213",
5412
+ name: "Trail Running Shoe",
5413
+ brand: "Northline",
5414
+ categories: ["Footwear", "Running", "Mens"],
5415
+ price: 129.99,
5416
+ in_stock: true,
5417
+ rating: 4.6
5418
+ },
5419
+ null,
5420
+ 2
5421
+ )
5324
5422
  }
5325
5423
  ]
5326
5424
  },
@@ -5485,26 +5583,26 @@ function tipsByIds(ids) {
5485
5583
  }
5486
5584
 
5487
5585
  // src/ui/Tips.tsx
5488
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
5586
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
5489
5587
  var TICK_MS = 16;
5490
5588
  var CHUNK_HOLD_MS = 2e3;
5491
5589
  var HOLD_MS = 12e3;
5492
5590
  function TipTitle({ children }) {
5493
- return /* @__PURE__ */ jsxs16(Text18, { children: [
5494
- /* @__PURE__ */ jsx16(Text18, { color: "cyan", children: "\u2726 " }),
5495
- /* @__PURE__ */ jsx16(Text18, { color: "white", bold: true, children })
5591
+ return /* @__PURE__ */ jsxs17(Box19, { gap: 1, children: [
5592
+ /* @__PURE__ */ jsx17(Text19, { color: "cyan", children: "\u2726" }),
5593
+ /* @__PURE__ */ jsx17(Text19, { color: "white", bold: true, children })
5496
5594
  ] });
5497
5595
  }
5498
5596
  function InlineSegment({ segment }) {
5499
5597
  switch (segment.type) {
5500
5598
  case "highlight":
5501
- return /* @__PURE__ */ jsx16(Text18, { color: COLORS.success, children: segment.value });
5599
+ return /* @__PURE__ */ jsx17(Text19, { color: COLORS.success, children: segment.value });
5502
5600
  case "link":
5503
- return /* @__PURE__ */ jsx16(Text18, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
5601
+ return /* @__PURE__ */ jsx17(Text19, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
5504
5602
  case "codeword":
5505
- return /* @__PURE__ */ jsx16(Text18, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
5603
+ return /* @__PURE__ */ jsx17(Text19, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
5506
5604
  default:
5507
- return /* @__PURE__ */ jsx16(Text18, { color: COLORS.muted, children: segment.value });
5605
+ return /* @__PURE__ */ jsx17(Text19, { color: COLORS.muted, children: segment.value });
5508
5606
  }
5509
5607
  }
5510
5608
  function TipContent({
@@ -5512,42 +5610,38 @@ function TipContent({
5512
5610
  revealed
5513
5611
  }) {
5514
5612
  let remaining = revealed;
5515
- const segments = [];
5516
- for (const chunk of chunks) {
5613
+ const slices = chunks.map((chunk) => {
5517
5614
  const slice = chunk.value.slice(0, Math.max(0, remaining));
5518
5615
  remaining -= chunk.value.length;
5616
+ return slice;
5617
+ });
5618
+ const blocks = [];
5619
+ for (let i = 0; i < chunks.length; i++) {
5620
+ const chunk = chunks[i];
5621
+ const slice = slices[i];
5519
5622
  if (!slice) continue;
5520
- const last = segments[segments.length - 1];
5521
- if (chunk.type === "text" && last?.type === "text") {
5522
- last.value = `${last.value} ${slice}`;
5523
- continue;
5524
- }
5525
- segments.push({
5623
+ const segment = {
5526
5624
  type: chunk.type,
5527
5625
  value: slice,
5528
5626
  href: chunk.type === "link" ? chunk.href : void 0
5529
- });
5627
+ };
5628
+ if (chunk.type === "code") {
5629
+ blocks.push({ type: "code", segment });
5630
+ continue;
5631
+ }
5632
+ const last = blocks[blocks.length - 1];
5633
+ if (last?.type === "inline") {
5634
+ last.segments.push(segment);
5635
+ } else {
5636
+ blocks.push({ type: "inline", segments: [segment] });
5637
+ }
5530
5638
  }
5531
- return /* @__PURE__ */ jsx16(Box18, { marginLeft: 2, children: /* @__PURE__ */ jsx16(Text18, { children: segments.map((segment, i) => /* @__PURE__ */ jsxs16(Text18, { children: [
5532
- i > 0 && " ",
5533
- /* @__PURE__ */ jsx16(InlineSegment, { segment })
5534
- ] }, i)) }) });
5535
- }
5536
- var TIP_PANEL_ROWS = 11;
5537
- function TipCard({
5538
- tip,
5539
- revealed
5540
- }) {
5541
- return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", gap: 1, children: [
5542
- /* @__PURE__ */ jsx16(TipTitle, { children: tip.title.slice(0, revealed) }),
5543
- /* @__PURE__ */ jsx16(
5544
- TipContent,
5545
- {
5546
- chunks: tip.chunks,
5547
- revealed: Math.max(0, revealed - tip.title.length)
5548
- }
5549
- )
5550
- ] });
5639
+ return /* @__PURE__ */ jsx17(Box19, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
5640
+ (block, i) => block.type === "code" ? /* @__PURE__ */ jsx17(Code, { children: block.segment.value }, i) : /* @__PURE__ */ jsx17(Text19, { children: block.segments.map((segment, j) => /* @__PURE__ */ jsxs17(Text19, { children: [
5641
+ j > 0 && " ",
5642
+ /* @__PURE__ */ jsx17(InlineSegment, { segment })
5643
+ ] }, j)) }, i)
5644
+ ) });
5551
5645
  }
5552
5646
  var progress = { stepId: null, tipIndex: 0, revealed: 0 };
5553
5647
  function Tips({
@@ -5620,11 +5714,16 @@ function Tips({
5620
5714
  return () => clearTimeout(timer);
5621
5715
  }, [revealed, totalLength, tips2.length]);
5622
5716
  if (!tip) return null;
5623
- return /* @__PURE__ */ jsx16(TipCard, { tip, revealed });
5717
+ const titleRevealed = tip.title.slice(0, revealed);
5718
+ const contentRevealed = Math.max(0, revealed - tip.title.length);
5719
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
5720
+ /* @__PURE__ */ jsx17(TipTitle, { children: titleRevealed }),
5721
+ /* @__PURE__ */ jsx17(TipContent, { chunks: tip.chunks, revealed: contentRevealed })
5722
+ ] });
5624
5723
  }
5625
5724
 
5626
5725
  // src/ui/App.tsx
5627
- import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
5726
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
5628
5727
  function App() {
5629
5728
  const {
5630
5729
  phase,
@@ -5635,10 +5734,11 @@ function App() {
5635
5734
  inputReq,
5636
5735
  user,
5637
5736
  workflow,
5737
+ settingUpAppId,
5638
5738
  review: review2
5639
5739
  } = useWizard();
5640
5740
  const { exit } = useApp();
5641
- const { columns, rows } = useWindowSize8();
5741
+ const { columns, rows } = useWindowSize7();
5642
5742
  const [showLogs, setShowLogs] = useState10(false);
5643
5743
  const [tipState, setTipState] = useState10("idle");
5644
5744
  const finished = phase === "done" || phase === "error";
@@ -5650,7 +5750,8 @@ function App() {
5650
5750
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
5651
5751
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
5652
5752
  const holdForTip = stepHasTips && promptPending && tipState === "revealing";
5653
- const showTips = stepHasTips && (phase === "running" || isCommandApprovalPrompt || holdForTip);
5753
+ const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
5754
+ const showNoticesInMain = !stepHasTips;
5654
5755
  const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
5655
5756
  useInput7(
5656
5757
  (_input, key) => {
@@ -5685,15 +5786,14 @@ function App() {
5685
5786
  });
5686
5787
  const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
5687
5788
  const flexDirection = columns > 90 ? "row" : "column";
5688
- const tipPanelRows = Math.min(TIP_PANEL_ROWS, Math.max(8, rows - 16));
5689
5789
  const showSidebar = flexDirection === "row";
5690
5790
  const scrollsPastViewport = phase === "idle" && homeScreen === "learnMore";
5691
5791
  return (
5692
5792
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
5693
5793
  the whole screen, and the scrolling throws off its cursor arithmetic —
5694
5794
  flicker and leftover rows. */
5695
- /* @__PURE__ */ jsxs17(
5696
- Box19,
5795
+ /* @__PURE__ */ jsxs18(
5796
+ Box20,
5697
5797
  {
5698
5798
  backgroundColor: COLORS.bg.main,
5699
5799
  flexDirection: "row",
@@ -5701,16 +5801,16 @@ function App() {
5701
5801
  height: scrollsPastViewport ? void 0 : rows,
5702
5802
  overflow: scrollsPastViewport ? "visible" : "hidden",
5703
5803
  children: [
5704
- mainWindowVisible && /* @__PURE__ */ jsxs17(
5705
- Box19,
5804
+ mainWindowVisible && /* @__PURE__ */ jsxs18(
5805
+ Box20,
5706
5806
  {
5707
5807
  flexDirection,
5708
5808
  width: "100%",
5709
5809
  maxHeight: rows,
5710
5810
  justifyContent: "space-between",
5711
5811
  children: [
5712
- showLogs ? /* @__PURE__ */ jsx17(Logs, {}) : /* @__PURE__ */ jsxs17(
5713
- Box19,
5812
+ showLogs ? /* @__PURE__ */ jsx18(Logs, {}) : /* @__PURE__ */ jsxs18(
5813
+ Box20,
5714
5814
  {
5715
5815
  flexDirection: "column",
5716
5816
  paddingX: 4,
@@ -5719,67 +5819,55 @@ function App() {
5719
5819
  flexGrow: 1,
5720
5820
  gap: 1,
5721
5821
  children: [
5722
- /* @__PURE__ */ jsxs17(Box19, { flexGrow: 2, flexDirection: "column", children: [
5723
- phase === "preflight" && !user && /* @__PURE__ */ jsx17(Box19, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text19, { color: COLORS.strong, bold: true, children: [
5724
- /* @__PURE__ */ jsx17(Spinner2, { type: "dots" }),
5822
+ /* @__PURE__ */ jsxs18(Box20, { flexGrow: 2, flexDirection: "column", children: [
5823
+ phase === "preflight" && !user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5824
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5725
5825
  " Signing in to Algolia"
5726
5826
  ] }) }),
5727
- phase === "preflight" && user && /* @__PURE__ */ jsx17(Box19, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text19, { color: COLORS.strong, bold: true, children: [
5728
- /* @__PURE__ */ jsx17(Spinner2, { type: "dots" }),
5827
+ phase === "preflight" && user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5828
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5729
5829
  " Getting things ready"
5730
5830
  ] }) }),
5731
- phase === "authenticating" && /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", marginBottom: 1, children: [
5732
- /* @__PURE__ */ jsxs17(Text19, { color: COLORS.strong, bold: true, children: [
5733
- /* @__PURE__ */ jsx17(Spinner2, { type: "dots" }),
5831
+ phase === "authenticating" && /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", marginBottom: 1, children: [
5832
+ /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5833
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5734
5834
  " Signing in to Algolia"
5735
5835
  ] }),
5736
- /* @__PURE__ */ jsx17(Text19, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
5836
+ /* @__PURE__ */ jsx18(Text20, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
5737
5837
  ] }),
5738
- /* @__PURE__ */ jsx17(CliOutput, {}),
5739
- showNotices && /* @__PURE__ */ jsx17(Notices, { showAll: true, border: false }),
5740
- /* @__PURE__ */ jsx17(Review, {}),
5741
- !showTips && /* @__PURE__ */ jsx17(PromptInput, {}),
5742
- phase === "error" && error && /* @__PURE__ */ jsx17(Box19, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text19, { color: COLORS.status.error, children: [
5838
+ settingUpAppId && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5839
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5840
+ " Setting up Wizard using app",
5841
+ " ",
5842
+ settingUpAppId
5843
+ ] }) }),
5844
+ /* @__PURE__ */ jsx18(CliOutput, {}),
5845
+ showTips && currentStep && /* @__PURE__ */ jsx18(
5846
+ Tips,
5847
+ {
5848
+ stepId: currentStep.id,
5849
+ ids: tipIds,
5850
+ onRevealStateChange: setTipState
5851
+ }
5852
+ ),
5853
+ showNoticesInMain && showNotices && /* @__PURE__ */ jsx18(Notices, { showAll: true, border: false }),
5854
+ /* @__PURE__ */ jsx18(Review, {}),
5855
+ !showTips && /* @__PURE__ */ jsx18(PromptInput, {}),
5856
+ phase === "error" && error && /* @__PURE__ */ jsx18(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.status.error, children: [
5743
5857
  "\u2716 ",
5744
5858
  error
5745
5859
  ] }) })
5746
5860
  ] }),
5747
- showTips && currentStep && /* Fixed height: a panel that grew as the tip streamed would
5748
- re-measure the notice feed above it on every character and
5749
- walk its scroll window. */
5750
- /* @__PURE__ */ jsx17(
5751
- Box19,
5752
- {
5753
- borderColor: COLORS.border,
5754
- borderStyle: "single",
5755
- borderBackgroundColor: COLORS.bg.main,
5756
- borderLeft: false,
5757
- borderRight: false,
5758
- borderBottom: false,
5759
- paddingTop: 1,
5760
- height: tipPanelRows,
5761
- flexShrink: 0,
5762
- overflow: "hidden",
5763
- flexDirection: "column",
5764
- children: /* @__PURE__ */ jsx17(
5765
- Tips,
5766
- {
5767
- stepId: currentStep.id,
5768
- ids: tipIds,
5769
- onRevealStateChange: setTipState
5770
- }
5771
- )
5772
- }
5773
- ),
5774
- showTips && !holdForTip && /* @__PURE__ */ jsx17(PromptInput, {})
5861
+ !showNoticesInMain && showNotices && /* @__PURE__ */ jsx18(Notices, {}),
5862
+ showTips && !holdForTip && /* @__PURE__ */ jsx18(PromptInput, {})
5775
5863
  ]
5776
5864
  }
5777
5865
  ),
5778
- showSidebar ? /* @__PURE__ */ jsx17(Sidebar, {}) : /* @__PURE__ */ jsx17(Ribbon, {})
5866
+ showSidebar ? /* @__PURE__ */ jsx18(Sidebar, {}) : /* @__PURE__ */ jsx18(Ribbon, {})
5779
5867
  ]
5780
5868
  }
5781
5869
  ),
5782
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx17(LearnMore, {}) : /* @__PURE__ */ jsx17(Welcome, {}))
5870
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx18(LearnMore, {}) : /* @__PURE__ */ jsx18(Welcome, {}))
5783
5871
  ]
5784
5872
  }
5785
5873
  )
@@ -5823,54 +5911,71 @@ function readValue(raw) {
5823
5911
  }
5824
5912
 
5825
5913
  // src/lib/algoliaAppPicker.ts
5826
- function secondaryFor(app) {
5914
+ function blockReasonFor(app) {
5915
+ return app.status !== "active" ? "Paused" : "Missing permissions";
5916
+ }
5917
+ function secondaryFor(app, highlightId) {
5918
+ if (!canSelectApplication(app)) {
5919
+ return { kind: "text", value: blockReasonFor(app) };
5920
+ }
5921
+ if (app.id === highlightId) {
5922
+ return { kind: "badge", value: "[DETECTED]" };
5923
+ }
5827
5924
  return app.plan ? { kind: "badge", value: app.plan } : void 0;
5828
5925
  }
5829
5926
  function labelFor(app) {
5830
5927
  return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
5831
5928
  }
5832
- function selectAndReport(app) {
5833
- useWizard.getState().pushCliOutput(
5834
- "stdout",
5835
- `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
5836
- );
5837
- return selectApplication(app.id);
5929
+ async function selectAndReport(id) {
5930
+ const store = useWizard.getState();
5931
+ store.setSettingUpApp(id);
5932
+ try {
5933
+ return await selectApplication(id);
5934
+ } finally {
5935
+ store.setSettingUpApp(null);
5936
+ }
5838
5937
  }
5839
- async function promptForApplication(leadIn = []) {
5938
+ async function promptForApplication(highlight) {
5840
5939
  const store = useWizard.getState();
5841
- const apps = await listApplications();
5842
- if (apps.length === 0) {
5940
+ const unordered = await listApplications();
5941
+ if (unordered.length === 0) {
5843
5942
  throw new Error(
5844
5943
  "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
5845
5944
  );
5846
5945
  }
5847
- if (apps.length === 1) {
5848
- const only = apps[0];
5849
- logger.info(
5850
- { app: only.id },
5851
- "single application on the account; selecting it"
5946
+ if (!unordered.some(canSelectApplication)) {
5947
+ throw new Error(
5948
+ "None of the applications on this Algolia account are usable \u2014 each is either paused or missing key-management access. Activate one, or grant it the `keys` ACL, then restart the wizard."
5852
5949
  );
5853
- for (const line of leadIn) store.pushCliOutput("stdout", line);
5854
- return selectAndReport(only);
5855
5950
  }
5856
- const messages = [
5857
- ...leadIn,
5858
- "Which Algolia application should the wizard work in?"
5951
+ const apps = [
5952
+ ...unordered.filter(canSelectApplication),
5953
+ ...unordered.filter((app) => !canSelectApplication(app))
5859
5954
  ];
5955
+ const envApp = highlight ? unordered.find((app) => app.id === highlight.id) : void 0;
5956
+ const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
5957
+ const messages = [];
5958
+ if (highlight && highlightIndex < 0) {
5959
+ messages.push(
5960
+ envApp ? `Could not use ${highlight.id} from ${highlight.file} \u2014 it\u2019s ${blockReasonFor(envApp).toLowerCase()}. Pick another below.` : `Could not use ${highlight.id} from ${highlight.file} \u2014 it may have been removed, or this account may not have access to it.`
5961
+ );
5962
+ }
5860
5963
  for (; ; ) {
5861
5964
  const choice = await store.requestUserInput({
5862
- prompt: "Select an application",
5965
+ prompt: "Which Algolia application should the wizard work in?",
5863
5966
  promptType: "multipleChoice",
5864
5967
  options: apps.map(labelFor),
5865
- secondary: apps.map(secondaryFor),
5968
+ secondary: apps.map((app) => secondaryFor(app, highlight?.id)),
5969
+ disabled: apps.map((app) => !canSelectApplication(app)),
5970
+ ...highlightIndex >= 0 ? { defaultSelectedIndex: highlightIndex } : {},
5866
5971
  messages
5867
5972
  });
5868
5973
  const chosen = apps.find((app) => labelFor(app) === choice);
5869
- if (!chosen) {
5974
+ if (!chosen || !canSelectApplication(chosen)) {
5870
5975
  throw new Error("Application picker received an unexpected selection");
5871
5976
  }
5872
5977
  try {
5873
- return await selectAndReport(chosen);
5978
+ return await selectAndReport(chosen.id);
5874
5979
  } catch (err) {
5875
5980
  logger.warn(
5876
5981
  { app: chosen.id, err: err.message },
@@ -5882,42 +5987,11 @@ async function promptForApplication(leadIn = []) {
5882
5987
  }
5883
5988
  }
5884
5989
  }
5885
- async function confirmEnvApplication(env, current) {
5886
- const useEnv = `Use ${env.id} (from ${env.file})`;
5887
- const choice = await useWizard.getState().requestUserInput({
5888
- prompt: "Select an application",
5889
- promptType: "multipleChoice",
5890
- options: [
5891
- useEnv,
5892
- current ? `Use ${labelFor(current)} (already selected)` : "Pick a different application"
5893
- ],
5894
- messages: [
5895
- `${env.file} already sets ${env.name}=${env.id}.`,
5896
- "Which Algolia application should the wizard work in?"
5897
- ]
5898
- });
5899
- return choice === useEnv;
5900
- }
5901
- async function selectEnvApplication(env) {
5902
- try {
5903
- return await selectAndReport({ id: env.id, name: "" });
5904
- } catch (err) {
5905
- logger.warn(
5906
- { app: env.id, err: err.message },
5907
- "could not select the application named in env; falling back to the picker"
5908
- );
5909
- return promptForApplication([
5910
- `Could not select ${env.id} from ${env.file} \u2014 it may have been removed, or this account may not have access to it.`
5911
- ]);
5912
- }
5913
- }
5914
- async function ensureApplication() {
5915
- const current = await currentApplication();
5990
+ async function ensureApplication(resumeAppId) {
5991
+ if (resumeAppId) return selectAndReport(resumeAppId);
5916
5992
  const env = await findEnvApplicationId();
5917
- if (env && env.id !== current?.id && await confirmEnvApplication(env, current)) {
5918
- return selectEnvApplication(env);
5919
- }
5920
- return current ?? await promptForApplication();
5993
+ if (!env) return promptForApplication();
5994
+ return promptForApplication({ id: env.id, file: env.file });
5921
5995
  }
5922
5996
 
5923
5997
  // src/lib/seed.ts
@@ -6198,7 +6272,7 @@ function delay(ms) {
6198
6272
  }
6199
6273
 
6200
6274
  // src/main.tsx
6201
- import { jsx as jsx18 } from "react/jsx-runtime";
6275
+ import { jsx as jsx19 } from "react/jsx-runtime";
6202
6276
  async function startup() {
6203
6277
  setProjectRoot(process.cwd());
6204
6278
  let args;
@@ -6248,7 +6322,7 @@ ${formatStepList(workflow)}`);
6248
6322
  }
6249
6323
  async function run(workflow) {
6250
6324
  const store = useWizard.getState();
6251
- const instance = render(/* @__PURE__ */ jsx18(App, {}), { incrementalRendering: true });
6325
+ const instance = render(/* @__PURE__ */ jsx19(App, {}), { incrementalRendering: true });
6252
6326
  await store.waitForStart();
6253
6327
  store.syncSteps(
6254
6328
  workflow.steps.map((s) => ({
@@ -6281,14 +6355,17 @@ async function run(workflow) {
6281
6355
  }
6282
6356
  store.setUser(user);
6283
6357
  let app;
6358
+ const resumableState = await loadResumableState(workflow);
6284
6359
  try {
6285
- app = await ensureApplication();
6360
+ app = await ensureApplication(
6361
+ resumableState ? runAppId(resumableState) : void 0
6362
+ );
6286
6363
  } catch (err) {
6287
6364
  store.setError(err instanceof Error ? err.message : String(err));
6288
6365
  await instance.waitUntilExit();
6289
6366
  process.exit(1);
6290
6367
  }
6291
- runWorkflow(workflow, app.id);
6368
+ runWorkflow(workflow, app.id, resumableState);
6292
6369
  }
6293
6370
  await requestTerminalSize();
6294
6371
  var started = await startup();