@algolia/wizard 0.112.0 → 0.113.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.
- package/dist/main.js +885 -120
- 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(
|
|
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 ?
|
|
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)
|
|
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(
|
|
1311
|
-
|
|
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) ? "
|
|
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}${
|
|
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
|
-
|
|
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
|
|
2465
|
-
|
|
2466
|
-
|
|
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.
|
|
2498
|
+
version: "0.113.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", {
|
|
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
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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: {
|
|
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
|
|
8111
|
+
var settingRecommendationSchema = z32.object({
|
|
8112
|
+
current: z32.any(),
|
|
8113
|
+
recommended: z32.any()
|
|
8114
|
+
});
|
|
8115
|
+
var settingsRecommendationEntriesSchema = z32.record(
|
|
8026
8116
|
z32.string(),
|
|
8027
|
-
|
|
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:
|
|
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}/
|
|
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
|
-
|
|
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
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
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
|
|
8173
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
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
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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.
|
|
10947
|
+
version: "0.113.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 {
|
|
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
|
|
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) :
|
|
11173
|
+
resumableState ? runAppId(resumableState) : applicationId
|
|
10412
11174
|
);
|
|
10413
|
-
runWorkflow(workflow, app.id, {
|
|
11175
|
+
runWorkflow(workflow, app.id, {
|
|
11176
|
+
skipResumePrompt: true,
|
|
11177
|
+
resumableState
|
|
11178
|
+
});
|
|
10414
11179
|
} catch (err) {
|
|
10415
11180
|
await exitProcessWithError(
|
|
10416
11181
|
store,
|