@algolia/wizard 0.112.0 → 0.114.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 +885 -120
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1216,6 +1216,7 @@ import { useLayoutEffect as useLayoutEffect4, useRef as useRef3, useState as use
1216
1216
  import { jsx as jsx8, jsxs as jsxs9 } from "react/jsx-runtime";
1217
1217
  var CANCEL = "cancel";
1218
1218
  var ARROW_WIDTH = 4;
1219
+ var MULTI_TOGGLE_WIDTH = "[ ] ".length;
1219
1220
  var COLUMN_GAP = 2;
1220
1221
  var BAR_PADDING = 2;
1221
1222
  var ROW_HEIGHT = 3;
@@ -1229,6 +1230,7 @@ function nextSelectableIndex(start, dir, rowCount, isDisabled) {
1229
1230
  }
1230
1231
  function SelectPrompt({
1231
1232
  options,
1233
+ labels,
1232
1234
  onSelect,
1233
1235
  question,
1234
1236
  helpText,
@@ -1242,10 +1244,11 @@ function SelectPrompt({
1242
1244
  disabled,
1243
1245
  defaultSelectedIndex = 0
1244
1246
  }) {
1245
- const hasCancel = Boolean(multi || cancelable);
1247
+ const hasCancel = Boolean(cancelable);
1246
1248
  const rows = hasCancel ? [...options, "Cancel"] : options;
1247
1249
  const cancelIndex = hasCancel ? options.length : -1;
1248
1250
  const isDisabled = (i) => i !== cancelIndex && Boolean(disabled?.[i]);
1251
+ const displayLabel = (i) => i === cancelIndex ? "Cancel" : labels?.[i] ?? options[i];
1249
1252
  const [index, setIndex] = useState8(() => {
1250
1253
  const start = defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0;
1251
1254
  return isDisabled(start) ? nextSelectableIndex(start, 1, rows.length, isDisabled) : start;
@@ -1253,7 +1256,7 @@ function SelectPrompt({
1253
1256
  const [checked, setChecked] = useState8(() => /* @__PURE__ */ new Set());
1254
1257
  const hints = [];
1255
1258
  if (rows.length > 1) hints.push({ key: "[\u2191] [\u2193]", label: "move" });
1256
- if (multi) hints.push({ key: "[space]", label: "select" });
1259
+ if (multi) hints.push({ key: "[space]", label: "toggle select" });
1257
1260
  hints.push({ key: "[enter]", label: "confirm" });
1258
1261
  const containerRef = useRef3(null);
1259
1262
  const { columns } = useWindowSize4();
@@ -1265,7 +1268,7 @@ function SelectPrompt({
1265
1268
  });
1266
1269
  const inner = Math.max(width - BAR_PADDING, 0);
1267
1270
  const labelWidth = Math.min(
1268
- ARROW_WIDTH + (multi ? 2 : 0) + Math.max(0, ...rows.map((opt) => opt.length)) + COLUMN_GAP,
1271
+ ARROW_WIDTH + (multi ? MULTI_TOGGLE_WIDTH : 0) + Math.max(0, ...rows.map((_, i) => displayLabel(i).length)) + COLUMN_GAP,
1269
1272
  inner
1270
1273
  );
1271
1274
  const badgeWidth = Math.max(
@@ -1288,7 +1291,10 @@ function SelectPrompt({
1288
1291
  }, [index, revealIndex]);
1289
1292
  const visible = rows.slice(scroll.offset, scroll.offset + scroll.capacity);
1290
1293
  useInput5((input, key) => {
1291
- if (rows.length === 0) return;
1294
+ if (rows.length === 0) {
1295
+ if (multi && key.return) onSelect([]);
1296
+ return;
1297
+ }
1292
1298
  if (key.upArrow || input === "k") {
1293
1299
  setIndex((i) => nextSelectableIndex(i, -1, rows.length, isDisabled));
1294
1300
  } else if (key.downArrow || input === "j") {
@@ -1307,8 +1313,10 @@ function SelectPrompt({
1307
1313
  } else if (!multi) {
1308
1314
  onSelect(options[index]);
1309
1315
  } else {
1310
- const picked = options.filter((_, i) => checked.has(i) && !isDisabled(i));
1311
- if (picked.length > 0) onSelect(picked);
1316
+ const picked = options.filter(
1317
+ (_, i) => checked.has(i) && !isDisabled(i)
1318
+ );
1319
+ onSelect(picked);
1312
1320
  }
1313
1321
  }
1314
1322
  });
@@ -1340,7 +1348,7 @@ function SelectPrompt({
1340
1348
  const i = scroll.offset + visibleIndex;
1341
1349
  const highlighted = i === index;
1342
1350
  const isCancel = i === cancelIndex;
1343
- const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
1351
+ const bullet = multi && !isCancel ? checked.has(i) ? "[x] " : "[ ] " : "";
1344
1352
  const sec = isCancel ? void 0 : secondary?.[i];
1345
1353
  const isText = sec?.kind === "text";
1346
1354
  const rowDisabled = isDisabled(i);
@@ -1351,7 +1359,7 @@ function SelectPrompt({
1351
1359
  disabled: rowDisabled,
1352
1360
  width: isText ? "100%" : barWidth,
1353
1361
  labelWidth: isText ? labelWidth : barLabelWidth,
1354
- label: `${bullet}${option}`,
1362
+ label: `${bullet}${displayLabel(i)}`,
1355
1363
  usePadding: true,
1356
1364
  children: [
1357
1365
  isText && textWidth > 0 && /* @__PURE__ */ jsx8(Box9, { width: textWidth, children: /* @__PURE__ */ jsx8(
@@ -1432,6 +1440,7 @@ function PromptInput() {
1432
1440
  table: inputReq.table,
1433
1441
  list: inputReq.list,
1434
1442
  options: inputReq.options,
1443
+ labels: inputReq.labels,
1435
1444
  secondary: inputReq.secondary,
1436
1445
  disabled: inputReq.disabled,
1437
1446
  defaultSelectedIndex: inputReq.defaultSelectedIndex,
@@ -1453,7 +1462,10 @@ function PromptInput() {
1453
1462
  table: inputReq.table,
1454
1463
  list: inputReq.list,
1455
1464
  options: inputReq.options,
1465
+ labels: inputReq.labels,
1456
1466
  error: inputReq.error,
1467
+ secondary: inputReq.secondary,
1468
+ cancelable: inputReq.cancelable,
1457
1469
  onSelect: handleSubmit
1458
1470
  },
1459
1471
  promptKey
@@ -1464,6 +1476,7 @@ function PromptInput() {
1464
1476
  SelectPrompt,
1465
1477
  {
1466
1478
  messages: inputReq.messages,
1479
+ list: inputReq.list,
1467
1480
  options: ["Continue"],
1468
1481
  onSelect: handleSubmit
1469
1482
  },
@@ -2173,6 +2186,7 @@ function wizardErrorType(actionId, step, error) {
2173
2186
  }
2174
2187
  return "Generate UI";
2175
2188
  }
2189
+ if (actionId === "build-ui") return "playbook";
2176
2190
  return void 0;
2177
2191
  }
2178
2192
  function commonEventProperties() {
@@ -2375,7 +2389,13 @@ async function loadSelectableApps() {
2375
2389
  }
2376
2390
  async function pickApplication(opts) {
2377
2391
  const store = useWizard.getState();
2378
- const apps = await loadSelectableApps();
2392
+ store.setLoadingMessage("Loading applications\u2026");
2393
+ let apps;
2394
+ try {
2395
+ apps = await loadSelectableApps();
2396
+ } finally {
2397
+ store.setLoadingMessage(null);
2398
+ }
2379
2399
  const envHighlight = opts.highlight;
2380
2400
  const envApp = envHighlight ? apps.find((app) => app.id === envHighlight.id) : void 0;
2381
2401
  const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
@@ -2461,15 +2481,21 @@ async function promptToChangeApplication(currentId) {
2461
2481
  }
2462
2482
  async function ensureApplication(resumeAppId) {
2463
2483
  if (resumeAppId) return selectAndReport(resumeAppId);
2464
- const env = await findEnvApplicationId();
2465
- if (!env) return promptForApplication();
2466
- return promptForApplication({ id: env.id, file: env.file });
2484
+ const store = useWizard.getState();
2485
+ store.setLoadingMessage("Loading applications\u2026");
2486
+ try {
2487
+ const env = await findEnvApplicationId();
2488
+ if (!env) return await promptForApplication();
2489
+ return await promptForApplication({ id: env.id, file: env.file });
2490
+ } finally {
2491
+ store.setLoadingMessage(null);
2492
+ }
2467
2493
  }
2468
2494
 
2469
2495
  // package.json
2470
2496
  var package_default = {
2471
2497
  name: "@algolia/wizard",
2472
- version: "0.112.0",
2498
+ version: "0.114.0",
2473
2499
  description: "Magically implement Algolia functionality in your codebase",
2474
2500
  type: "module",
2475
2501
  engines: {
@@ -2684,7 +2710,7 @@ async function ensureConsent() {
2684
2710
  config.aiConsent = true;
2685
2711
  await saveConfig(config);
2686
2712
  }
2687
- async function makeContext(state) {
2713
+ async function makeContext(state, reentered) {
2688
2714
  const outputs = {};
2689
2715
  const completedSteps = [];
2690
2716
  for (const s of state.steps) {
@@ -2698,6 +2724,7 @@ async function makeContext(state) {
2698
2724
  return {
2699
2725
  outputs,
2700
2726
  completedSteps,
2727
+ reentered,
2701
2728
  getStepOutput: (stepId) => outputs[stepId],
2702
2729
  requestUserInput: (prompt) => useWizard.getState().requestUserInput(prompt),
2703
2730
  notify: (notice) => useWizard.getState().pushNotice(notice),
@@ -2752,6 +2779,7 @@ async function rewindTo(state, targetIndex, fromIndex) {
2752
2779
  async function runStep(state, index, step) {
2753
2780
  const store = useWizard.getState();
2754
2781
  const record = state.steps[index];
2782
+ const reentered = record.status === "running" || record.status === "error";
2755
2783
  const startedAt = Date.now();
2756
2784
  const appId = runAppId(state);
2757
2785
  trackActionStart({
@@ -2770,7 +2798,7 @@ async function runStep(state, index, step) {
2770
2798
  store.syncSteps([...state.steps], index);
2771
2799
  await persistState(state);
2772
2800
  await markInteraction();
2773
- const ctx = await makeContext(state);
2801
+ const ctx = await makeContext(state, reentered);
2774
2802
  const stopSavingCost = saveCostDuringStep(state);
2775
2803
  let output;
2776
2804
  try {
@@ -2818,6 +2846,9 @@ async function startFresh(workflow, appId) {
2818
2846
  return initWorkflowState(workflow, nowIso(), appId);
2819
2847
  }
2820
2848
  async function resolveWorkflowState(workflow, appId, options) {
2849
+ if (options?.resumableState === null) {
2850
+ return startFresh(workflow, appId);
2851
+ }
2821
2852
  const resumed = options?.resumableState !== void 0 ? options.resumableState : await loadResumableState(workflow);
2822
2853
  if (!resumed) {
2823
2854
  return initWorkflowState(workflow, nowIso(), appId);
@@ -6111,7 +6142,10 @@ var isSameFramework = (a, b) => {
6111
6142
  };
6112
6143
  function confirmed(name, version) {
6113
6144
  const frameworks = [{ name, version: version ?? "unknown" }];
6114
- track("AI Wizard Frontend Framework Confirmed", { frameworks, framework: name });
6145
+ track("AI Wizard Frontend Framework Confirmed", {
6146
+ frameworks,
6147
+ framework: name
6148
+ });
6115
6149
  return { frameworks };
6116
6150
  }
6117
6151
  async function askOtherFramework(ctx) {
@@ -6130,8 +6164,8 @@ async function askOtherFramework(ctx) {
6130
6164
  prompt = "please enter the framework used for your frontend:";
6131
6165
  }
6132
6166
  }
6133
- async function confirmFramework(ctx) {
6134
- const detected = ctx.getStepOutput("project-scan");
6167
+ async function confirmFramework(ctx, scan) {
6168
+ const detected = scan ?? ctx.getStepOutput("project-scan");
6135
6169
  const detectedFrameworks = detected.frameworks ?? [];
6136
6170
  const options = [...CURATED_FRAMEWORKS];
6137
6171
  for (const fw of detectedFrameworks) {
@@ -6421,6 +6455,7 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
6421
6455
  import z29 from "zod";
6422
6456
  import { access, mkdir as mkdir5, readdir as readdir3, readFile as readFile10 } from "node:fs/promises";
6423
6457
  import {
6458
+ basename as basename6,
6424
6459
  dirname as dirname5,
6425
6460
  extname,
6426
6461
  isAbsolute as isAbsolute4,
@@ -6594,6 +6629,17 @@ var implementSchema = z29.object({
6594
6629
  value: z29.string()
6595
6630
  })
6596
6631
  )
6632
+ }).optional(),
6633
+ uiReport: z29.object({
6634
+ files: z29.array(
6635
+ z29.object({
6636
+ label: z29.string(),
6637
+ path: z29.string()
6638
+ })
6639
+ ),
6640
+ checkPassed: z29.boolean(),
6641
+ typecheckCommand: z29.string().optional(),
6642
+ devCommand: z29.string().optional()
6597
6643
  }).optional()
6598
6644
  });
6599
6645
  var implementationOutputSchema = z29.object({
@@ -6602,14 +6648,17 @@ var implementationOutputSchema = z29.object({
6602
6648
  // Only for the search use case: the path of whatever module the agent
6603
6649
  // defined the Algolia config constants in, so the wizard can check it
6604
6650
  // won't end up gitignored (it's public, meant to be committed).
6605
- searchConfigFile: z29.string().optional()
6651
+ searchConfigFile: z29.string().optional(),
6652
+ uiFiles: z29.array(z29.object({ label: z29.string(), path: z29.string() })).optional(),
6653
+ devCommand: z29.string().optional()
6606
6654
  });
6607
6655
  var validationOutputSchema = z29.object({
6608
6656
  summary: z29.string(),
6609
6657
  sufficient: z29.boolean(),
6610
6658
  additionalInstructions: z29.string().optional(),
6611
6659
  unrelatedFailure: z29.string().optional(),
6612
- styleOnly: z29.boolean().optional()
6660
+ styleOnly: z29.boolean().optional(),
6661
+ typecheckCommand: z29.string().optional()
6613
6662
  });
6614
6663
  var MAX_IMPLEMENT_VALIDATION_ATTEMPTS = 3;
6615
6664
  var INGESTION_MAX_STEPS = 96;
@@ -6751,11 +6800,13 @@ function ingestionInstructions(input) {
6751
6800
  }
6752
6801
  function searchInstructions(input) {
6753
6802
  const indexedEntity = input.findings.confirmedEntities?.[0];
6754
- const indexedContentMessage = indexedEntity ? `Indexed content context: ${JSON.stringify({
6803
+ const templateMessage = input.uiTemplate ? `UI template: ${JSON.stringify(input.uiTemplate)}. Implement its layout, search placeholder, hit fields, filters, and sort controls. Use only the template's index attributes. Do not infer or invent record fields from the project.` : null;
6804
+ const indexedEntityMessage = indexedEntity ? `Indexed content context: ${JSON.stringify({
6755
6805
  entity: indexedEntity.name,
6756
6806
  attributes: indexedEntity.attributes,
6757
6807
  sourcePaths: indexedEntity.paths
6758
6808
  })}. Use the source and the existing UI that displays this entity to understand the search task, result content, and appropriate scope.` : null;
6809
+ const indexedContentMessage = templateMessage || indexedEntityMessage;
6759
6810
  const mountCandidateMessage = input.searchLocation ? `The confirmed-content placement analysis suggested "${input.searchLocation}" as a mount candidate. Before editing, read that file and inspect its parent layout, sibling controls, and the relevant content UI. If the evidence confirms it, import and render the new search component there; otherwise mount it in the better existing file. In all cases, mount the component before finishing; do not leave an unrendered component.` : "The scan found no reliable mount candidate. Determine the best existing location from the layout hierarchy and the UI that displays the indexed content, then mount the new search component there; do not default to a header or navigation component or leave an unrendered component.";
6760
6811
  const placementMessage = "Place content-specific search beside the heading or controls for the corresponding browse/listing task. Use shared navigation only for genuinely app-wide search and only when it fits the available space and responsive layout without crowding primary actions.";
6761
6812
  const replacementMessage = `${SEARCH_SCOPE_RULE} Replace the usage of each same-scope search control with the new component, keep its intentional placement, and delete the code that only it uses, for example its provider, dialog, and hooks. Keep differently scoped search, filters, and navigation.`;
@@ -6788,7 +6839,10 @@ function searchInstructions(input) {
6788
6839
  "Give the search input frame the same height, corner radius, border color and width, background, and focus style as the input it replaces. When it replaces nothing, copy them from the form controls beside it. Read these values from their source (the theme, the component props, or the CSS). Do not keep the widget theme defaults, for example a 44px height and 4px corners, and do not invent colors. When the app theme exists only in code, take the values from that theme object.",
6789
6840
  "When you add UI text to a localized dictionary or message file, add the same keys, translated, to every locale the project supports.",
6790
6841
  "Before reporting, mount the component. Do not run a type check, build, or test command unless a later instruction allows it: the validation pass runs the type check, after the developer agrees to it. If a tool limit is reached or an error remains unresolved, keep the implementation, report status=success, and name the remaining issue in the summary so validation can drive the next pass. Report status=fail only when no search implementation can be written, with the concrete blocker as the reason.",
6791
- "The summary should be extremely concise; do not mention manual testing steps."
6842
+ "The summary should be extremely concise; do not mention manual testing steps.",
6843
+ ...input.uiTemplate ? [
6844
+ "For the playbook recap, report uiFiles as every file you created or edited for this UI. Each entry needs a short action label and its repo-relative path. Report devCommand only when you found the project\u2019s existing command for starting the app locally. Do not invent a command, host, or port."
6845
+ ] : []
6792
6846
  ];
6793
6847
  }
6794
6848
  var SEARCH_SCOPE_RULE = "An existing search control has the same scope as the new search when its results include the indexed content, for example a site-wide search dialog or command palette. A search control that returns only other content, for example settings or help pages, has a different scope.";
@@ -6875,7 +6929,9 @@ The widget reads the item and noResults templates only from each indices entry.
6875
6929
  }
6876
6930
  function cdnSearchInstructions(input) {
6877
6931
  const entityName = input.findings.confirmedEntities?.[0]?.name;
6878
- const placeholder = entityName ? `Search ${entityName} records...` : "Search indexed content...";
6932
+ const templatePlaceholder = input.uiTemplate?.search_placeholder;
6933
+ const entityNamePlaceholder = entityName ? `Search ${entityName} records...` : "Search indexed content...";
6934
+ const placeholder = templatePlaceholder ?? entityNamePlaceholder;
6879
6935
  return [
6880
6936
  `The target frontend has no package.json. Add the search container to its existing HTML or template, create separate classic config and search scripts, define window.${SEARCH_CONFIG_APP_ID}, window.${SEARCH_CONFIG_SEARCH_KEY}, and window.${SEARCH_CONFIG_INDEX_NAME} in the config script, and report that script as "searchConfigFile". Never create package.json, create a component, use imports or exports, or run npm, pnpm, yarn, or bun.`,
6881
6937
  `Set window.${SEARCH_CONFIG_APP_ID} to "${input.appId}" and window.${SEARCH_CONFIG_INDEX_NAME} to "${input.targetIndex}".`,
@@ -6887,9 +6943,12 @@ function cdnSearchInstructions(input) {
6887
6943
  ];
6888
6944
  }
6889
6945
  function validationInstructions(input) {
6946
+ const templateMessage = input.uiTemplate ? `UI template: ${JSON.stringify(input.uiTemplate)}. Confirm the implementation uses its layout, search placeholder, hit fields, filters, and sort controls with the template's exact index attributes.` : null;
6947
+ const indexedEntityMessage = input.findings.confirmedEntities?.[0] ? `Indexed content context: ${JSON.stringify(input.findings.confirmedEntities?.[0] ?? null)}.` : null;
6948
+ const contentMessage = (templateMessage || indexedEntityMessage) ?? "";
6890
6949
  return [
6891
6950
  "Validate the Algolia search implementation by inspecting the project source.",
6892
- `Indexed content context: ${JSON.stringify(input.findings.confirmedEntities?.[0] ?? null)}.`,
6951
+ contentMessage,
6893
6952
  `The confirmed placement candidate was "${input.searchLocation ?? "unknown"}", but verify the actual mount rather than assuming the candidate was used.`,
6894
6953
  "Inspect the generated search component, its actual mount file, that file's parent layout and sibling controls, and the relevant content UI. Do not rely only on type checks or tests.",
6895
6954
  `Confirm that the application renders exactly one search control for the indexed content, and that the new component is not mounted twice. ${SEARCH_SCOPE_RULE} If a same-scope search control is still mounted, set sufficient=false and name the file that mounts it. Do not set sufficient=false for a differently scoped search control.`,
@@ -6920,6 +6979,9 @@ function validationInstructions(input) {
6920
6979
  "You can only read source files. If a required property can be checked in the source, check it there. If it can be established only in a browser (rendered contrast, keyboard behavior, 320-pixel reflow, live-region status, computed accessible name), set sufficient=true when the source shows no violation. Never request devtools, a screenshot, or a contrast tool, and never repeat a finding that an earlier pass already fixed.",
6921
6980
  "If a check fails only in untouched code, set unrelatedFailure to a concise failure description.",
6922
6981
  "Set styleOnly=true when sufficient=false and every finding is one of these: the theme palette defaults remain, a palette override loses to the theme, the highlight classes keep the theme blue, or the input frame does not match the size, radius, border, or background of the controls that it must match. If any other finding exists, for example a light panel on a dark page or a result that a click does not open, do not set styleOnly.",
6982
+ ...input.uiTemplate ? [
6983
+ "When you run a type check, report its exact command as typecheckCommand. Omit it when no type check ran."
6984
+ ] : [],
6923
6985
  "Keep the summary to one short sentence. Put the details in additionalInstructions."
6924
6986
  ];
6925
6987
  }
@@ -6947,13 +7009,7 @@ var useCaseToolMap = {
6947
7009
  "reviewScript",
6948
7010
  "notifyUser"
6949
7011
  ],
6950
- search: [
6951
- ...FS_READ_TOOLS,
6952
- "writeFile",
6953
- "editFile",
6954
- "runShell",
6955
- "notifyUser"
6956
- ],
7012
+ search: [...FS_READ_TOOLS, "writeFile", "editFile", "runShell", "notifyUser"],
6957
7013
  validation: [...FS_READ_TOOLS, "runShell", "notifyUser"]
6958
7014
  };
6959
7015
  function toolsForUseCase(useCase, ingestionSource) {
@@ -7106,7 +7162,10 @@ function ingestFailure(attempt, missingRunReason = "no successful run was record
7106
7162
  if (!attempt) return { reason: missingRunReason };
7107
7163
  if (!attempt.approved) return { reason: "you declined to run it" };
7108
7164
  if (ranButIndexedNothing(attempt)) {
7109
- return { reason: "it ran but indexed 0 records", detail: EMPTY_INGEST_DETAIL };
7165
+ return {
7166
+ reason: "it ran but indexed 0 records",
7167
+ detail: EMPTY_INGEST_DETAIL
7168
+ };
7110
7169
  }
7111
7170
  const detail = errorLine(attempt.output);
7112
7171
  if (attempt.timedOut) {
@@ -7286,22 +7345,19 @@ async function resolveIngestionSource(ctx, repoRoot) {
7286
7345
  logger.warn({ reason: copied.reason }, "implement: file upload unavailable");
7287
7346
  }
7288
7347
  }
7289
- async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7348
+ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, options = {}) {
7290
7349
  const repoRoot = process.cwd();
7291
- const scan = ctx.getStepOutput("project-scan");
7350
+ const scan = options.scan ?? ctx.getStepOutput("project-scan");
7292
7351
  const entities = ctx.getStepOutput(
7293
7352
  "confirm-entities"
7294
7353
  );
7295
7354
  const language = {
7296
7355
  languages: ctx.getStepOutput("confirm-language")?.languages ?? scan.languages,
7297
- frameworks: ctx.getStepOutput("confirm-framework")?.frameworks ?? scan.frameworks
7356
+ frameworks: options.frameworks ?? ctx.getStepOutput("confirm-framework")?.frameworks ?? scan.frameworks
7298
7357
  };
7299
7358
  const selected = ctx.getStepOutput(
7300
7359
  "select-index"
7301
7360
  );
7302
- if (!selected?.selection) {
7303
- throw new Error(`No index was selected`);
7304
- }
7305
7361
  await assertGitRepoWithHead(repoRoot);
7306
7362
  let ingestionSource = "generated";
7307
7363
  let uploadFilePath = void 0;
@@ -7312,7 +7368,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7312
7368
  repoRoot
7313
7369
  ));
7314
7370
  }
7315
- const targetIndex = selected?.selection;
7371
+ const targetIndex = options.targetIndex ?? selected?.selection;
7372
+ if (!targetIndex) {
7373
+ throw new Error(`No index was selected`);
7374
+ }
7316
7375
  useWizard.getState().setTargetIndex(targetIndex ?? null);
7317
7376
  if (useCases.includes("ingestion")) {
7318
7377
  await mkdir5(join11(repoRoot, INGEST_DIR), { recursive: true });
@@ -7380,7 +7439,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7380
7439
  frontendHasPackageJson: await frontendHasPackageJson(
7381
7440
  repoRoot,
7382
7441
  searchLocation
7383
- )
7442
+ ),
7443
+ uiTemplate: options.uiTemplate
7384
7444
  };
7385
7445
  let agentRuns = 0;
7386
7446
  let ingestCommand;
@@ -7449,7 +7509,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7449
7509
  tools: toolsForUseCase("validation"),
7450
7510
  outputSchema: validationOutputSchema,
7451
7511
  modelProfile: "validation" /* validation */,
7452
- toolContext: { ...makeToolContext(repoRoot), dependencyManifestsOnly: true }
7512
+ toolContext: {
7513
+ ...makeToolContext(repoRoot),
7514
+ dependencyManifestsOnly: true
7515
+ }
7453
7516
  });
7454
7517
  }
7455
7518
  if (useCases.includes("ingestion")) {
@@ -7667,6 +7730,10 @@ ${detail}` : ""}`
7667
7730
  }
7668
7731
  }
7669
7732
  let searchConfigFile;
7733
+ let uiFiles;
7734
+ let devCommand;
7735
+ let typecheckCommand;
7736
+ let checkPassed = false;
7670
7737
  if (useCases.includes("search")) {
7671
7738
  ingestionScript = await findIngestionScript(
7672
7739
  repoRoot,
@@ -7717,6 +7784,10 @@ ${detail}` : ""}`
7717
7784
  attempt > FIRST_IMPLEMENTATION_ATTEMPT
7718
7785
  );
7719
7786
  summaries.push(formatSummary("search", searchResult.summary));
7787
+ if (searchResult.uiFiles?.length) {
7788
+ uiFiles = searchResult.uiFiles;
7789
+ }
7790
+ devCommand ??= searchResult.devCommand;
7720
7791
  if (searchResult.searchConfigFile) {
7721
7792
  searchConfigFile = searchResult.searchConfigFile;
7722
7793
  }
@@ -7748,6 +7819,8 @@ ${detail}` : ""}`
7748
7819
  }
7749
7820
  const styleFindingsRemain = finalAttempt && !validation.sufficient && validation.styleOnly === true;
7750
7821
  if (validation.sufficient && (!validation.unrelatedFailure || finalAttempt) || styleFindingsRemain) {
7822
+ checkPassed = buildCheckPlan.run && validation.sufficient && !validation.unrelatedFailure;
7823
+ typecheckCommand = validation.typecheckCommand;
7751
7824
  if (validation.unrelatedFailure) {
7752
7825
  summaries.push("Validation: Unrelated check failures remain.");
7753
7826
  }
@@ -7819,6 +7892,19 @@ ${detail}` : ""}`
7819
7892
  { name: SEARCH_CONFIG_INDEX_NAME, value: targetIndex }
7820
7893
  ]
7821
7894
  }
7895
+ } : {},
7896
+ ...options.uiTemplate && useCases.includes("search") ? {
7897
+ uiReport: {
7898
+ files: uiFiles?.length ? uiFiles : [...new Set(useWizard.getState().writtenFiles)].map(
7899
+ (file) => ({
7900
+ label: "updated",
7901
+ path: relative7(repoRoot, file) || basename6(file)
7902
+ })
7903
+ ),
7904
+ checkPassed,
7905
+ ...typecheckCommand ? { typecheckCommand } : {},
7906
+ ...devCommand ? { devCommand } : {}
7907
+ }
7822
7908
  } : {}
7823
7909
  };
7824
7910
  }
@@ -8022,17 +8108,18 @@ var schemaComparisonEntrySchema = z32.object({
8022
8108
  status: z32.string(),
8023
8109
  mapping_confidence: z32.number()
8024
8110
  });
8025
- var settingsRecommendationSchema = z32.record(
8111
+ var settingRecommendationSchema = z32.object({
8112
+ current: z32.any(),
8113
+ recommended: z32.any()
8114
+ });
8115
+ var settingsRecommendationEntriesSchema = z32.record(
8026
8116
  z32.string(),
8027
- z32.object({
8028
- current: z32.any(),
8029
- recommended: z32.any()
8030
- })
8117
+ settingRecommendationSchema
8031
8118
  );
8032
8119
  var assessmentResultSchema = z32.object({
8033
8120
  ready: z32.boolean(),
8034
8121
  schema_comparison: z32.array(schemaComparisonEntrySchema),
8035
- settings_recommendation: settingsRecommendationSchema
8122
+ settings_recommendation: settingsRecommendationEntriesSchema
8036
8123
  });
8037
8124
  var playbookExecutionSchema = z32.object({
8038
8125
  uuid: z32.string(),
@@ -8051,6 +8138,18 @@ var playbookExecutionCreateResponseSchema = playbookExecutionSchema.extend({
8051
8138
  resumed: z32.boolean(),
8052
8139
  sample_data: z32.boolean()
8053
8140
  });
8141
+ var uiTemplateFieldSchema = z32.record(z32.string(), z32.unknown());
8142
+ var uiTemplateSchema = z32.looseObject({
8143
+ layout: z32.string(),
8144
+ record_label: z32.object({
8145
+ singular: z32.string(),
8146
+ plural: z32.string()
8147
+ }),
8148
+ search_placeholder: z32.string().optional(),
8149
+ hit: uiTemplateFieldSchema,
8150
+ filters: z32.array(uiTemplateFieldSchema),
8151
+ sort_by: z32.array(uiTemplateFieldSchema)
8152
+ });
8054
8153
 
8055
8154
  // src/lib/dashboardApi/api.ts
8056
8155
  async function getPlaybooks() {
@@ -8092,8 +8191,47 @@ async function getPlaybookExecution(uuid) {
8092
8191
  }
8093
8192
  async function assessPlaybookExecution(uuid) {
8094
8193
  const { data } = await dashboardRequest(
8095
- `/api/1/playbook-integrations/playbooks/executions/${uuid}/assess`,
8194
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}/start_data_assessment`,
8195
+ playbookExecutionSchema,
8196
+ { method: "POST" }
8197
+ );
8198
+ return data;
8199
+ }
8200
+ async function updatePlaybookStepStatus(uuid, stepStatus, statusReason) {
8201
+ const reason = statusReason?.trim();
8202
+ const { data } = await dashboardRequest(
8203
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}/update_step_status`,
8204
+ playbookExecutionSchema,
8205
+ {
8206
+ method: "PATCH",
8207
+ headers: { "content-type": "application/json" },
8208
+ body: JSON.stringify({
8209
+ step_status: stepStatus,
8210
+ ...stepStatus === "error" && reason ? { status_reason: reason } : {}
8211
+ })
8212
+ }
8213
+ );
8214
+ return data;
8215
+ }
8216
+ async function applyPlaybookExecutionSettings(uuid, settingsAccepted, settingsDeclined) {
8217
+ const { data } = await dashboardRequest(
8218
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}/apply_index_settings`,
8096
8219
  playbookExecutionSchema,
8220
+ {
8221
+ method: "POST",
8222
+ headers: { "content-type": "application/json" },
8223
+ body: JSON.stringify({
8224
+ settings_accepted: settingsAccepted,
8225
+ settings_declined: settingsDeclined
8226
+ })
8227
+ }
8228
+ );
8229
+ return data;
8230
+ }
8231
+ async function getPlaybookUiTemplate(uuid) {
8232
+ const { data } = await dashboardRequest(
8233
+ `/api/1/playbook-integrations/playbooks/executions/${uuid}/generate_ui_template`,
8234
+ uiTemplateSchema,
8097
8235
  { method: "POST" }
8098
8236
  );
8099
8237
  return data;
@@ -8113,6 +8251,13 @@ async function selectPlaybookStep(ctx, requestedSlug) {
8113
8251
  if (slugs.length === 0) {
8114
8252
  throw new Error("No playbooks are available.");
8115
8253
  }
8254
+ const nextPlaybookSlug = ctx.getUserInput("nextPlaybookSlug");
8255
+ if (nextPlaybookSlug) {
8256
+ ctx.setUserInput("nextPlaybookSlug", void 0);
8257
+ if (slugs.includes(nextPlaybookSlug)) {
8258
+ return persistSlug(ctx, nextPlaybookSlug);
8259
+ }
8260
+ }
8116
8261
  const alreadyChose = typeof ctx.getUserInput("playbookSlug") === "string";
8117
8262
  if (!alreadyChose && requestedSlug && slugs.includes(requestedSlug)) {
8118
8263
  return persistSlug(ctx, requestedSlug);
@@ -8151,7 +8296,22 @@ function formatIndexOption(index) {
8151
8296
  function isIndexSelectable(index) {
8152
8297
  return index.entries >= MIN_ENTRIES_FOR_SELECTION;
8153
8298
  }
8154
- async function selectIndexStep2(ctx) {
8299
+ function persistIndex(ctx, result) {
8300
+ ctx.setUserInput("playbookIndexName", result.indexName);
8301
+ ctx.setUserInput("playbookSampleIndex", result.sampleIndex);
8302
+ return result;
8303
+ }
8304
+ function alreadyChoseIndex(ctx) {
8305
+ return typeof ctx.getUserInput("playbookIndexName") === "string" || typeof ctx.getUserInput("playbookSampleIndex") === "boolean";
8306
+ }
8307
+ function requestedIndexError(indices, requestedIndexName) {
8308
+ const selectedIndex = indices.find((idx) => idx.name === requestedIndexName);
8309
+ if (!selectedIndex) {
8310
+ return `"${requestedIndexName}" is not a valid index. Choose one of the available indices.`;
8311
+ }
8312
+ return `"${requestedIndexName}" has no records. Choose an index with data.`;
8313
+ }
8314
+ async function selectIndexStep2(ctx, requestedIndexName) {
8155
8315
  let indices = ctx.getUserInput("fetchedIndices");
8156
8316
  !indices && ctx.notify({
8157
8317
  messages: ["Fetching indices from your Algolia application\u2026"]
@@ -8162,25 +8322,29 @@ async function selectIndexStep2(ctx) {
8162
8322
  ctx.setUserInput("fetchedIndices", indices);
8163
8323
  }
8164
8324
  const hasIndices = indices.length > 0;
8165
- const indexOptions = hasIndices ? indices.map(formatIndexOption) : [];
8166
- const options = [
8167
- ...indexOptions,
8168
- CREATE_SAMPLE_INDEX,
8169
- GO_BACK_OPTION,
8170
- SELECT_APP_OPTION
8325
+ const choices = [
8326
+ ...indices.map((index) => ({
8327
+ label: formatIndexOption(index),
8328
+ disabled: !isIndexSelectable(index)
8329
+ })),
8330
+ { label: CREATE_SAMPLE_INDEX, disabled: false },
8331
+ { label: GO_BACK_OPTION, disabled: false },
8332
+ { label: SELECT_APP_OPTION, disabled: false }
8171
8333
  ];
8172
- const disabled = hasIndices ? [
8173
- ...indices.map((idx) => !isIndexSelectable(idx)),
8174
- false,
8175
- // CREATE_SAMPLE_INDEX is selectable
8176
- false,
8177
- // GO_BACK_OPTION is selectable
8178
- false
8179
- // SELECT_APP_OPTION is selectable
8180
- ] : void 0;
8334
+ const options = choices.map((choice) => choice.label);
8335
+ const disabled = hasIndices ? choices.map((choice) => choice.disabled) : void 0;
8181
8336
  const prompt = hasIndices ? "Select an index:" : `${app.name} doesn't have any indices yet. Start from a sample data set
8182
8337
  you can explore or switch to another application`;
8183
- let error;
8338
+ if (!alreadyChoseIndex(ctx) && requestedIndexName) {
8339
+ const selectedIndex = indices.find((idx) => idx.name === requestedIndexName);
8340
+ if (selectedIndex && isIndexSelectable(selectedIndex)) {
8341
+ return persistIndex(ctx, {
8342
+ indexName: selectedIndex.name,
8343
+ sampleIndex: false
8344
+ });
8345
+ }
8346
+ }
8347
+ let error = !alreadyChoseIndex(ctx) && requestedIndexName ? requestedIndexError(indices, requestedIndexName) : void 0;
8184
8348
  let result;
8185
8349
  while (result === void 0) {
8186
8350
  const selection = await ctx.requestUserInput({
@@ -8198,6 +8362,8 @@ you can explore or switch to another application`;
8198
8362
  }
8199
8363
  if (selection === GO_BACK_OPTION) {
8200
8364
  ctx.setUserInput("fetchedIndices", void 0);
8365
+ ctx.setUserInput("playbookIndexName", void 0);
8366
+ ctx.setUserInput("playbookSampleIndex", void 0);
8201
8367
  return ctx.returnToStep("select-playbook");
8202
8368
  }
8203
8369
  if (selection === SELECT_APP_OPTION) {
@@ -8223,7 +8389,7 @@ you can explore or switch to another application`;
8223
8389
  sampleIndex: false
8224
8390
  };
8225
8391
  }
8226
- return result;
8392
+ return persistIndex(ctx, result);
8227
8393
  }
8228
8394
 
8229
8395
  // src/actions/playbookSteps/confirmIndex.ts
@@ -8247,7 +8413,7 @@ async function confirmIndexStep(ctx) {
8247
8413
  helpText: "Going back lets you choose a different index."
8248
8414
  });
8249
8415
  if (typeof selection !== "string") {
8250
- throw new Error("confirm-index received an unexpected result");
8416
+ throw new Error("confirm-sample-index received an unexpected result");
8251
8417
  }
8252
8418
  if (selection === GO_BACK_OPTION2) {
8253
8419
  return ctx.returnToStep("select-index");
@@ -8396,6 +8562,13 @@ var POLL_NOTICES = [
8396
8562
  async function dataAssessmentStep(ctx) {
8397
8563
  const { slug: slug2 } = ctx.getStepOutput("select-playbook");
8398
8564
  const { indexName, existingExecution } = indexForAssessment(ctx);
8565
+ const assessmentToRedisplay = ctx.getUserInput(
8566
+ "assessedPlaybookExecution"
8567
+ );
8568
+ if (assessmentToRedisplay?.source_index_name === indexName && assessmentToRedisplay.playbook_slug === slug2) {
8569
+ await promptReady(ctx, assessmentToRedisplay);
8570
+ return assessmentToRedisplay;
8571
+ }
8399
8572
  if (existingExecution) return existingExecution;
8400
8573
  try {
8401
8574
  showNotice(ctx, CREATING_EXECUTION_NOTICE);
@@ -8404,6 +8577,7 @@ async function dataAssessmentStep(ctx) {
8404
8577
  const assessed = await runAssessment(ctx, execution, indexName);
8405
8578
  ctx.clearNotices();
8406
8579
  await promptReady(ctx, assessed);
8580
+ ctx.setUserInput("assessedPlaybookExecution", assessed);
8407
8581
  return assessed;
8408
8582
  } finally {
8409
8583
  ctx.clearNotices();
@@ -8414,10 +8588,10 @@ function indexForAssessment(ctx) {
8414
8588
  if (!selected.sampleIndex) {
8415
8589
  return { indexName: selected.indexName, existingExecution: null };
8416
8590
  }
8417
- const created = ctx.getStepOutput("confirm-index");
8591
+ const created = ctx.getStepOutput("confirm-sample-index");
8418
8592
  if (!created) {
8419
8593
  throw new Error(
8420
- "data-assessment expected confirm-index to create a sample index"
8594
+ "data-assessment expected confirm-sample-index to create a sample index"
8421
8595
  );
8422
8596
  }
8423
8597
  return {
@@ -8445,36 +8619,24 @@ function startPollNotices(ctx) {
8445
8619
  return () => clearTimeout(timer);
8446
8620
  }
8447
8621
  function isNotReady(execution) {
8448
- return execution.step_status === "error" && execution.status_reason === "not_ready";
8622
+ return execution.step_status === "error" && !!execution.status_reason?.includes("not ready");
8449
8623
  }
8450
8624
  async function runAssessment(ctx, execution, indexName) {
8451
8625
  if (execution.step_status === "done") {
8452
8626
  return execution;
8453
8627
  }
8454
8628
  if (isNotReady(execution)) {
8455
- await reassessNotReady(
8456
- ctx,
8457
- execution,
8458
- indexName
8459
- );
8629
+ await reassessNotReady(ctx, execution, indexName);
8460
8630
  } else {
8461
8631
  await startAssessment(ctx, execution, indexName);
8462
8632
  }
8463
8633
  for (; ; ) {
8464
- const assessed = await pollUntilAssessed(
8465
- ctx,
8466
- execution.uuid,
8467
- indexName
8468
- );
8634
+ const assessed = await pollUntilAssessed(ctx, execution.uuid, indexName);
8469
8635
  if (assessed.step_status === "done") {
8470
8636
  return assessed;
8471
8637
  }
8472
8638
  if (isNotReady(assessed)) {
8473
- await reassessNotReady(
8474
- ctx,
8475
- assessed,
8476
- indexName
8477
- );
8639
+ await reassessNotReady(ctx, assessed, indexName);
8478
8640
  continue;
8479
8641
  }
8480
8642
  if (assessed.step_status !== "error") {
@@ -8603,9 +8765,474 @@ function assessmentFailureMessage(execution) {
8603
8765
  return execution.status_reason ?? "Data assessment failed.";
8604
8766
  }
8605
8767
 
8768
+ // src/actions/playbookSteps/settingsOptimizationPrompts.ts
8769
+ var SWITCH_APPLICATION_OPTION2 = "Switch application";
8770
+ var SWITCH_INDEX_OPTION2 = "Switch index";
8771
+ var RETRY_OPTION2 = "Retry";
8772
+ async function promptSettingsRecommendations(ctx, indexName, recommendations) {
8773
+ const selection = await ctx.requestUserInput({
8774
+ prompt: `recommended settings for ${indexName} \u2014 toggle to apply.`,
8775
+ promptType: "multiSelect",
8776
+ options: recommendations.map((recommendation) => recommendation.key),
8777
+ labels: recommendations.map((recommendation) => recommendation.label),
8778
+ secondary: recommendations.map((recommendation) => ({
8779
+ kind: "text",
8780
+ value: recommendation.value
8781
+ })),
8782
+ cancelable: true
8783
+ });
8784
+ if (!Array.isArray(selection) && selection !== "cancel") {
8785
+ throw new Error("recommendations received an unexpected selection");
8786
+ }
8787
+ return selection;
8788
+ }
8789
+ async function promptNothingSelected(ctx) {
8790
+ const choice = await ctx.requestUserInput({
8791
+ prompt: "Nothing was selected. Continue or go back to the previous step.",
8792
+ promptType: "multipleChoice",
8793
+ options: ["continue", "back"],
8794
+ labels: ["Continue", "Back"]
8795
+ });
8796
+ if (choice !== "continue" && choice !== "back") {
8797
+ throw new Error("settings applied received an unexpected result");
8798
+ }
8799
+ return choice;
8800
+ }
8801
+ async function promptSettingsApplied(ctx, accepted) {
8802
+ const count = accepted.length;
8803
+ const choice = await ctx.requestUserInput({
8804
+ prompt: `${count} ${count === 1 ? "index setting" : "index settings"} applied.`,
8805
+ promptType: "multipleChoice",
8806
+ labels: ["Continue"],
8807
+ options: ["continue"],
8808
+ list: accepted.map((recommendation) => ({
8809
+ label: recommendation.label,
8810
+ color: "success"
8811
+ }))
8812
+ });
8813
+ if (choice !== "continue") {
8814
+ throw new Error("settings applied received an unexpected result");
8815
+ }
8816
+ }
8817
+ async function promptNothingToOptimize(ctx, indexName, playbookSlug) {
8818
+ await ctx.requestUserInput({
8819
+ prompt: "Continue",
8820
+ promptType: "notice",
8821
+ options: [],
8822
+ list: [
8823
+ {
8824
+ label: `Your index ${indexName} is already optimized for ${playbookSlug} use case. No index settings changes required.`,
8825
+ color: "success"
8826
+ }
8827
+ ]
8828
+ });
8829
+ }
8830
+ async function promptOptimizationRecovery(ctx, message) {
8831
+ const choice = await ctx.requestUserInput({
8832
+ prompt: "Applying index settings failed.",
8833
+ promptType: "multipleChoice",
8834
+ options: [SWITCH_APPLICATION_OPTION2, SWITCH_INDEX_OPTION2, RETRY_OPTION2],
8835
+ messages: [message],
8836
+ helpText: "Retry applies the same recommendations. Switch index or application to pick a different target for playbook."
8837
+ });
8838
+ if (choice === SWITCH_APPLICATION_OPTION2) {
8839
+ return ctx.changeApplication();
8840
+ }
8841
+ if (choice === SWITCH_INDEX_OPTION2) {
8842
+ return ctx.returnToStep("select-index");
8843
+ }
8844
+ if (choice === RETRY_OPTION2) return;
8845
+ throw new Error("recommendations received an unexpected recovery choice");
8846
+ }
8847
+
8848
+ // src/actions/playbookSteps/settingsOptimization.ts
8849
+ var SETTING_LABELS = {
8850
+ searchableAttributes: "Searchable Attributes",
8851
+ customRanking: "Custom Ranking",
8852
+ attributesForFaceting: "Facets & Filters",
8853
+ filters: "Filters",
8854
+ geo: "Geolocation",
8855
+ unretrievableAttributes: "Unretrievable"
8856
+ };
8857
+ async function settingsOptimizationStep(ctx) {
8858
+ const execution = await requireExecution(ctx);
8859
+ const {
8860
+ uuid,
8861
+ current_step,
8862
+ step_status,
8863
+ source_index_name,
8864
+ playbook_slug,
8865
+ assessment_result
8866
+ } = execution;
8867
+ if (isOptimizationDone(execution)) return execution;
8868
+ if (!isOptimizationEligible(execution)) {
8869
+ throw new Error(
8870
+ `received an execution that cannot be optimized: ${current_step} ${step_status}`
8871
+ );
8872
+ }
8873
+ const settingsRecommendation = assessment_result?.settings_recommendation ?? {};
8874
+ const settingsToOptimize = formatSettingsToOptimize(settingsRecommendation);
8875
+ if (settingsToOptimize.length === 0) {
8876
+ await promptNothingToOptimize(ctx, source_index_name, playbook_slug);
8877
+ return await applyPlaybookExecutionSettings(uuid, [], []);
8878
+ }
8879
+ for (; ; ) {
8880
+ const accepted = await promptSettingsRecommendations(
8881
+ ctx,
8882
+ source_index_name,
8883
+ settingsToOptimize
8884
+ );
8885
+ if (accepted === "cancel") {
8886
+ return ctx.returnToStep("data-assessment");
8887
+ }
8888
+ if (accepted.length === 0) {
8889
+ const choice = await promptNothingSelected(ctx);
8890
+ if (choice === "back") continue;
8891
+ }
8892
+ ctx.notify({ messages: ["applying index settings\u2026"] });
8893
+ try {
8894
+ const declined = Object.keys(settingsRecommendation).filter(
8895
+ (group) => !accepted.includes(group)
8896
+ );
8897
+ const optimized = await applyWithRecovery(ctx, uuid, accepted, declined);
8898
+ ctx.clearNotices();
8899
+ const acceptedRecommendations = settingsToOptimize.filter(
8900
+ (setting) => accepted.includes(setting.key)
8901
+ );
8902
+ await promptSettingsApplied(ctx, acceptedRecommendations);
8903
+ return optimized;
8904
+ } finally {
8905
+ ctx.clearNotices();
8906
+ }
8907
+ }
8908
+ }
8909
+ async function requireExecution(ctx) {
8910
+ const saved = ctx.getStepOutput("data-assessment");
8911
+ if (!saved) {
8912
+ throw new Error("recommendations expected a completed data assessment");
8913
+ }
8914
+ if (!ctx.reentered || isOptimizationDone(saved)) return saved;
8915
+ return getPlaybookExecution(saved.uuid);
8916
+ }
8917
+ function isOptimizationDone(execution) {
8918
+ const isOptimizationDone2 = execution.current_step === "optimization" && execution.step_status === "done";
8919
+ const isOnImplementStep = execution.current_step === "implement";
8920
+ return isOptimizationDone2 || isOnImplementStep;
8921
+ }
8922
+ function isOptimizationEligible(execution) {
8923
+ const isAssessmentDone = execution.current_step === "assessment" && execution.step_status === "done";
8924
+ const isOptimizationInProgress = execution.current_step === "optimization" && execution.step_status !== "done";
8925
+ return isAssessmentDone || isOptimizationInProgress;
8926
+ }
8927
+ function formatSettingsToOptimize(recommendations) {
8928
+ return Object.entries(recommendations).filter(
8929
+ ([, setting]) => JSON.stringify(setting.current) !== JSON.stringify(setting.recommended)
8930
+ ).map(([key, setting]) => ({
8931
+ key,
8932
+ label: settingLabel(key),
8933
+ value: formatSettingValue(setting.recommended)
8934
+ }));
8935
+ }
8936
+ function settingLabel(key) {
8937
+ const label = SETTING_LABELS[key];
8938
+ if (label) return label;
8939
+ const [first, ...rest] = key.split(/(?=[A-Z])/);
8940
+ const capitalizedFirstWord = first.charAt(0).toUpperCase() + first.slice(1);
8941
+ return capitalizedFirstWord + " " + rest.join(" ");
8942
+ }
8943
+ function formatSettingValue(value) {
8944
+ if (value == null) return "not detected in this index";
8945
+ if (Array.isArray(value)) return value.length ? value.join(", ") : "none";
8946
+ if (typeof value === "object") return JSON.stringify(value);
8947
+ return String(value);
8948
+ }
8949
+ async function applyWithRecovery(ctx, uuid, accepted, declined) {
8950
+ return retryOnDashboardError(
8951
+ () => applyPlaybookExecutionSettings(uuid, accepted, declined),
8952
+ async (err) => promptOptimizationRecovery(ctx, requestFailureMessage2(err))
8953
+ );
8954
+ }
8955
+ function requestFailureMessage2(err) {
8956
+ const message = err.errorBody()?.message;
8957
+ return typeof message === "string" && message.trim() ? message : "Couldn't apply index settings.";
8958
+ }
8959
+
8960
+ // src/actions/playbookSteps/generateUi.ts
8961
+ import z36 from "zod";
8962
+
8963
+ // src/actions/playbookSteps/generateUiPrompts.ts
8964
+ var SWITCH_APPLICATION_OPTION3 = "Switch application";
8965
+ var SWITCH_INDEX_OPTION3 = "Switch index";
8966
+ var RETRY_OPTION3 = "Retry";
8967
+ async function promptUiGenerated(ctx, report) {
8968
+ const { files, checkPassed, devCommand } = report ?? {};
8969
+ const list = [];
8970
+ if (files) {
8971
+ const editedFiles = files.map((file) => ({
8972
+ label: file.label,
8973
+ helpText: file.path,
8974
+ color: "success"
8975
+ }));
8976
+ list.push(...editedFiles);
8977
+ }
8978
+ if (checkPassed) {
8979
+ list.push({ label: "build & typecheck passed", color: "success" });
8980
+ }
8981
+ list.push({
8982
+ label: "playbook UI completed",
8983
+ color: "success"
8984
+ });
8985
+ if (devCommand) {
8986
+ list.push({
8987
+ label: "view it locally",
8988
+ helpText: devCommand,
8989
+ color: "success"
8990
+ });
8991
+ }
8992
+ await ctx.requestUserInput({
8993
+ prompt: "Continue",
8994
+ promptType: "notice",
8995
+ options: [],
8996
+ messages: ["implemented the playbook UI"],
8997
+ list
8998
+ });
8999
+ }
9000
+ async function promptUiRecovery(ctx, message) {
9001
+ const choice = await ctx.requestUserInput({
9002
+ prompt: "Generating the playbook UI failed.",
9003
+ promptType: "multipleChoice",
9004
+ options: [SWITCH_APPLICATION_OPTION3, SWITCH_INDEX_OPTION3, RETRY_OPTION3],
9005
+ messages: [message],
9006
+ helpText: "Retry repeats the failed request. Switch index or application to pick a different target for playbook."
9007
+ });
9008
+ if (choice === SWITCH_APPLICATION_OPTION3) {
9009
+ return ctx.changeApplication();
9010
+ }
9011
+ if (choice === SWITCH_INDEX_OPTION3) {
9012
+ return ctx.returnToStep("select-index");
9013
+ }
9014
+ if (choice === RETRY_OPTION3) return;
9015
+ throw new Error("generate UI received an unexpected recovery choice");
9016
+ }
9017
+
9018
+ // src/actions/playbookSteps/generateUi.ts
9019
+ var generatePlaybookUiSchema = z36.object({
9020
+ summary: z36.string(),
9021
+ framework: z36.string().optional(),
9022
+ layoutName: z36.string().optional()
9023
+ });
9024
+ async function generatePlaybookUiStep(ctx) {
9025
+ const execution = await requireExecution2(ctx);
9026
+ const { uuid, current_step, step_status, source_index_name, playbook_slug } = execution;
9027
+ if (isUiDone(execution)) {
9028
+ return {
9029
+ summary: "Playbook UI already generated.",
9030
+ framework: void 0,
9031
+ layoutName: void 0
9032
+ };
9033
+ }
9034
+ if (!isUiEligible(execution)) {
9035
+ throw new Error(
9036
+ `received an execution that cannot generate a UI: ${current_step} ${step_status}`
9037
+ );
9038
+ }
9039
+ ctx.notify({
9040
+ messages: ["Reviewing your codebase\u2026"]
9041
+ });
9042
+ const template = await loadTemplateWithRecovery(ctx, uuid);
9043
+ const startedAt = Date.now();
9044
+ try {
9045
+ const scan = await projectScan(ctx);
9046
+ const confirmed2 = await confirmFramework(ctx, scan);
9047
+ const framework = confirmed2.frameworks[0]?.name;
9048
+ if (!framework) {
9049
+ throw new Error("No frontend framework was confirmed");
9050
+ }
9051
+ ctx.notify({
9052
+ messages: ["Generating UI from the playbook template\u2026"]
9053
+ });
9054
+ const result = await implement(ctx, ["search"], {
9055
+ targetIndex: source_index_name,
9056
+ uiTemplate: template,
9057
+ scan,
9058
+ frameworks: confirmed2.frameworks
9059
+ });
9060
+ await markDoneWithRecovery(ctx, uuid);
9061
+ track("Playbook UI Layout Completed", {
9062
+ playbook_name: playbook_slug,
9063
+ index_name: source_index_name,
9064
+ framework,
9065
+ duration_ms: Date.now() - startedAt,
9066
+ // TBD: should this track only implementation time or implementation + template loading + scan time?
9067
+ layout_name: template.layout
9068
+ });
9069
+ await promptUiGenerated(ctx, result.uiReport);
9070
+ return {
9071
+ summary: result.summary,
9072
+ framework,
9073
+ layoutName: template.layout
9074
+ };
9075
+ } catch (err) {
9076
+ await markPlaybookUiStepFailed(execution, err);
9077
+ throw err;
9078
+ } finally {
9079
+ ctx.clearNotices();
9080
+ }
9081
+ }
9082
+ async function requireExecution2(ctx) {
9083
+ const saved = ctx.getStepOutput("recommendations");
9084
+ if (!saved) {
9085
+ throw new Error("generate UI expected a completed settings optimization");
9086
+ }
9087
+ if (!ctx.reentered) return saved;
9088
+ return getPlaybookExecution(saved.uuid);
9089
+ }
9090
+ function isUiDone(execution) {
9091
+ const { current_step, step_status } = execution;
9092
+ return current_step === "implement" && step_status === "done";
9093
+ }
9094
+ function isUiEligible(execution) {
9095
+ const { current_step, step_status } = execution;
9096
+ return current_step === "optimization" && step_status === "done" || current_step === "implement" && step_status !== "done";
9097
+ }
9098
+ async function loadTemplateWithRecovery(ctx, uuid) {
9099
+ const request = () => getPlaybookUiTemplate(uuid);
9100
+ const requestRecoveryPrompt = (err) => promptUiRecovery(
9101
+ ctx,
9102
+ requestFailureMessage3(err, "Couldn't load the playbook UI template.")
9103
+ );
9104
+ return retryOnDashboardError(request, requestRecoveryPrompt);
9105
+ }
9106
+ async function markDoneWithRecovery(ctx, uuid) {
9107
+ const request = () => updatePlaybookStepStatus(uuid, "done");
9108
+ const requestRecoveryPrompt = (err) => promptUiRecovery(
9109
+ ctx,
9110
+ requestFailureMessage3(err, "Couldn't update the playbook UI step.")
9111
+ );
9112
+ await retryOnDashboardError(request, requestRecoveryPrompt);
9113
+ }
9114
+ async function markPlaybookUiStepFailed(execution, err) {
9115
+ const reason = err instanceof Error ? err.message : String(err);
9116
+ try {
9117
+ await updatePlaybookStepStatus(execution.uuid, "error", reason);
9118
+ } catch (statusErr) {
9119
+ logger.warn(
9120
+ { err: statusErr, executionUuid: execution.uuid },
9121
+ "failed to update playbook UI step status"
9122
+ );
9123
+ throw statusErr;
9124
+ }
9125
+ }
9126
+ function requestFailureMessage3(err, fallback) {
9127
+ const message = err.errorBody()?.message;
9128
+ return typeof message === "string" && message.trim() ? message : fallback;
9129
+ }
9130
+
9131
+ // src/actions/playbookSteps/summary.ts
9132
+ import { z as z37 } from "zod";
9133
+ var playbookSummarySchema = z37.object({
9134
+ slug: z37.string(),
9135
+ applicationId: z37.string(),
9136
+ indexName: z37.string(),
9137
+ writtenFileCount: z37.number().int().nonnegative()
9138
+ });
9139
+ async function playbookSummaryStep(ctx) {
9140
+ const { slug: slug2 } = ctx.getStepOutput("select-playbook");
9141
+ const execution = ctx.getStepOutput("data-assessment");
9142
+ ctx.notify({
9143
+ messages: ["Summary of the playbook execution"]
9144
+ });
9145
+ const { application_id, source_index_name } = execution;
9146
+ const writtenFileCount = new Set(useWizard.getState().writtenFiles).size;
9147
+ const nextPlaybooks = await getNextPlaybooks(slug2);
9148
+ if (nextPlaybooks.length === 0) {
9149
+ await emptyNextPlaybooksPrompt(
9150
+ ctx,
9151
+ slug2,
9152
+ application_id,
9153
+ source_index_name,
9154
+ writtenFileCount
9155
+ );
9156
+ return {
9157
+ slug: slug2,
9158
+ applicationId: application_id,
9159
+ indexName: source_index_name,
9160
+ writtenFileCount
9161
+ };
9162
+ }
9163
+ const next = await nextPlaybooksPrompt(
9164
+ ctx,
9165
+ slug2,
9166
+ application_id,
9167
+ source_index_name,
9168
+ writtenFileCount,
9169
+ nextPlaybooks
9170
+ );
9171
+ if (typeof next !== "string") {
9172
+ throw new Error("playbookSummary received an unexpected non-text result");
9173
+ }
9174
+ ctx.setUserInput("nextPlaybookSlug", next);
9175
+ ctx.setUserInput("assessedPlaybookExecution", void 0);
9176
+ useWizard.getState().clearWrittenFiles();
9177
+ ctx.returnToStep("select-playbook");
9178
+ }
9179
+ async function getNextPlaybooks(selectedPlaybookSlug) {
9180
+ try {
9181
+ const playbooks = await getPlaybooks();
9182
+ return playbooks.filter(
9183
+ (playbook) => playbook.slug !== selectedPlaybookSlug
9184
+ );
9185
+ } catch (err) {
9186
+ logger.warn({ err }, "failed to get next playbooks");
9187
+ return [];
9188
+ }
9189
+ }
9190
+ function emptyNextPlaybooksPrompt(ctx, playbookSelectedSlug, applicationId, indexName, writtenFileCount) {
9191
+ return ctx.requestUserInput({
9192
+ prompt: "Continue",
9193
+ promptType: "notice",
9194
+ options: [],
9195
+ list: [
9196
+ {
9197
+ label: `${playbookSelectedSlug} playbook complete`,
9198
+ color: "success"
9199
+ }
9200
+ ],
9201
+ messages: [
9202
+ `app ${applicationId}`,
9203
+ `index ${indexName}`,
9204
+ `files ${writtenFileCount} written`
9205
+ ]
9206
+ });
9207
+ }
9208
+ function nextPlaybooksPrompt(ctx, playbookSelectedSlug, applicationId, indexName, writtenFileCount, nextPlaybooks) {
9209
+ const options = nextPlaybooks.map((playbook) => playbook.slug);
9210
+ const secondary = nextPlaybooks.map((playbook) => ({
9211
+ kind: "text",
9212
+ value: `\xB7 ${playbook.description ?? playbook.title}`
9213
+ }));
9214
+ return ctx.requestUserInput({
9215
+ prompt: "Choose another playbook to run:",
9216
+ promptType: "multipleChoice",
9217
+ options,
9218
+ secondary,
9219
+ list: [
9220
+ {
9221
+ label: `${playbookSelectedSlug} playbook complete`,
9222
+ color: "success"
9223
+ }
9224
+ ],
9225
+ messages: [
9226
+ `app ${applicationId}`,
9227
+ `index ${indexName}`,
9228
+ `files ${writtenFileCount} written`
9229
+ ]
9230
+ });
9231
+ }
9232
+
8606
9233
  // src/lib/cli.ts
8607
- var USAGE = `Usage: wizard [workflow-id] [options]
8608
- wizard playbook [playbook-slug] [options]
9234
+ var USAGE = `Usage: wizard [workflow-id] [--application <id>] [options]
9235
+ wizard playbook [playbook-slug] [--application <id>] [--index <name>] [options]
8609
9236
 
8610
9237
  Options:
8611
9238
  --seed <step-id> Start the workflow at the step with this id, with earlier
@@ -8619,6 +9246,10 @@ Options:
8619
9246
  the encrypted file it falls back to \u2014 see CONTRIBUTING.md),
8620
9247
  for this project and any other, so later runs create new
8621
9248
  ones. Your Algolia login is not touched.
9249
+ --application <id>
9250
+ Select this Algolia application without prompting.
9251
+ --index <name> Playbook only. Use this index without prompting. Requires
9252
+ --application.
8622
9253
  -h, --help Print this message.`;
8623
9254
  function parseCliArgs(argv) {
8624
9255
  const positionals = [];
@@ -8626,37 +9257,70 @@ function parseCliArgs(argv) {
8626
9257
  let telemetry = true;
8627
9258
  let resetOnRun = false;
8628
9259
  let help = false;
9260
+ let applicationId;
9261
+ let indexName;
8629
9262
  for (let i = 0; i < argv.length; i++) {
8630
9263
  const arg = argv[i];
8631
- if (arg === "--seed") {
8632
- const next = argv[i + 1];
8633
- if (next !== void 0 && !next.startsWith("-")) {
8634
- seed = next;
9264
+ const next = argv[i + 1];
9265
+ switch (arg) {
9266
+ case "--seed":
9267
+ if (next !== void 0 && !next.startsWith("-")) {
9268
+ seed = next;
9269
+ i++;
9270
+ } else {
9271
+ seed = null;
9272
+ }
9273
+ break;
9274
+ case "--no-telemetry":
9275
+ telemetry = false;
9276
+ break;
9277
+ case "--reset-on-run":
9278
+ resetOnRun = true;
9279
+ break;
9280
+ case "--application":
9281
+ applicationId = requireValue(arg, next);
8635
9282
  i++;
8636
- } else {
8637
- seed = null;
8638
- }
8639
- } else if (arg.startsWith("--seed=")) {
8640
- seed = arg.slice("--seed=".length);
8641
- } else if (arg === "--no-telemetry") {
8642
- telemetry = false;
8643
- } else if (arg === "--reset-on-run") {
8644
- resetOnRun = true;
8645
- } else if (arg === "--help" || arg === "-h") {
8646
- help = true;
8647
- } else if (arg.startsWith("-")) {
8648
- throw new Error(`Unknown option "${arg}".`);
8649
- } else {
8650
- positionals.push(arg);
9283
+ break;
9284
+ case "--index":
9285
+ indexName = requireValue(arg, next);
9286
+ i++;
9287
+ break;
9288
+ case "--help":
9289
+ case "-h":
9290
+ help = true;
9291
+ break;
9292
+ default:
9293
+ if (arg.startsWith("-")) throw new Error(`Unknown option "${arg}".`);
9294
+ positionals.push(arg);
8651
9295
  }
8652
9296
  }
8653
- return { positionals, seed, telemetry, resetOnRun, help };
9297
+ return { positionals, seed, telemetry, resetOnRun, help, applicationId, indexName };
9298
+ }
9299
+ function requireValue(flag, value) {
9300
+ if (value === void 0) {
9301
+ throw new Error(`${flag} requires a value.`);
9302
+ }
9303
+ return value;
8654
9304
  }
8655
9305
  function getPlaybookSlugArg(argv = process.argv.slice(2)) {
8656
9306
  const { positionals } = parseCliArgs(argv);
8657
9307
  if (positionals[0] !== "playbook") return void 0;
8658
9308
  return positionals[1];
8659
9309
  }
9310
+ function getPlaybookIndexNameArg(argv = process.argv.slice(2)) {
9311
+ const { positionals, indexName } = parseCliArgs(argv);
9312
+ if (positionals[0] !== "playbook") return void 0;
9313
+ return indexName;
9314
+ }
9315
+ function cliArgsError(args, workflowId) {
9316
+ if (args.indexName !== void 0 && workflowId !== "playbook") {
9317
+ return "--index can only be used with the playbook workflow.";
9318
+ }
9319
+ if (args.indexName !== void 0 && args.applicationId === void 0) {
9320
+ return "--index requires --application.";
9321
+ }
9322
+ return void 0;
9323
+ }
8660
9324
 
8661
9325
  // src/workflows/playbook.ts
8662
9326
  var playbookWorkflow = {
@@ -8667,27 +9331,46 @@ var playbookWorkflow = {
8667
9331
  defineStep({
8668
9332
  id: "select-playbook",
8669
9333
  title: "Select playbook",
9334
+ visible: false,
8670
9335
  outputSchema: selectPlaybookSchema,
8671
9336
  run: (ctx) => selectPlaybookStep(ctx, getPlaybookSlugArg())
8672
9337
  }),
8673
9338
  defineStep({
8674
9339
  id: "select-index",
8675
- title: "Select index",
9340
+ title: "entity select",
8676
9341
  outputSchema: selectIndexSchema,
8677
- run: (ctx) => selectIndexStep2(ctx)
9342
+ run: (ctx) => selectIndexStep2(ctx, getPlaybookIndexNameArg())
8678
9343
  }),
8679
9344
  defineStep({
8680
- id: "confirm-index",
8681
- title: "Confirm sample index creation",
9345
+ id: "confirm-sample-index",
9346
+ title: "confirm sample index",
8682
9347
  outputSchema: confirmIndexSchema,
8683
9348
  visible: false,
8684
9349
  run: (ctx) => confirmIndexStep(ctx)
8685
9350
  }),
8686
9351
  defineStep({
8687
9352
  id: "data-assessment",
8688
- title: "Assess index data",
9353
+ title: "data assessment",
8689
9354
  outputSchema: playbookExecutionSchema,
8690
9355
  run: (ctx) => dataAssessmentStep(ctx)
9356
+ }),
9357
+ defineStep({
9358
+ id: "recommendations",
9359
+ title: "recommendations",
9360
+ outputSchema: playbookExecutionSchema,
9361
+ run: (ctx) => settingsOptimizationStep(ctx)
9362
+ }),
9363
+ defineStep({
9364
+ id: "build-ui",
9365
+ title: "build ui",
9366
+ outputSchema: generatePlaybookUiSchema,
9367
+ run: (ctx) => generatePlaybookUiStep(ctx)
9368
+ }),
9369
+ defineStep({
9370
+ id: "done",
9371
+ title: "done",
9372
+ outputSchema: playbookSummarySchema,
9373
+ run: (ctx) => playbookSummaryStep(ctx)
8691
9374
  })
8692
9375
  ]
8693
9376
  };
@@ -10046,6 +10729,11 @@ var dataAssessment = {
10046
10729
  settings_snapshot: null,
10047
10730
  created_at: "2026-09-22T09:00:00.000Z"
10048
10731
  };
10732
+ var generatePlaybookUi = {
10733
+ summary: "Generated an order component from the playbook UI template.",
10734
+ framework: "Next.js",
10735
+ layoutName: "list layout"
10736
+ };
10049
10737
  var review = {
10050
10738
  summaryPoints: [
10051
10739
  "Ingested 25 generated Product records into wizard_seed_products.",
@@ -10068,8 +10756,20 @@ var SEEDS = {
10068
10756
  playbook: {
10069
10757
  "select-playbook": selectPlaybook,
10070
10758
  "select-index": selectPlaybookIndex,
10071
- "confirm-index": null,
10072
- "data-assessment": dataAssessment
10759
+ "confirm-sample-index": null,
10760
+ "data-assessment": dataAssessment,
10761
+ recommendations: {
10762
+ ...dataAssessment,
10763
+ current_step: "optimization"
10764
+ },
10765
+ "build-ui": generatePlaybookUi,
10766
+ done: {
10767
+ slug: "orders",
10768
+ applicationId: "SEEDAPPID",
10769
+ indexName: "wizard_seed_orders",
10770
+ writtenFileCount: 0,
10771
+ nextPlaybooks: []
10772
+ }
10073
10773
  }
10074
10774
  };
10075
10775
  var SEED_ALGOLIA_STATE = {
@@ -10198,10 +10898,53 @@ async function resetProjectState() {
10198
10898
  return { dir, removed: targets };
10199
10899
  }
10200
10900
 
10901
+ // src/lib/resumeSession.ts
10902
+ function formatRunTarget(target) {
10903
+ return [target.playbookSlug, target.applicationId, target.indexName].filter((part) => Boolean(part)).join(" \u2022 ");
10904
+ }
10905
+ function stringField(value) {
10906
+ return typeof value === "string" && value ? value : void 0;
10907
+ }
10908
+ function stepOutput2(state, stepId) {
10909
+ const output = state.steps.find((step) => step.id === stepId)?.output;
10910
+ return output && typeof output === "object" ? output : void 0;
10911
+ }
10912
+ function sessionRunTarget(state) {
10913
+ const applicationId = runAppId(state);
10914
+ if (state.workflowId !== "playbook") {
10915
+ return { applicationId };
10916
+ }
10917
+ const playbook = stepOutput2(state, "select-playbook");
10918
+ const index = stepOutput2(state, "select-index");
10919
+ return {
10920
+ applicationId,
10921
+ playbookSlug: stringField(state.userInputs.playbookSlug) ?? stringField(playbook?.slug),
10922
+ indexName: stringField(state.userInputs.playbookIndexName) ?? stringField(index?.indexName)
10923
+ };
10924
+ }
10925
+ function resumeOptionLabel(session) {
10926
+ const label = formatRunTarget(session);
10927
+ return label ? `Resume (${label})` : "Resume";
10928
+ }
10929
+ function startNewOptionLabel(next) {
10930
+ const label = formatRunTarget(next);
10931
+ return label ? `Start new (${label})` : "Start new";
10932
+ }
10933
+ async function promptResumeOrStartNew(session, next) {
10934
+ const resumeOption = resumeOptionLabel(session);
10935
+ const startOption = startNewOptionLabel(next);
10936
+ const answer = await useWizard.getState().requestUserInput({
10937
+ prompt: "You have a previous session. Resume it, or start a new run?",
10938
+ promptType: "multipleChoice",
10939
+ options: [resumeOption, startOption]
10940
+ });
10941
+ return answer === resumeOption ? "resume" : "start-new";
10942
+ }
10943
+
10201
10944
  // package.json with { type: 'json' }
10202
10945
  var package_default2 = {
10203
10946
  name: "@algolia/wizard",
10204
- version: "0.112.0",
10947
+ version: "0.114.0",
10205
10948
  description: "Magically implement Algolia functionality in your codebase",
10206
10949
  type: "module",
10207
10950
  engines: {
@@ -10342,6 +11085,13 @@ ${USAGE}`);
10342
11085
  console.error(`Unknown workflow "${requestedId}". Available: ${available}`);
10343
11086
  return 1;
10344
11087
  }
11088
+ const flagError = cliArgsError(args, workflow.id);
11089
+ if (flagError) {
11090
+ console.error(flagError);
11091
+ console.error(`
11092
+ ${USAGE}`);
11093
+ return 1;
11094
+ }
10345
11095
  if (await checkVersion()) {
10346
11096
  console.error(
10347
11097
  "You have loaded an old version of the Wizard. Please use @algolia/wizard@latest"
@@ -10365,10 +11115,16 @@ ${formatStepList(workflow)}`);
10365
11115
  return 1;
10366
11116
  }
10367
11117
  }
10368
- return { workflow, skipResumePrompt: args.seed !== void 0 };
11118
+ return {
11119
+ workflow,
11120
+ skipResumePrompt: args.seed !== void 0,
11121
+ applicationId: args.applicationId,
11122
+ playbookSlug: workflow.id === "playbook" ? args.positionals[1] : void 0,
11123
+ indexName: workflow.id === "playbook" ? args.indexName : void 0
11124
+ };
10369
11125
  }
10370
11126
  async function run(opts) {
10371
- const { workflow, skipResumePrompt } = opts;
11127
+ const { workflow, skipResumePrompt, applicationId, playbookSlug, indexName } = opts;
10372
11128
  const store = useWizard.getState();
10373
11129
  const instance = render(/* @__PURE__ */ jsx23(App, {}), { incrementalRendering: true });
10374
11130
  await store.waitForStart();
@@ -10405,12 +11161,21 @@ async function run(opts) {
10405
11161
  }
10406
11162
  store.setUser(user);
10407
11163
  let app;
10408
- const resumableState = await loadResumableState(workflow);
11164
+ const session = await loadResumableState(workflow);
10409
11165
  try {
11166
+ const choice = session && !skipResumePrompt ? await promptResumeOrStartNew(sessionRunTarget(session), {
11167
+ applicationId,
11168
+ playbookSlug,
11169
+ indexName
11170
+ }) : void 0;
11171
+ const resumableState = choice === "start-new" ? null : session ?? void 0;
10410
11172
  app = await ensureApplication(
10411
- resumableState ? runAppId(resumableState) : void 0
11173
+ resumableState ? runAppId(resumableState) : applicationId
10412
11174
  );
10413
- runWorkflow(workflow, app.id, { skipResumePrompt, resumableState });
11175
+ runWorkflow(workflow, app.id, {
11176
+ skipResumePrompt: true,
11177
+ resumableState
11178
+ });
10414
11179
  } catch (err) {
10415
11180
  await exitProcessWithError(
10416
11181
  store,