@algolia/wizard 0.87.0-rc.211.681 → 0.88.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 +336 -220
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
- import { Box as Box22, Text as Text23, useApp as useApp2, useInput as useInput9, useWindowSize as useWindowSize8 } from "ink";
7
+ import { Box as Box23, Text as Text24, useApp as useApp2, useInput as useInput10, useWindowSize as useWindowSize8 } from "ink";
8
8
  import Spinner3 from "ink-spinner";
9
9
 
10
10
  // src/lib/terminalSize.ts
@@ -98,8 +98,8 @@ function runAlgoliaCli(args, { onOutput, redact } = {}) {
98
98
  let stdout = "";
99
99
  let stderr = "";
100
100
  const splitters = {
101
- stdout: lineSplitter((line) => onOutput?.("stdout", line)),
102
- stderr: lineSplitter((line) => onOutput?.("stderr", line))
101
+ stdout: lineSplitter((line) => onOutput?.("stdout", mask(line, redact))),
102
+ stderr: lineSplitter((line) => onOutput?.("stderr", mask(line, redact)))
103
103
  };
104
104
  child.stdout.on("data", (chunk) => {
105
105
  const text = String(chunk);
@@ -287,6 +287,16 @@ var CLI_OUTPUT_LIMIT = 200;
287
287
  function commandApprovalKey(command, cwd) {
288
288
  return `${cwd}\0${command}`;
289
289
  }
290
+ function appendOutputLog(logs, outputCount, entry) {
291
+ if (outputCount < CLI_OUTPUT_LIMIT) {
292
+ return { logs: [...logs, entry], _outputLogCount: outputCount + 1 };
293
+ }
294
+ const oldest = logs.findIndex((e) => e.kind === "output");
295
+ return {
296
+ logs: [...logs.filter((_, i) => i !== oldest), entry],
297
+ _outputLogCount: outputCount
298
+ };
299
+ }
290
300
  var useWizard = create((set, get) => ({
291
301
  phase: "idle",
292
302
  homeScreen: "home",
@@ -304,6 +314,7 @@ var useWizard = create((set, get) => ({
304
314
  writtenFiles: [],
305
315
  approvedCommands: /* @__PURE__ */ new Set(),
306
316
  logs: [],
317
+ _outputLogCount: 0,
307
318
  error: null,
308
319
  inputReq: null,
309
320
  _resolve: null,
@@ -396,11 +407,23 @@ var useWizard = create((set, get) => ({
396
407
  get()._clearNoticeQueue();
397
408
  set({ notices: [] });
398
409
  },
399
- pushCliOutput: (stream, text) => set((s) => ({
400
- cliOutput: [...s.cliOutput, { id: nanoid(), stream, text }].slice(
401
- -CLI_OUTPUT_LIMIT
402
- )
403
- })),
410
+ // Logged too, so a line cut off on the main screen can be read in the log view.
411
+ pushCliOutput: (stream, text) => {
412
+ const id = nanoid();
413
+ set((s) => ({
414
+ cliOutput: [...s.cliOutput, { id, stream, text }].slice(
415
+ -CLI_OUTPUT_LIMIT
416
+ ),
417
+ ...appendOutputLog(s.logs, s._outputLogCount, {
418
+ id,
419
+ kind: "output",
420
+ name: text,
421
+ stream,
422
+ status: "success",
423
+ startedAt: Date.now()
424
+ })
425
+ }));
426
+ },
404
427
  clearCliOutput: () => set({ cliOutput: [] }),
405
428
  setReview: (review2) => set({ review: review2 }),
406
429
  setTargetIndex: (index) => set({ targetIndex: index }),
@@ -474,6 +497,7 @@ var useWizard = create((set, get) => ({
474
497
  writtenFiles: [],
475
498
  approvedCommands: /* @__PURE__ */ new Set(),
476
499
  logs: [],
500
+ _outputLogCount: 0,
477
501
  error: null,
478
502
  inputReq: null,
479
503
  _resolve: null,
@@ -713,6 +737,32 @@ import { useState as useState9 } from "react";
713
737
  import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
714
738
  import { useLayoutEffect as useLayoutEffect3, useState as useState6 } from "react";
715
739
 
740
+ // src/lib/wrapExact.ts
741
+ function wrapExactLine(line, width) {
742
+ if (line.length === 0) return [" "];
743
+ const lines = [];
744
+ let i = 0;
745
+ while (i < line.length) {
746
+ if (line.length - i <= width) {
747
+ lines.push(line.slice(i));
748
+ break;
749
+ }
750
+ let end = i + width;
751
+ if (line.charAt(end) !== " ") {
752
+ const lastSpace = line.lastIndexOf(" ", end - 1);
753
+ if (lastSpace >= i) end = lastSpace + 1;
754
+ }
755
+ if (end <= i) end = i + width;
756
+ lines.push(line.slice(i, end));
757
+ i = end;
758
+ }
759
+ return lines;
760
+ }
761
+ function wrapExact(text, width) {
762
+ if (width <= 0) return [text || " "];
763
+ return text.split(/\r\n|\n|\r/).flatMap((line) => wrapExactLine(line, width));
764
+ }
765
+
716
766
  // src/ui/DecisionSelect.tsx
717
767
  import { Box as Box5, Text as Text5, useInput } from "ink";
718
768
  import { useState as useState3 } from "react";
@@ -1028,30 +1078,6 @@ function Review() {
1028
1078
  import { jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
1029
1079
  var BLANK2 = { prefix: "", text: " ", color: COLORS.dim };
1030
1080
  var COMMAND_PREFIX = "$ ";
1031
- function wrapExactLine(line, width) {
1032
- if (line.length === 0) return [" "];
1033
- const lines = [];
1034
- let i = 0;
1035
- while (i < line.length) {
1036
- if (line.length - i <= width) {
1037
- lines.push(line.slice(i));
1038
- break;
1039
- }
1040
- let end = i + width;
1041
- if (line.charAt(end) !== " ") {
1042
- const lastSpace = line.lastIndexOf(" ", end - 1);
1043
- if (lastSpace >= i) end = lastSpace + 1;
1044
- }
1045
- if (end <= i) end = i + width;
1046
- lines.push(line.slice(i, end));
1047
- i = end;
1048
- }
1049
- return lines;
1050
- }
1051
- function wrapExact(text, width) {
1052
- if (width <= 0) return [text || " "];
1053
- return text.split(/\r\n|\n|\r/).flatMap((line) => wrapExactLine(line, width));
1054
- }
1055
1081
  function fieldLines(prefix, value, color, width) {
1056
1082
  return wrapText(value, Math.max(width - prefix.length, 1)).map((text, i) => ({
1057
1083
  prefix: i === 0 ? prefix : " ".repeat(prefix.length),
@@ -2332,7 +2358,7 @@ async function ensureApplication(resumeAppId) {
2332
2358
  // package.json
2333
2359
  var package_default = {
2334
2360
  name: "@algolia/wizard",
2335
- version: "0.87.0-rc.211.681",
2361
+ version: "0.88.0",
2336
2362
  description: "Magically implement Algolia functionality in your codebase",
2337
2363
  type: "module",
2338
2364
  engines: {
@@ -2805,9 +2831,6 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
2805
2831
  }
2806
2832
  }
2807
2833
 
2808
- // src/actions/selectIndex.ts
2809
- import { basename } from "node:path";
2810
-
2811
2834
  // src/actions/listIndices.ts
2812
2835
  import { z as z5 } from "zod";
2813
2836
  var indicesListSchema = z5.object({
@@ -2842,28 +2865,11 @@ function indexNameError(name) {
2842
2865
  return "Index name cannot end with .txt, .bin, or .idx.";
2843
2866
  return void 0;
2844
2867
  }
2845
- function slug(text) {
2846
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
2847
- }
2848
- function suggestIndexName(project, entity, taken) {
2849
- const base = ([slug(project), slug(entity ?? "")].filter(Boolean).join("_") || "records").slice(0, MAX_INDEX_NAME_LENGTH);
2850
- let name = base;
2851
- for (let n = 2; taken.includes(name); n++) {
2852
- const suffix = `_${n}`;
2853
- name = base.slice(0, MAX_INDEX_NAME_LENGTH - suffix.length) + suffix;
2854
- }
2855
- return name;
2856
- }
2857
2868
  var selectIndexStep = async (ctx) => {
2858
2869
  await requireApplication(ctx.appId);
2859
2870
  const indices = await listIndices();
2860
2871
  const names = indices.map((i) => i.name);
2861
2872
  const hasIndices = names.length > 0;
2862
- const entity = ctx.getStepOutput(
2863
- "confirm-entities"
2864
- )?.confirmedEntities?.[0]?.name;
2865
- const suggested = suggestIndexName(basename(process.cwd()), entity, names);
2866
- const suggestionHint = `Press Enter to use "${suggested}".`;
2867
2873
  let error;
2868
2874
  let chosen;
2869
2875
  while (chosen === void 0) {
@@ -2878,8 +2884,7 @@ var selectIndexStep = async (ctx) => {
2878
2884
  promptType: "textInput",
2879
2885
  options: [],
2880
2886
  messages: [
2881
- "This app has no existing indices \u2014 enter a name to create one.",
2882
- suggestionHint
2887
+ "This app has no existing indices \u2014 enter a name to create one."
2883
2888
  ],
2884
2889
  error
2885
2890
  });
@@ -2889,18 +2894,17 @@ var selectIndexStep = async (ctx) => {
2889
2894
  let candidate;
2890
2895
  let isNewName = true;
2891
2896
  if (!hasIndices) {
2892
- candidate = selection.trim() || suggested;
2897
+ candidate = selection.trim();
2893
2898
  } else if (selection === CREATE_NEW_INDEX) {
2894
2899
  const name = await ctx.requestUserInput({
2895
2900
  prompt: "Name the new index:",
2896
2901
  promptType: "textInput",
2897
- options: [],
2898
- messages: [suggestionHint]
2902
+ options: []
2899
2903
  });
2900
2904
  if (typeof name !== "string") {
2901
2905
  throw new Error("selectIndex received an unexpected non-text result");
2902
2906
  }
2903
- candidate = name.trim() || suggested;
2907
+ candidate = name.trim();
2904
2908
  } else {
2905
2909
  candidate = selection;
2906
2910
  isNewName = false;
@@ -3136,14 +3140,14 @@ import z9 from "zod";
3136
3140
  import { readFile as readFile4 } from "node:fs/promises";
3137
3141
 
3138
3142
  // src/lib/tools/env.ts
3139
- import { basename as basename2 } from "node:path";
3143
+ import { basename } from "node:path";
3140
3144
  function isEnvFile(filePath) {
3141
- const name = basename2(filePath);
3145
+ const name = basename(filePath);
3142
3146
  return name === ".env" || name.startsWith(".env.");
3143
3147
  }
3144
3148
  function isSecretEnvFile(filePath) {
3145
3149
  if (!isEnvFile(filePath)) return false;
3146
- const name = basename2(filePath);
3150
+ const name = basename(filePath);
3147
3151
  return !/\.(example|sample|template)$/.test(name);
3148
3152
  }
3149
3153
  function secretEnvWriteRefusal(filePath) {
@@ -3778,7 +3782,7 @@ import { execFile as execFile2 } from "node:child_process";
3778
3782
  import { tool as tool8 } from "ai";
3779
3783
  import z15 from "zod";
3780
3784
  import { lstat as lstat3, readdir as readdir2, readFile as readFile8, realpath, stat as stat2 } from "node:fs/promises";
3781
- import { basename as basename3, isAbsolute as isAbsolute2, join as join8, relative as relative4, sep as sep2 } from "node:path";
3785
+ import { basename as basename2, isAbsolute as isAbsolute2, join as join8, relative as relative4, sep as sep2 } from "node:path";
3782
3786
  import { performance } from "node:perf_hooks";
3783
3787
  import { Worker } from "node:worker_threads";
3784
3788
  var MAX_GIT_OUTPUT_BYTES = 32e6;
@@ -4121,7 +4125,7 @@ async function safeSearchPath(rootPath, file) {
4121
4125
  }
4122
4126
  async function searchableFiles(root, target) {
4123
4127
  try {
4124
- if (SKIP_FILES.has(basename3(target)) && (await lstat3(target)).isFile()) {
4128
+ if (SKIP_FILES.has(basename2(target)) && (await lstat3(target)).isFile()) {
4125
4129
  return [target];
4126
4130
  }
4127
4131
  const gitRoot = await gitRootForTarget(root, target);
@@ -4300,7 +4304,7 @@ import { tool as tool9 } from "ai";
4300
4304
  import z16 from "zod";
4301
4305
  import { createHash as createHash2 } from "node:crypto";
4302
4306
  import { readFile as readFile9, stat as stat3 } from "node:fs/promises";
4303
- import { basename as basename4, relative as relative5, resolve as resolve3 } from "node:path";
4307
+ import { basename as basename3, relative as relative5, resolve as resolve3 } from "node:path";
4304
4308
 
4305
4309
  // src/lib/tools/utils/prompt.ts
4306
4310
  var chain = Promise.resolve();
@@ -4398,7 +4402,7 @@ function commandDiffersFromReviewedScript(ctx, command, cwd) {
4398
4402
  if (approval.command === command && approval.cwd === cwd) return false;
4399
4403
  if (approval.command === command) return true;
4400
4404
  const scriptReferences = [
4401
- basename4(approval.filePath),
4405
+ basename3(approval.filePath),
4402
4406
  relative5(ctx.root, approval.filePath),
4403
4407
  ".algolia-wizard"
4404
4408
  ];
@@ -5119,8 +5123,8 @@ function generateRecordTool(ctx, createModel = defaultCreateModel2) {
5119
5123
  ...record,
5120
5124
  objectID: nanoid2()
5121
5125
  }));
5122
- const slug2 = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
5123
- const relPath = `${DATA_DIR}/${slug2}.json`;
5126
+ const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
5127
+ const relPath = `${DATA_DIR}/${slug}.json`;
5124
5128
  const resolved2 = resolveInRoot(ctx, relPath);
5125
5129
  if (resolved2.ok === false) return resolved2.error;
5126
5130
  const writeError = await safeWriteFile(
@@ -6073,7 +6077,7 @@ import {
6073
6077
  // src/lib/git.ts
6074
6078
  import { execFile as execFile3 } from "node:child_process";
6075
6079
  import { copyFile, mkdir as mkdir4, stat as stat4 } from "node:fs/promises";
6076
- import { basename as basename5, dirname as dirname3, isAbsolute as isAbsolute3, join as join9, resolve as resolve4 } from "node:path";
6080
+ import { basename as basename4, dirname as dirname3, isAbsolute as isAbsolute3, join as join9, resolve as resolve4 } from "node:path";
6077
6081
  var MAX_BUFFER = 32 * 1024 * 1024;
6078
6082
  function git(args) {
6079
6083
  return new Promise((resolve6, reject) => {
@@ -6110,7 +6114,7 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
6110
6114
  } catch {
6111
6115
  return { ok: false, reason: `"${sourcePath}" does not exist` };
6112
6116
  }
6113
- const relPath = join9(ingestDir, basename5(source));
6117
+ const relPath = join9(ingestDir, basename4(source));
6114
6118
  const dest = join9(repoRoot, relPath);
6115
6119
  if (resolve4(source) === resolve4(dest)) {
6116
6120
  return { ok: true, relPath };
@@ -6495,29 +6499,9 @@ function cdnSearchInstructions(input) {
6495
6499
  input.searchKey ? `Set window.${SEARCH_CONFIG_SEARCH_KEY} to "${input.searchKey}".` : `A real search-only key could not be provisioned${input.searchKeyError ? ` (${input.searchKeyError})` : ""} \u2014 set window.${SEARCH_CONFIG_SEARCH_KEY} to the placeholder "${SEARCH_KEY_PLACEHOLDER}" and add a prominent TODO for the developer to fill in a real one.`,
6496
6500
  'Load these exact pinned tags in order before the config and search scripts: <script src="https://cdn.jsdelivr.net/npm/algoliasearch@5.59.0/dist/lite/builds/browser.umd.js" integrity="sha256-pduQHl1jn0IaN/BIpSotQm7Y5THdpKcX3Bw6xdRteRw=" crossorigin="anonymous"></script> then <script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.117.0/dist/instantsearch.production.min.js" integrity="sha256-5zhKxAGeH7ThWfnmdD4pS1gsmafE2b8aWRsq8set/dc=" crossorigin="anonymous"></script>.',
6497
6501
  'When using the InstantSearch theme, load this exact tag before the application styles: <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@8.22.1/themes/satellite-min.css" integrity="sha256-5/eHPZl63VYJSDVOKrOgGe/5+owUkX3uPAW6+XYeLNc=" crossorigin="anonymous">. Never use an unversioned or differently versioned theme URL, and never omit its integrity attribute.',
6498
- `In that classic search script, use window['algoliasearch/lite'].liteClient and window.instantsearch. Add window.instantsearch.widgets.autocomplete as the only search widget. Configure it with container '#autocomplete', the content-specific placeholder ${JSON.stringify(placeholder)}, and an indices array containing one object whose indexName is window.${SEARCH_CONFIG_INDEX_NAME}. Put the item and noResults functions in the templates option of that indices entry, implement getURL only for an existing route, and match the item template to the entity schema. Keep insights: true and call search.start(). Do not substitute searchBox or hits, omit indices, use imports or exports, or use type="module".`,
6499
- CDN_AUTOCOMPLETE_SHAPE
6502
+ `In that classic search script, use window['algoliasearch/lite'].liteClient and window.instantsearch. Add window.instantsearch.widgets.autocomplete as the only search widget. Configure it with container '#autocomplete', the content-specific placeholder ${JSON.stringify(placeholder)}, and an indices array containing one object whose indexName is window.${SEARCH_CONFIG_INDEX_NAME}; put item and noResults functions in its templates option, implement getURL only for an existing route, and match the item template to the entity schema. Keep insights: true and call search.start(). Do not substitute searchBox or hits, omit indices, use imports or exports, or use type="module".`
6500
6503
  ];
6501
6504
  }
6502
- var CDN_AUTOCOMPLETE_SHAPE = `Use this shape for the autocomplete call. Replace the item fields and the getURL route with the real record fields and an existing route:
6503
- \`\`\`js
6504
- window.instantsearch.widgets.autocomplete({
6505
- container: '#autocomplete',
6506
- placeholder: '...',
6507
- indices: [
6508
- {
6509
- indexName: window.${SEARCH_CONFIG_INDEX_NAME},
6510
- getURL: (item) => '/existing-route/' + item.slug,
6511
- templates: {
6512
- item: ({ item, onSelect }, { html, components }) =>
6513
- html\`<div onClick=\${onSelect}>\${components.Highlight({ hit: item, attribute: 'name' })}</div>\`,
6514
- noResults: (_, { html }) => html\`<span>No results</span>\`,
6515
- },
6516
- },
6517
- ],
6518
- })
6519
- \`\`\`
6520
- The widget reads the item and noResults templates only from each indices entry. Its own top-level templates option accepts only panel. If item is not inside indices[].templates, the widget throws "Template must be 'string' or 'function', was 'undefined' (key: item)" on the first search and shows no results.`;
6521
6505
  function validationInstructions(input) {
6522
6506
  return [
6523
6507
  "Validate the Algolia search implementation by inspecting the project source.",
@@ -6538,8 +6522,7 @@ function validationInstructions(input) {
6538
6522
  "Do not modify files. Report each implementation issue as a concrete instruction for the next implementation pass.",
6539
6523
  `Detected verification tools: ${JSON.stringify(input.findings.verification ?? [])}. Use only a type checker; ignore any linter or formatter in this list.`,
6540
6524
  ...!input.frontendHasPackageJson ? [
6541
- "The frontend has no package.json: never create one or run npm, pnpm, yarn, or bun. Validate the CDN implementation by inspecting its HTML or template and classic scripts for the search container, Algolia \u2192 InstantSearch \u2192 config \u2192 search load order, browser globals, autocomplete widget configuration, search.start(), and exact pinned CDN URLs and integrity attributes for every Algolia script or stylesheet. Source inspection is required even when the project has no automated checks. window.instantsearch.widgets.autocomplete is a real widget in the pinned InstantSearch build; do not report its use as an error, and do not fetch a remote bundle or URL to verify it.",
6542
- "In the autocomplete call, confirm that the item and noResults templates are inside the templates option of the indices entry, not in the top-level templates option of the widget. The top-level templates option accepts only panel, so an item template there throws on the first search and no results show. If item is missing from indices[].templates, set sufficient=false and tell the next pass to move the templates into the indices entry."
6525
+ "The frontend has no package.json: never create one or run npm, pnpm, yarn, or bun. Validate the CDN implementation by inspecting its HTML or template and classic scripts for the search container, Algolia \u2192 InstantSearch \u2192 config \u2192 search load order, browser globals, autocomplete widget configuration, search.start(), and exact pinned CDN URLs and integrity attributes for every Algolia script or stylesheet. Source inspection is required even when the project has no automated checks. window.instantsearch.widgets.autocomplete is a real widget in the pinned InstantSearch build; do not report its use as an error, and do not fetch a remote bundle or URL to verify it."
6543
6526
  ] : [],
6544
6527
  "Always call reportStatus with status=success after validation, even when sufficient=false.",
6545
6528
  "Set sufficient=false if the input's computed accessible name is empty or generic, including when aria-labelledby references a missing or empty element; a placeholder does not count as the label.",
@@ -6679,8 +6662,8 @@ async function firstRecordIn(filePath) {
6679
6662
  async function ingestedRecordSample(repoRoot, entityName, uploadFilePath) {
6680
6663
  const candidates = [];
6681
6664
  if (entityName) {
6682
- const slug2 = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
6683
- candidates.push(join11(repoRoot, INGEST_DIR, "data", `${slug2}.json`));
6665
+ const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
6666
+ candidates.push(join11(repoRoot, INGEST_DIR, "data", `${slug}.json`));
6684
6667
  }
6685
6668
  if (uploadFilePath?.toLowerCase().endsWith(".json")) {
6686
6669
  candidates.push(join11(repoRoot, uploadFilePath));
@@ -7477,25 +7460,14 @@ var defaultWorkflow = {
7477
7460
  "ingestion"
7478
7461
  );
7479
7462
  return reviewStep(ctx, {
7480
- nextStepsGuidance: ingestionGuidance(ingestion2)
7463
+ // ingestCommand was already shown verbatim as a notice; an
7464
+ // LLM-paraphrased restatement in nextSteps risks being wrong.
7465
+ nextStepsGuidance: ingestion2?.ingestScriptRan ? "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them." : "Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above."
7481
7466
  });
7482
7467
  }
7483
7468
  })
7484
7469
  ]
7485
7470
  };
7486
- function ingestionGuidance(ingestion2) {
7487
- if (ingestion2?.ingestScriptRan) {
7488
- return "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them.";
7489
- }
7490
- const partialCount = ingestion2?.ingestRecordCount ?? 0;
7491
- if (partialCount > 0) {
7492
- return `The ingestion script indexed ${partialCount} record(s) and then failed before it finished. Say that plainly. Do not say that ingestion succeeded or that no records were indexed. Tell the user to fix the error shown above and run the ingestion script again. Do not restate the exact command, because the wizard already showed it to them above.`;
7493
- }
7494
- if (ingestion2?.ingestCommand) {
7495
- return "No records were indexed in this run. Say that plainly, and do not say that the wizard created or populated an index. Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above.";
7496
- }
7497
- return "No ingestion script ran and no records were indexed in this run. Say that plainly. Do not say that the wizard created, selected, or populated an index, and do not tell the user to run an ingestion script, because the wizard has no tested command for one. Tell the user to run the wizard again to create and run the ingestion script.";
7498
- }
7499
7471
 
7500
7472
  // src/actions/playbookSteps/selectPlaybook.ts
7501
7473
  import { z as z33 } from "zod";
@@ -7628,9 +7600,9 @@ async function getPlaybooks() {
7628
7600
  );
7629
7601
  return data;
7630
7602
  }
7631
- async function createPlaybookExecution(appId, slug2, indexName, options) {
7603
+ async function createPlaybookExecution(appId, slug, indexName, options) {
7632
7604
  const { data } = await dashboardRequest(
7633
- `/api/1/playbook-integrations/applications/${appId}/playbooks/${slug2}/executions`,
7605
+ `/api/1/playbook-integrations/applications/${appId}/playbooks/${slug}/executions`,
7634
7606
  playbookExecutionCreateResponseSchema,
7635
7607
  {
7636
7608
  method: "POST",
@@ -7671,9 +7643,9 @@ async function assessPlaybookExecution(uuid) {
7671
7643
  var selectPlaybookSchema = z33.object({
7672
7644
  slug: z33.string()
7673
7645
  });
7674
- function persistSlug(ctx, slug2) {
7675
- ctx.setUserInput("playbookSlug", slug2);
7676
- return { slug: slug2 };
7646
+ function persistSlug(ctx, slug) {
7647
+ ctx.setUserInput("playbookSlug", slug);
7648
+ return { slug };
7677
7649
  }
7678
7650
  async function selectPlaybookStep(ctx, requestedSlug) {
7679
7651
  const playbooks = await getPlaybooks();
@@ -7804,13 +7776,13 @@ async function confirmIndexStep(ctx) {
7804
7776
  if (!sampleIndex) {
7805
7777
  return null;
7806
7778
  }
7807
- const { slug: slug2 } = ctx.getStepOutput("select-playbook");
7779
+ const { slug } = ctx.getStepOutput("select-playbook");
7808
7780
  const selection = await ctx.requestUserInput({
7809
7781
  prompt: "Ready to create sample index?",
7810
7782
  promptType: "multipleChoice",
7811
7783
  options: [CONTINUE_OPTION, GO_BACK_OPTION2],
7812
7784
  messages: [
7813
- `A new index called "algolia_playbooks_${slug2}_sample_index" will be created with sample ${slug2} data.`
7785
+ `A new index called "algolia_playbooks_${slug}_sample_index" will be created with sample ${slug} data.`
7814
7786
  ],
7815
7787
  helpText: "Going back lets you choose a different index."
7816
7788
  });
@@ -7962,13 +7934,13 @@ var POLL_NOTICES = [
7962
7934
  { message: "detecting required fields\u2026" }
7963
7935
  ];
7964
7936
  async function dataAssessmentStep(ctx) {
7965
- const { slug: slug2 } = ctx.getStepOutput("select-playbook");
7937
+ const { slug } = ctx.getStepOutput("select-playbook");
7966
7938
  const { indexName, existingExecution } = indexForAssessment(ctx);
7967
7939
  if (existingExecution) return existingExecution;
7968
7940
  try {
7969
7941
  showNotice(ctx, CREATING_EXECUTION_NOTICE);
7970
7942
  const { id: appId } = await requireApplication();
7971
- const execution = await resolveExecution(ctx, appId, slug2, indexName);
7943
+ const execution = await resolveExecution(ctx, appId, slug, indexName);
7972
7944
  const assessed = await runAssessment(ctx, execution, indexName);
7973
7945
  ctx.clearNotices();
7974
7946
  await promptReady(ctx, assessed);
@@ -8073,24 +8045,24 @@ function unexpectedAssessmentStatus(status) {
8073
8045
  `data-assessment received an unexpected assessment status: ${status}`
8074
8046
  );
8075
8047
  }
8076
- async function resolveExecution(ctx, appId, slug2, indexName) {
8077
- const created = await createWithRecovery(ctx, appId, slug2, indexName);
8048
+ async function resolveExecution(ctx, appId, slug, indexName) {
8049
+ const created = await createWithRecovery(ctx, appId, slug, indexName);
8078
8050
  if (!created.resumed) return created;
8079
8051
  const choice = await promptResumeExecution(ctx, indexName);
8080
8052
  if (choice === CONTINUE_EXECUTION_OPTION) return created;
8081
8053
  if (choice === RESTART_OPTION) {
8082
- return createWithRecovery(ctx, appId, slug2, indexName, { forceNew: true });
8054
+ return createWithRecovery(ctx, appId, slug, indexName, { forceNew: true });
8083
8055
  }
8084
8056
  if (choice === GO_BACK_OPTION3) {
8085
8057
  return ctx.returnToStep("select-index");
8086
8058
  }
8087
8059
  throw new Error("data-assessment received an unexpected execution choice");
8088
8060
  }
8089
- async function createWithRecovery(ctx, appId, slug2, indexName, options) {
8061
+ async function createWithRecovery(ctx, appId, slug, indexName, options) {
8090
8062
  return retryOnDashboardError(
8091
8063
  () => {
8092
8064
  showNotice(ctx, CREATING_EXECUTION_NOTICE);
8093
- return createPlaybookExecution(appId, slug2, indexName, options);
8065
+ return createPlaybookExecution(appId, slug, indexName, options);
8094
8066
  },
8095
8067
  onRequestFailure(ctx, indexName)
8096
8068
  );
@@ -8692,10 +8664,15 @@ var sidebarCommands = [
8692
8664
  { keyHint: "tab", description: "toggle logs" },
8693
8665
  { keyHint: "esc", description: "exit wizard" }
8694
8666
  ];
8667
+ var logsSidebarCommands = [
8668
+ { keyHint: "tab", description: "toggle logs" },
8669
+ { keyHint: "esc", description: "back" }
8670
+ ];
8695
8671
 
8696
8672
  // src/ui/Sidebar.tsx
8697
8673
  import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
8698
- function Sidebar() {
8674
+ function Sidebar({ inLogs = false }) {
8675
+ const commands = inLogs ? logsSidebarCommands : sidebarCommands;
8699
8676
  return /* @__PURE__ */ jsxs16(
8700
8677
  Box17,
8701
8678
  {
@@ -8712,7 +8689,7 @@ function Sidebar() {
8712
8689
  ] }),
8713
8690
  /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", gap: 1, children: [
8714
8691
  /* @__PURE__ */ jsx16(Progress, {}),
8715
- /* @__PURE__ */ jsx16(Box17, { flexDirection: "column", children: sidebarCommands.map((c) => {
8692
+ /* @__PURE__ */ jsx16(Box17, { flexDirection: "column", children: commands.map((c) => {
8716
8693
  return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", gap: 1, children: [
8717
8694
  /* @__PURE__ */ jsx16(Text18, { color: COLORS.primary, children: `[${c.keyHint}]` }),
8718
8695
  /* @__PURE__ */ jsx16(Text18, { color: COLORS.muted, children: c.description })
@@ -8750,23 +8727,130 @@ function Ribbon() {
8750
8727
  }
8751
8728
 
8752
8729
  // src/ui/App.tsx
8753
- import { useState as useState12 } from "react";
8730
+ import { useState as useState14 } from "react";
8754
8731
 
8755
8732
  // src/ui/Logs.tsx
8733
+ import { Box as Box20, Text as Text21, useInput as useInput9 } from "ink";
8734
+ import { useLayoutEffect as useLayoutEffect6, useState as useState12 } from "react";
8735
+
8736
+ // src/ui/LogDetails.tsx
8756
8737
  import { Box as Box19, Text as Text20, useInput as useInput8 } from "ink";
8757
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
8738
+ import { useLayoutEffect as useLayoutEffect5, useMemo, useState as useState11 } from "react";
8739
+
8740
+ // src/ui/logFormat.ts
8758
8741
  var KIND_COLOR = {
8759
8742
  tool: COLORS.primary,
8760
- prompt: COLORS.badge
8743
+ prompt: COLORS.badge,
8744
+ output: COLORS.muted
8745
+ };
8746
+ var STREAM_COLOR = {
8747
+ stderr: COLORS.muted,
8748
+ stdout: COLORS.dim
8761
8749
  };
8762
8750
  var STATUS_COLOR = {
8763
8751
  running: COLORS.status.running,
8764
8752
  error: COLORS.danger
8765
8753
  };
8766
8754
  function logNameColor(entry) {
8767
- return STATUS_COLOR[entry.status] ?? KIND_COLOR[entry.kind];
8755
+ const base = entry.stream ? STREAM_COLOR[entry.stream] : KIND_COLOR[entry.kind];
8756
+ return STATUS_COLOR[entry.status] ?? base;
8757
+ }
8758
+ function formatTimestamp(ms) {
8759
+ const d = new Date(ms);
8760
+ const pad = (n) => String(n).padStart(2, "0");
8761
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
8762
+ }
8763
+ function durationText(entry) {
8764
+ return entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
8768
8765
  }
8766
+
8767
+ // src/ui/LogDetails.tsx
8768
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
8769
+ var BLANK4 = { text: " ", color: COLORS.dim };
8770
+ var VALUE_INDENT = " ";
8771
+ var TAB = " ";
8772
+ function wrapLines(text, width, line, indent = "") {
8773
+ return wrapExact(
8774
+ text.replaceAll(" ", TAB),
8775
+ Math.max(width - indent.length, 1)
8776
+ ).map((part) => ({ ...line, text: indent + part }));
8777
+ }
8778
+ function formatValue(value) {
8779
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
8780
+ }
8781
+ function inputFields(input) {
8782
+ if (input === void 0) return [];
8783
+ if (input !== null && typeof input === "object" && !Array.isArray(input)) {
8784
+ return Object.entries(input).filter(([, value]) => value !== void 0).map(([label, value]) => [label, formatValue(value)]);
8785
+ }
8786
+ return [["input", formatValue(input)]];
8787
+ }
8788
+ function metaText(entry) {
8789
+ const parts = [`[${formatTimestamp(entry.startedAt)}]`];
8790
+ if (entry.kind === "tool") parts.push(entry.status, durationText(entry));
8791
+ return parts.filter(Boolean).join(" ");
8792
+ }
8793
+ function buildDetailLines(entry, width) {
8794
+ const lines = wrapLines(entry.name, width, {
8795
+ color: logNameColor(entry),
8796
+ bold: true
8797
+ });
8798
+ lines.push({ text: metaText(entry), color: COLORS.dim });
8799
+ for (const [label, value] of inputFields(entry.input)) {
8800
+ lines.push(BLANK4, { text: label, color: COLORS.muted });
8801
+ lines.push(
8802
+ ...wrapLines(value, width, { color: COLORS.primary }, VALUE_INDENT)
8803
+ );
8804
+ }
8805
+ return lines;
8806
+ }
8807
+ function LogDetails({
8808
+ entry,
8809
+ onClose
8810
+ }) {
8811
+ const [width, setWidth] = useState11(0);
8812
+ const lines = useMemo(
8813
+ () => width > 0 ? buildDetailLines(entry, width) : [],
8814
+ [entry, width]
8815
+ );
8816
+ const scroll = useScrollWindow({ itemCount: lines.length });
8817
+ useLayoutEffect5(() => {
8818
+ if (scroll.measured) setWidth(scroll.width);
8819
+ }, [scroll.measured, scroll.width]);
8820
+ useInput8((input, key) => {
8821
+ if (input === "k" || key.upArrow) scroll.scrollBy(-1);
8822
+ else if (input === "j" || key.downArrow) scroll.scrollBy(1);
8823
+ else if (key.escape) onClose();
8824
+ });
8825
+ const visible = lines.slice(scroll.offset, scroll.offset + scroll.capacity);
8826
+ return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", flexGrow: 1, children: [
8827
+ /* @__PURE__ */ jsx18(ScrollView, { scroll, children: visible.map((line, i) => /* @__PURE__ */ jsx18(
8828
+ Text20,
8829
+ {
8830
+ color: line.color,
8831
+ bold: line.bold,
8832
+ wrap: "truncate",
8833
+ children: line.text
8834
+ },
8835
+ `detail-${scroll.offset + i}`
8836
+ )) }),
8837
+ /* @__PURE__ */ jsxs18(Box19, { gap: 2, children: [
8838
+ scroll.maxOffset > 0 && /* @__PURE__ */ jsxs18(Text20, { children: [
8839
+ /* @__PURE__ */ jsx18(Text20, { color: COLORS.primary, children: "[j] [k]" }),
8840
+ /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: " scroll" })
8841
+ ] }),
8842
+ /* @__PURE__ */ jsxs18(Text20, { children: [
8843
+ /* @__PURE__ */ jsx18(Text20, { color: COLORS.primary, children: "[esc]" }),
8844
+ /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: " back" })
8845
+ ] })
8846
+ ] })
8847
+ ] });
8848
+ }
8849
+
8850
+ // src/ui/Logs.tsx
8851
+ import { Fragment, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
8769
8852
  var ROW_GAP = 1;
8853
+ var SELECTED_MARKER = "\u276F";
8770
8854
  function truncate3(str, maxWidth) {
8771
8855
  if (maxWidth <= 0) return "";
8772
8856
  return str.length > maxWidth ? `${str.slice(0, maxWidth - 1)}\u2026` : str;
@@ -8777,53 +8861,84 @@ function rawInputText(input) {
8777
8861
  if (!str || str === "{}") return "";
8778
8862
  return str.replace(/\s+/g, " ").trim();
8779
8863
  }
8780
- function formatTimestamp(ms) {
8781
- const d = new Date(ms);
8782
- const pad = (n) => String(n).padStart(2, "0");
8783
- return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
8784
- }
8785
- function Logs() {
8864
+ function Logs({ onBack }) {
8786
8865
  const logs = useWizard((s) => s.logs);
8787
- const scroll = useScrollWindow({ itemCount: logs.length, followBottom: true });
8788
- useInput8((input, key) => {
8789
- if (input === "k" || key.upArrow) scroll.scrollBy(-1);
8790
- else if (input === "j" || key.downArrow) scroll.scrollBy(1);
8866
+ const [openLogId, setOpenLogId] = useState12(null);
8867
+ const [selectedId, setSelectedId] = useState12(null);
8868
+ const found = selectedId === null ? -1 : logs.findIndex((e) => e.id === selectedId);
8869
+ if (selectedId !== null && found < 0) setSelectedId(null);
8870
+ const scroll = useScrollWindow({
8871
+ itemCount: logs.length,
8872
+ followBottom: selectedId === null
8791
8873
  });
8874
+ const selectedIndex = found >= 0 ? found : logs.length - 1;
8875
+ const openEntry = logs.find((e) => e.id === openLogId);
8876
+ const { revealIndex } = scroll;
8877
+ useLayoutEffect6(() => {
8878
+ if (selectedIndex >= 0) revealIndex(selectedIndex);
8879
+ }, [selectedIndex, revealIndex]);
8880
+ const moveSelection = (delta) => {
8881
+ if (logs.length === 0) return;
8882
+ const next = Math.min(Math.max(selectedIndex + delta, 0), logs.length - 1);
8883
+ setSelectedId(next === logs.length - 1 ? null : logs[next].id);
8884
+ };
8885
+ useInput9(
8886
+ (input, key) => {
8887
+ if (input === "k" || key.upArrow) moveSelection(-1);
8888
+ else if (input === "j" || key.downArrow) moveSelection(1);
8889
+ else if (key.return && logs[selectedIndex]) {
8890
+ setOpenLogId(logs[selectedIndex].id);
8891
+ } else if (key.escape) onBack();
8892
+ },
8893
+ { isActive: !openEntry }
8894
+ );
8792
8895
  const visible = logs.slice(scroll.offset, scroll.offset + scroll.capacity);
8793
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
8794
- logs.length === 0 && /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: "No logs yet." }),
8795
- /* @__PURE__ */ jsx18(ScrollView, { scroll, children: visible.map((entry) => {
8896
+ return /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: openEntry ? /* @__PURE__ */ jsx19(LogDetails, { entry: openEntry, onClose: () => setOpenLogId(null) }) : /* @__PURE__ */ jsxs19(Fragment, { children: [
8897
+ logs.length === 0 && /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: "No logs yet." }),
8898
+ /* @__PURE__ */ jsx19(ScrollView, { scroll, children: visible.map((entry, i) => {
8899
+ const selected = scroll.offset + i === selectedIndex;
8796
8900
  const timestamp = `[${formatTimestamp(entry.startedAt)}]`;
8797
- const durationText = entry.kind === "tool" && entry.durationMs !== void 0 ? `${entry.durationMs}ms` : "";
8901
+ const duration = durationText(entry);
8798
8902
  const rawPreview = rawInputText(entry.input);
8799
- const partCount = 2 + (rawPreview ? 1 : 0) + (durationText ? 1 : 0);
8903
+ const partCount = 3 + (rawPreview ? 1 : 0) + (duration ? 1 : 0);
8800
8904
  const gaps = (partCount - 1) * ROW_GAP;
8801
- let budget = scroll.width - timestamp.length - durationText.length - gaps;
8905
+ let budget = scroll.width - SELECTED_MARKER.length - timestamp.length - duration.length - gaps;
8802
8906
  const name = truncate3(entry.name, budget);
8803
8907
  budget -= name.length;
8804
8908
  const preview = rawPreview ? truncate3(rawPreview, budget) : "";
8805
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "row", gap: ROW_GAP, children: [
8806
- /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: timestamp }),
8807
- /* @__PURE__ */ jsx18(Text20, { color: logNameColor(entry), wrap: "truncate", children: name }),
8808
- preview && /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, wrap: "truncate", children: preview }),
8809
- durationText && /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: durationText })
8909
+ return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "row", gap: ROW_GAP, children: [
8910
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.success, children: selected ? SELECTED_MARKER : " " }),
8911
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: timestamp }),
8912
+ /* @__PURE__ */ jsx19(Text21, { color: logNameColor(entry), wrap: "truncate", children: name }),
8913
+ preview && /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, wrap: "truncate", children: preview }),
8914
+ duration && /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: duration })
8810
8915
  ] }, entry.id);
8811
8916
  }) }),
8812
- /* @__PURE__ */ jsxs18(Text20, { children: [
8813
- /* @__PURE__ */ jsx18(Text20, { color: COLORS.primary, children: "[j] [k]" }),
8814
- /* @__PURE__ */ jsx18(Text20, { color: COLORS.dim, children: " scroll" })
8917
+ /* @__PURE__ */ jsxs19(Box20, { gap: 2, children: [
8918
+ /* @__PURE__ */ jsxs19(Text21, { children: [
8919
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.primary, children: "[j] [k]" }),
8920
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: " select" })
8921
+ ] }),
8922
+ /* @__PURE__ */ jsxs19(Text21, { children: [
8923
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.primary, children: "[enter]" }),
8924
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: " details" })
8925
+ ] }),
8926
+ /* @__PURE__ */ jsxs19(Text21, { children: [
8927
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.primary, children: "[esc]" }),
8928
+ /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: " back" })
8929
+ ] })
8815
8930
  ] })
8816
- ] });
8931
+ ] }) });
8817
8932
  }
8818
8933
 
8819
8934
  // src/ui/Tips.tsx
8820
- import { useEffect as useEffect3, useRef as useRef4, useState as useState11 } from "react";
8821
- import { Box as Box21, Text as Text22 } from "ink";
8935
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState13 } from "react";
8936
+ import { Box as Box22, Text as Text23 } from "ink";
8822
8937
  import terminalLink from "terminal-link";
8823
8938
 
8824
8939
  // src/ui/Code.tsx
8825
- import { Box as Box20, Text as Text21 } from "ink";
8826
- import { jsx as jsx19 } from "react/jsx-runtime";
8940
+ import { Box as Box21, Text as Text22 } from "ink";
8941
+ import { jsx as jsx20 } from "react/jsx-runtime";
8827
8942
  var TOKEN_RE = /("(?:\\.|[^"\\])*"|\btrue\b|\bfalse\b|\bnull\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|[{}[\]:,])/g;
8828
8943
  function highlightJson(json) {
8829
8944
  const parts = json.split(TOKEN_RE);
@@ -8832,22 +8947,22 @@ function highlightJson(json) {
8832
8947
  if (part[0] === '"') {
8833
8948
  const next = parts.slice(i + 1).find((p) => p.trim());
8834
8949
  const isKey = next?.trimStart().startsWith(":");
8835
- return /* @__PURE__ */ jsx19(Text21, { color: isKey ? "cyan" : "green", children: part }, i);
8950
+ return /* @__PURE__ */ jsx20(Text22, { color: isKey ? "cyan" : "green", children: part }, i);
8836
8951
  }
8837
8952
  if (part === "true" || part === "false" || part === "null") {
8838
- return /* @__PURE__ */ jsx19(Text21, { color: "magenta", children: part }, i);
8953
+ return /* @__PURE__ */ jsx20(Text22, { color: "magenta", children: part }, i);
8839
8954
  }
8840
8955
  if (/^-?\d/.test(part)) {
8841
- return /* @__PURE__ */ jsx19(Text21, { color: "yellow", children: part }, i);
8956
+ return /* @__PURE__ */ jsx20(Text22, { color: "yellow", children: part }, i);
8842
8957
  }
8843
8958
  if (/^[{}[\]:,]$/.test(part)) {
8844
- return /* @__PURE__ */ jsx19(Text21, { color: COLORS.dim, children: part }, i);
8959
+ return /* @__PURE__ */ jsx20(Text22, { color: COLORS.dim, children: part }, i);
8845
8960
  }
8846
- return /* @__PURE__ */ jsx19(Text21, { children: part }, i);
8961
+ return /* @__PURE__ */ jsx20(Text22, { children: part }, i);
8847
8962
  });
8848
8963
  }
8849
8964
  function Code({ children }) {
8850
- return /* @__PURE__ */ jsx19(Box20, { children: /* @__PURE__ */ jsx19(Text21, { children: highlightJson(children) }) });
8965
+ return /* @__PURE__ */ jsx20(Box21, { children: /* @__PURE__ */ jsx20(Text22, { children: highlightJson(children) }) });
8851
8966
  }
8852
8967
 
8853
8968
  // src/ui/copy/tips.ts
@@ -9043,26 +9158,26 @@ var tips = [
9043
9158
  ];
9044
9159
 
9045
9160
  // src/ui/Tips.tsx
9046
- import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
9161
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
9047
9162
  var TICK_MS = 16;
9048
9163
  var CHUNK_HOLD_MS = 2e3;
9049
9164
  var TIP_DURATION_MS = 25e3;
9050
9165
  function TipTitle({ children }) {
9051
- return /* @__PURE__ */ jsxs19(Box21, { gap: 1, children: [
9052
- /* @__PURE__ */ jsx20(Text22, { color: "cyan", children: "\u2726" }),
9053
- /* @__PURE__ */ jsx20(Text22, { color: "white", bold: true, children })
9166
+ return /* @__PURE__ */ jsxs20(Box22, { gap: 1, children: [
9167
+ /* @__PURE__ */ jsx21(Text23, { color: "cyan", children: "\u2726" }),
9168
+ /* @__PURE__ */ jsx21(Text23, { color: "white", bold: true, children })
9054
9169
  ] });
9055
9170
  }
9056
9171
  function InlineSegment({ segment }) {
9057
9172
  switch (segment.type) {
9058
9173
  case "highlight":
9059
- return /* @__PURE__ */ jsx20(Text22, { color: COLORS.success, children: segment.value });
9174
+ return /* @__PURE__ */ jsx21(Text23, { color: COLORS.success, children: segment.value });
9060
9175
  case "link":
9061
- return /* @__PURE__ */ jsx20(Text22, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
9176
+ return /* @__PURE__ */ jsx21(Text23, { color: "cyan", underline: true, children: segment.href ? terminalLink(segment.value, segment.href) : segment.value });
9062
9177
  case "codeword":
9063
- return /* @__PURE__ */ jsx20(Text22, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
9178
+ return /* @__PURE__ */ jsx21(Text23, { color: COLORS.highlight.fg, backgroundColor: COLORS.highlight.bg, children: segment.value });
9064
9179
  default:
9065
- return /* @__PURE__ */ jsx20(Text22, { color: COLORS.muted, children: segment.value });
9180
+ return /* @__PURE__ */ jsx21(Text23, { color: COLORS.muted, children: segment.value });
9066
9181
  }
9067
9182
  }
9068
9183
  function TipContent({
@@ -9096,10 +9211,10 @@ function TipContent({
9096
9211
  blocks.push({ type: "inline", segments: [segment] });
9097
9212
  }
9098
9213
  }
9099
- return /* @__PURE__ */ jsx20(Box21, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
9100
- (block, i) => block.type === "code" ? /* @__PURE__ */ jsx20(Code, { children: block.segment.value }, i) : /* @__PURE__ */ jsx20(Text22, { children: block.segments.map((segment, j) => /* @__PURE__ */ jsxs19(Text22, { children: [
9214
+ return /* @__PURE__ */ jsx21(Box22, { flexDirection: "column", marginLeft: 2, gap: 1, children: blocks.map(
9215
+ (block, i) => block.type === "code" ? /* @__PURE__ */ jsx21(Code, { children: block.segment.value }, i) : /* @__PURE__ */ jsx21(Text23, { children: block.segments.map((segment, j) => /* @__PURE__ */ jsxs20(Text23, { children: [
9101
9216
  j > 0 && " ",
9102
- /* @__PURE__ */ jsx20(InlineSegment, { segment })
9217
+ /* @__PURE__ */ jsx21(InlineSegment, { segment })
9103
9218
  ] }, j)) }, i)
9104
9219
  ) });
9105
9220
  }
@@ -9129,7 +9244,7 @@ function Tips({
9129
9244
  onRevealStateChange,
9130
9245
  onComplete
9131
9246
  }) {
9132
- const [position, setPosition] = useState11(() => {
9247
+ const [position, setPosition] = useState13(() => {
9133
9248
  const now = Date.now();
9134
9249
  return {
9135
9250
  tipIndex: clock.tipIndex,
@@ -9189,9 +9304,9 @@ function Tips({
9189
9304
  if (position.complete) return null;
9190
9305
  const titleRevealed = tip.title.slice(0, revealed);
9191
9306
  const contentRevealed = Math.max(0, revealed - tip.title.length);
9192
- return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
9193
- /* @__PURE__ */ jsx20(TipTitle, { children: titleRevealed }),
9194
- /* @__PURE__ */ jsx20(TipContent, { chunks: tip.chunks, revealed: contentRevealed })
9307
+ return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", marginBottom: 1, gap: 1, children: [
9308
+ /* @__PURE__ */ jsx21(TipTitle, { children: titleRevealed }),
9309
+ /* @__PURE__ */ jsx21(TipContent, { chunks: tip.chunks, revealed: contentRevealed })
9195
9310
  ] });
9196
9311
  }
9197
9312
 
@@ -9233,7 +9348,7 @@ function useRepaintOnResize(debounceMs = 100) {
9233
9348
  }
9234
9349
 
9235
9350
  // src/ui/App.tsx
9236
- import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
9351
+ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
9237
9352
  function App() {
9238
9353
  const {
9239
9354
  phase,
@@ -9252,14 +9367,14 @@ function App() {
9252
9367
  const { columns, rows } = useWindowSize8();
9253
9368
  useRepaintOnResize();
9254
9369
  const compactRun = isCompactTerminal(columns, rows);
9255
- const [showLogs, setShowLogs] = useState12(false);
9256
- const [seenInputReq, setSeenInputReq] = useState12(inputReq);
9370
+ const [showLogs, setShowLogs] = useState14(false);
9371
+ const [seenInputReq, setSeenInputReq] = useState14(inputReq);
9257
9372
  if (inputReq !== seenInputReq) {
9258
9373
  setSeenInputReq(inputReq);
9259
9374
  if (inputReq) setShowLogs(false);
9260
9375
  }
9261
- const [tipState, setTipState] = useState12("idle");
9262
- const [tipsComplete, setTipsComplete] = useState12(false);
9376
+ const [tipState, setTipState] = useState14("idle");
9377
+ const [tipsComplete, setTipsComplete] = useState14(false);
9263
9378
  const finished = phase === "done" || phase === "error";
9264
9379
  const currentStep = steps[currentStepIndex];
9265
9380
  const workflowDef = workflow ? getWorkflow(workflow.id) : void 0;
@@ -9270,15 +9385,15 @@ function App() {
9270
9385
  const showTips = !compactRun && tipsActive && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || holdForTip);
9271
9386
  const showNoticesInMain = !tipsActive;
9272
9387
  const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2;
9273
- useInput9(
9388
+ useInput10(
9274
9389
  (_input, key) => {
9275
9390
  if (key.return) {
9276
9391
  exit();
9277
9392
  }
9278
9393
  },
9279
- { isActive: finished }
9394
+ { isActive: finished && !showLogs }
9280
9395
  );
9281
- useInput9((_input, key) => {
9396
+ useInput10((_input, key) => {
9282
9397
  if (phase === "idle" || phase === "authenticating") return;
9283
9398
  if (key.tab) {
9284
9399
  setShowLogs(!showLogs);
@@ -9289,8 +9404,9 @@ function App() {
9289
9404
  });
9290
9405
  }
9291
9406
  });
9292
- const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && !holdForTip && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
9293
- useInput9((_input, key) => {
9407
+ const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || // The log view owns esc: it closes the details, then leaves the list.
9408
+ showLogs || phase === "awaitingInput" && !holdForTip && (inputReq?.promptType === "enterToContinue" || inputReq?.promptType === "commandApproval");
9409
+ useInput10((_input, key) => {
9294
9410
  if (escOwnedElsewhere) return;
9295
9411
  if (key.escape) {
9296
9412
  track("AI Wizard Interaction", {
@@ -9308,8 +9424,8 @@ function App() {
9308
9424
  /* Clamped to exactly the viewport: a taller frame makes Ink clear and repaint
9309
9425
  the whole screen, and the scrolling throws off its cursor arithmetic.
9310
9426
  Flicker and leftover rows. */
9311
- /* @__PURE__ */ jsxs20(
9312
- Box22,
9427
+ /* @__PURE__ */ jsxs21(
9428
+ Box23,
9313
9429
  {
9314
9430
  backgroundColor: COLORS.bg.main,
9315
9431
  flexDirection: "row",
@@ -9317,16 +9433,16 @@ function App() {
9317
9433
  height: rows,
9318
9434
  overflow: "hidden",
9319
9435
  children: [
9320
- mainWindowVisible && /* @__PURE__ */ jsxs20(
9321
- Box22,
9436
+ mainWindowVisible && /* @__PURE__ */ jsxs21(
9437
+ Box23,
9322
9438
  {
9323
9439
  flexDirection,
9324
9440
  width: "100%",
9325
9441
  maxHeight: rows,
9326
9442
  justifyContent: "space-between",
9327
9443
  children: [
9328
- showLogs ? /* @__PURE__ */ jsx21(Logs, {}) : /* @__PURE__ */ jsxs20(
9329
- Box22,
9444
+ showLogs ? /* @__PURE__ */ jsx22(Logs, { onBack: () => setShowLogs(false) }) : /* @__PURE__ */ jsxs21(
9445
+ Box23,
9330
9446
  {
9331
9447
  flexDirection: "column",
9332
9448
  paddingX: 4,
@@ -9335,58 +9451,58 @@ function App() {
9335
9451
  flexGrow: 1,
9336
9452
  gap: 1,
9337
9453
  children: [
9338
- /* @__PURE__ */ jsxs20(Box22, { flexGrow: 2, flexDirection: "column", children: [
9339
- phase === "preflight" && !user && /* @__PURE__ */ jsx21(Box22, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text23, { color: COLORS.strong, bold: true, children: [
9340
- /* @__PURE__ */ jsx21(Spinner3, { type: "dots" }),
9454
+ /* @__PURE__ */ jsxs21(Box23, { flexGrow: 2, flexDirection: "column", children: [
9455
+ phase === "preflight" && !user && /* @__PURE__ */ jsx22(Box23, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs21(Text24, { color: COLORS.strong, bold: true, children: [
9456
+ /* @__PURE__ */ jsx22(Spinner3, { type: "dots" }),
9341
9457
  " Signing in to Algolia"
9342
9458
  ] }) }),
9343
- phase === "preflight" && user && /* @__PURE__ */ jsx21(Box22, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text23, { color: COLORS.strong, bold: true, children: [
9344
- /* @__PURE__ */ jsx21(Spinner3, { type: "dots" }),
9459
+ phase === "preflight" && user && /* @__PURE__ */ jsx22(Box23, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs21(Text24, { color: COLORS.strong, bold: true, children: [
9460
+ /* @__PURE__ */ jsx22(Spinner3, { type: "dots" }),
9345
9461
  " Getting things ready"
9346
9462
  ] }) }),
9347
- phase === "authenticating" && /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", marginBottom: 1, children: [
9348
- /* @__PURE__ */ jsxs20(Text23, { color: COLORS.strong, bold: true, children: [
9349
- /* @__PURE__ */ jsx21(Spinner3, { type: "dots" }),
9463
+ phase === "authenticating" && /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", marginBottom: 1, children: [
9464
+ /* @__PURE__ */ jsxs21(Text24, { color: COLORS.strong, bold: true, children: [
9465
+ /* @__PURE__ */ jsx22(Spinner3, { type: "dots" }),
9350
9466
  " Signing in to Algolia"
9351
9467
  ] }),
9352
- /* @__PURE__ */ jsx21(Text23, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
9468
+ /* @__PURE__ */ jsx22(Text24, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
9353
9469
  ] }),
9354
- settingUpAppId && /* @__PURE__ */ jsx21(Box22, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text23, { color: COLORS.strong, bold: true, children: [
9355
- /* @__PURE__ */ jsx21(Spinner3, { type: "dots" }),
9470
+ settingUpAppId && /* @__PURE__ */ jsx22(Box23, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs21(Text24, { color: COLORS.strong, bold: true, children: [
9471
+ /* @__PURE__ */ jsx22(Spinner3, { type: "dots" }),
9356
9472
  " Setting up Wizard using app",
9357
9473
  " ",
9358
9474
  settingUpAppId
9359
9475
  ] }) }),
9360
- phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx21(Box22, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text23, { color: COLORS.strong, bold: true, children: [
9361
- /* @__PURE__ */ jsx21(Spinner3, { type: "dots" }),
9476
+ phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx22(Box23, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs21(Text24, { color: COLORS.strong, bold: true, children: [
9477
+ /* @__PURE__ */ jsx22(Spinner3, { type: "dots" }),
9362
9478
  " ",
9363
9479
  loadingMessage
9364
9480
  ] }) }),
9365
- /* @__PURE__ */ jsx21(CliOutput, {}),
9366
- showTips && /* @__PURE__ */ jsx21(
9481
+ /* @__PURE__ */ jsx22(CliOutput, {}),
9482
+ showTips && /* @__PURE__ */ jsx22(
9367
9483
  Tips,
9368
9484
  {
9369
9485
  onRevealStateChange: setTipState,
9370
9486
  onComplete: () => setTipsComplete(true)
9371
9487
  }
9372
9488
  ),
9373
- showNoticesInMain && showNotices && /* @__PURE__ */ jsx21(Notices, { border: false }),
9374
- /* @__PURE__ */ jsx21(Review, {}),
9375
- !showTips && /* @__PURE__ */ jsx21(PromptInput, {}),
9376
- phase === "error" && error && /* @__PURE__ */ jsx21(Box22, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text23, { color: COLORS.status.error, children: [
9489
+ showNoticesInMain && showNotices && /* @__PURE__ */ jsx22(Notices, { border: false }),
9490
+ /* @__PURE__ */ jsx22(Review, {}),
9491
+ !showTips && /* @__PURE__ */ jsx22(PromptInput, {}),
9492
+ phase === "error" && error && /* @__PURE__ */ jsx22(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs21(Text24, { color: COLORS.status.error, children: [
9377
9493
  "\u2716 ",
9378
9494
  error
9379
9495
  ] }) })
9380
9496
  ] }),
9381
- !showNoticesInMain && showNotices && /* @__PURE__ */ jsx21(Notices, {})
9497
+ !showNoticesInMain && showNotices && /* @__PURE__ */ jsx22(Notices, {})
9382
9498
  ]
9383
9499
  }
9384
9500
  ),
9385
- showSidebar ? /* @__PURE__ */ jsx21(Sidebar, {}) : /* @__PURE__ */ jsx21(Ribbon, {})
9501
+ showSidebar ? /* @__PURE__ */ jsx22(Sidebar, { inLogs: showLogs }) : /* @__PURE__ */ jsx22(Ribbon, {})
9386
9502
  ]
9387
9503
  }
9388
9504
  ),
9389
- phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx21(LearnMore, {}) : /* @__PURE__ */ jsx21(Welcome, {}))
9505
+ phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx22(LearnMore, {}) : /* @__PURE__ */ jsx22(Welcome, {}))
9390
9506
  ]
9391
9507
  }
9392
9508
  )
@@ -9625,7 +9741,7 @@ async function resetProjectState() {
9625
9741
  // package.json with { type: 'json' }
9626
9742
  var package_default2 = {
9627
9743
  name: "@algolia/wizard",
9628
- version: "0.87.0-rc.211.681",
9744
+ version: "0.88.0",
9629
9745
  description: "Magically implement Algolia functionality in your codebase",
9630
9746
  type: "module",
9631
9747
  engines: {
@@ -9731,7 +9847,7 @@ async function checkVersion() {
9731
9847
  }
9732
9848
 
9733
9849
  // src/main.tsx
9734
- import { jsx as jsx22 } from "react/jsx-runtime";
9850
+ import { jsx as jsx23 } from "react/jsx-runtime";
9735
9851
  var FAILURE_EXIT_CODE = 1;
9736
9852
  async function exitProcessWithError(store, instance, message) {
9737
9853
  store.setError(message);
@@ -9794,7 +9910,7 @@ ${formatStepList(workflow)}`);
9794
9910
  async function run(opts) {
9795
9911
  const { workflow, skipResumePrompt } = opts;
9796
9912
  const store = useWizard.getState();
9797
- const instance = render(/* @__PURE__ */ jsx22(App, {}), { incrementalRendering: true });
9913
+ const instance = render(/* @__PURE__ */ jsx23(App, {}), { incrementalRendering: true });
9798
9914
  await store.waitForStart();
9799
9915
  store.syncSteps(
9800
9916
  workflow.steps.map((s) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.87.0-rc.211.681",
3
+ "version": "0.88.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {