@algolia/wizard 0.6.0-rc.51.25 → 0.6.0-rc.51.26
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 +192 -126
- package/docs/algolia-sdk/README.md +0 -5
- package/package.json +1 -1
- package/docs/algolia-sdk/search-single-index.md +0 -42
package/dist/main.js
CHANGED
|
@@ -2200,6 +2200,7 @@ function shellQuote(value) {
|
|
|
2200
2200
|
// src/lib/languages.ts
|
|
2201
2201
|
var ENTRYPOINT_TOKEN = "{entrypoint}";
|
|
2202
2202
|
var INGEST_DIR = ".algolia-wizard";
|
|
2203
|
+
var VISIBLE_INGEST_DIR = "algolia-wizard";
|
|
2203
2204
|
var PY_VENV = `${INGEST_DIR}/.venv`;
|
|
2204
2205
|
var PY_VENV_PYTHON = `${PY_VENV}/bin/python`;
|
|
2205
2206
|
var PY_REQUIREMENTS = `${INGEST_DIR}/requirements.txt`;
|
|
@@ -2308,7 +2309,8 @@ var LANGUAGE_PROFILES = {
|
|
|
2308
2309
|
// -x skips the venv this same directory holds; without it the check
|
|
2309
2310
|
// compiles every installed package instead of the generated script.
|
|
2310
2311
|
label: "python compileall",
|
|
2311
|
-
argv: ["python3", "-m", "compileall", "-q", "-x", "[.]venv", INGEST_DIR]
|
|
2312
|
+
argv: ["python3", "-m", "compileall", "-q", "-x", "[.]venv", INGEST_DIR],
|
|
2313
|
+
requiresFile: INGEST_DIR
|
|
2312
2314
|
}
|
|
2313
2315
|
],
|
|
2314
2316
|
envReadInstruction: "Read them from `os.environ`.",
|
|
@@ -2397,7 +2399,8 @@ var LANGUAGE_PROFILES = {
|
|
|
2397
2399
|
{
|
|
2398
2400
|
id: "gomod",
|
|
2399
2401
|
// Imports in the generated file are the declaration; `go mod tidy`
|
|
2400
|
-
// resolves and fetches them
|
|
2402
|
+
// resolves and fetches them — which only works because the script lives
|
|
2403
|
+
// outside INGEST_DIR (see VISIBLE_INGEST_DIR).
|
|
2401
2404
|
dependency: { mode: "code-imports" },
|
|
2402
2405
|
installSteps: [{ argv: ["go", "mod", "tidy"] }],
|
|
2403
2406
|
ingest: {
|
|
@@ -2412,7 +2415,7 @@ var LANGUAGE_PROFILES = {
|
|
|
2412
2415
|
versionPin: "v4",
|
|
2413
2416
|
docKey: "go"
|
|
2414
2417
|
},
|
|
2415
|
-
ingestEntrypointExample: `${
|
|
2418
|
+
ingestEntrypointExample: `${VISIBLE_INGEST_DIR}/ingest.go`,
|
|
2416
2419
|
verification: [
|
|
2417
2420
|
{ label: "go vet", argv: ["go", "vet", "./..."], requiresFile: "go.mod" }
|
|
2418
2421
|
],
|
|
@@ -2448,7 +2451,11 @@ var LANGUAGE_PROFILES = {
|
|
|
2448
2451
|
{
|
|
2449
2452
|
id: "gradle",
|
|
2450
2453
|
detectFiles: ["build.gradle", "build.gradle.kts"],
|
|
2451
|
-
dependency: {
|
|
2454
|
+
dependency: {
|
|
2455
|
+
mode: "agent-declares",
|
|
2456
|
+
file: "build.gradle",
|
|
2457
|
+
alternatives: ["build.gradle.kts"]
|
|
2458
|
+
},
|
|
2452
2459
|
installSteps: [],
|
|
2453
2460
|
// Auto-running means executing the repo's own ./gradlew wrapper; out of
|
|
2454
2461
|
// scope for now, so the wizard writes the code and prints the command.
|
|
@@ -2487,13 +2494,33 @@ var LANGUAGE_PROFILES = {
|
|
|
2487
2494
|
packageManagers: [
|
|
2488
2495
|
{
|
|
2489
2496
|
id: "gradle",
|
|
2490
|
-
|
|
2497
|
+
detectFiles: ["build.gradle.kts", "build.gradle"],
|
|
2498
|
+
dependency: {
|
|
2499
|
+
mode: "agent-declares",
|
|
2500
|
+
file: "build.gradle.kts",
|
|
2501
|
+
alternatives: ["build.gradle"]
|
|
2502
|
+
},
|
|
2491
2503
|
installSteps: [],
|
|
2492
2504
|
ingest: {
|
|
2493
2505
|
kind: "manual",
|
|
2494
2506
|
entrypointExtensions: [".kt"],
|
|
2495
2507
|
runCommand: "./gradlew runAlgoliaIngest"
|
|
2496
2508
|
}
|
|
2509
|
+
},
|
|
2510
|
+
// Kotlin/Maven is rare but real, and pom.xml is a Kotlin manifest — without
|
|
2511
|
+
// this spec such a repo falls through to Gradle and is told to run a
|
|
2512
|
+
// ./gradlew task that doesn't exist. Compiling needs the repo's own
|
|
2513
|
+
// kotlin-maven-plugin, so the run stays the developer's step.
|
|
2514
|
+
{
|
|
2515
|
+
id: "maven",
|
|
2516
|
+
detectFiles: ["pom.xml"],
|
|
2517
|
+
dependency: { mode: "agent-declares", file: "pom.xml" },
|
|
2518
|
+
installSteps: [{ argv: ["mvn", "-q", "-DskipTests", "compile"] }],
|
|
2519
|
+
ingest: {
|
|
2520
|
+
kind: "manual",
|
|
2521
|
+
entrypointExtensions: [".kt"],
|
|
2522
|
+
runCommand: "mvn -q org.codehaus.mojo:exec-maven-plugin:3.5.0:java -Dexec.mainClass=AlgoliaWizardIngest"
|
|
2523
|
+
}
|
|
2497
2524
|
}
|
|
2498
2525
|
],
|
|
2499
2526
|
sdk: {
|
|
@@ -2660,9 +2687,12 @@ var LANGUAGE_PROFILES = {
|
|
|
2660
2687
|
ingestEntrypointExample: `${INGEST_DIR}/ingest.dart`,
|
|
2661
2688
|
verification: [
|
|
2662
2689
|
{
|
|
2690
|
+
// Gated on the directory it analyzes, not just pubspec.yaml: a run that
|
|
2691
|
+
// only built a search UI never created it, and `dart analyze` on a
|
|
2692
|
+
// missing path fails the whole verification pass.
|
|
2663
2693
|
label: "dart analyze",
|
|
2664
2694
|
argv: ["dart", "analyze", INGEST_DIR],
|
|
2665
|
-
requiresFile:
|
|
2695
|
+
requiresFile: INGEST_DIR
|
|
2666
2696
|
}
|
|
2667
2697
|
],
|
|
2668
2698
|
envReadInstruction: "Read them from `Platform.environment`.",
|
|
@@ -2670,6 +2700,13 @@ var LANGUAGE_PROFILES = {
|
|
|
2670
2700
|
}
|
|
2671
2701
|
};
|
|
2672
2702
|
var DEFAULT_LANGUAGE_ID = "javascript";
|
|
2703
|
+
var JAVASCRIPT = "javascript";
|
|
2704
|
+
var CURATED_LANGUAGES = Object.values(
|
|
2705
|
+
LANGUAGE_PROFILES
|
|
2706
|
+
).map((profile2) => profile2.displayName);
|
|
2707
|
+
function isBackendLanguage(profile2) {
|
|
2708
|
+
return profile2.id !== JAVASCRIPT;
|
|
2709
|
+
}
|
|
2673
2710
|
function normalizeLanguageName(name) {
|
|
2674
2711
|
return name.toLowerCase().trim().replace(/[\s_-]+/g, "");
|
|
2675
2712
|
}
|
|
@@ -2683,6 +2720,14 @@ function resolveLanguageProfile(name) {
|
|
|
2683
2720
|
const id = ALIAS_TO_ID.get(normalizeLanguageName(name));
|
|
2684
2721
|
return id ? LANGUAGE_PROFILES[id] : void 0;
|
|
2685
2722
|
}
|
|
2723
|
+
function isSameLanguage(a, b) {
|
|
2724
|
+
const x = resolveLanguageProfile(a);
|
|
2725
|
+
const y = resolveLanguageProfile(b);
|
|
2726
|
+
if (x && y) return x.id === y.id;
|
|
2727
|
+
if (x || y) return false;
|
|
2728
|
+
const folded = normalizeLanguageName(a);
|
|
2729
|
+
return folded !== "" && folded === normalizeLanguageName(b);
|
|
2730
|
+
}
|
|
2686
2731
|
var BASE_SKIP_DIRS = ["node_modules", ".git", "dist"];
|
|
2687
2732
|
var ALL_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2688
2733
|
...BASE_SKIP_DIRS,
|
|
@@ -2704,6 +2749,17 @@ function isWorktreeRelativeCommand(command) {
|
|
|
2704
2749
|
function withCommand(argv, command) {
|
|
2705
2750
|
return [command, ...argv.slice(1)];
|
|
2706
2751
|
}
|
|
2752
|
+
function resolveDeclaredManifest(root, packageManager) {
|
|
2753
|
+
const { dependency } = packageManager;
|
|
2754
|
+
if (dependency.mode !== "agent-declares" || !dependency.alternatives?.length) {
|
|
2755
|
+
return packageManager;
|
|
2756
|
+
}
|
|
2757
|
+
const present = [dependency.file, ...dependency.alternatives].find(
|
|
2758
|
+
(file) => existsSync2(join9(root, file))
|
|
2759
|
+
);
|
|
2760
|
+
if (!present || present === dependency.file) return packageManager;
|
|
2761
|
+
return { ...packageManager, dependency: { ...dependency, file: present } };
|
|
2762
|
+
}
|
|
2707
2763
|
async function manifestPresent(root, manifest, listing) {
|
|
2708
2764
|
if (!manifest.startsWith("*.")) return existsSync2(join9(root, manifest));
|
|
2709
2765
|
if (!listing.entries) {
|
|
@@ -2730,13 +2786,27 @@ async function detectProfilesFromManifests(root) {
|
|
|
2730
2786
|
async function hasProfileManifest(root, profile2) {
|
|
2731
2787
|
return profileManifestPresent(root, profile2, {});
|
|
2732
2788
|
}
|
|
2789
|
+
async function pickIngestionCandidates(root, confirmedNames) {
|
|
2790
|
+
const confirmed3 = confirmedNames.map((name) => resolveLanguageProfile(name)).filter((p) => p !== void 0);
|
|
2791
|
+
const onDisk = await detectProfilesFromManifests(root);
|
|
2792
|
+
const onDiskIds = new Set(onDisk.map((p) => p.id));
|
|
2793
|
+
const candidates = [
|
|
2794
|
+
...new Map(
|
|
2795
|
+
confirmed3.filter((p) => onDiskIds.has(p.id)).map((p) => [p.id, p])
|
|
2796
|
+
).values()
|
|
2797
|
+
];
|
|
2798
|
+
return { candidates, confirmed: confirmed3, onDisk };
|
|
2799
|
+
}
|
|
2733
2800
|
async function resolveToolchain(root, profile2) {
|
|
2734
2801
|
const matched = profile2.packageManagers.find(
|
|
2735
2802
|
(pm) => [...pm.lockfiles ?? [], ...pm.detectFiles ?? []].some(
|
|
2736
2803
|
(f) => existsSync2(join9(root, f))
|
|
2737
2804
|
)
|
|
2738
2805
|
);
|
|
2739
|
-
const packageManager =
|
|
2806
|
+
const packageManager = resolveDeclaredManifest(
|
|
2807
|
+
root,
|
|
2808
|
+
matched ?? profile2.packageManagers[0]
|
|
2809
|
+
);
|
|
2740
2810
|
let { installSteps, ingest } = packageManager;
|
|
2741
2811
|
installSteps = installSteps.map(
|
|
2742
2812
|
(step) => isWorktreeRelativeCommand(step.argv[0]) ? { ...step, argv: withCommand(step.argv, join9(root, step.argv[0])) } : step
|
|
@@ -2860,27 +2930,61 @@ import { join as join11 } from "node:path";
|
|
|
2860
2930
|
|
|
2861
2931
|
// src/lib/tools/utils/runCommand.ts
|
|
2862
2932
|
import { spawn as spawn2 } from "node:child_process";
|
|
2863
|
-
|
|
2933
|
+
var INSTALL_TIMEOUT_MS = 15 * 6e4;
|
|
2934
|
+
var INGEST_TIMEOUT_MS = 15 * 6e4;
|
|
2935
|
+
var VERIFY_TIMEOUT_MS = 10 * 6e4;
|
|
2936
|
+
var KILL_GRACE_MS = 5e3;
|
|
2937
|
+
function runCommand(command, args, options = {}) {
|
|
2938
|
+
const { cwd, env, timeoutMs = VERIFY_TIMEOUT_MS } = options;
|
|
2864
2939
|
return new Promise((resolve4) => {
|
|
2865
2940
|
let output = "";
|
|
2941
|
+
let settled = false;
|
|
2866
2942
|
const child = spawn2(command, args, {
|
|
2867
2943
|
cwd,
|
|
2868
|
-
|
|
2944
|
+
shell: false,
|
|
2945
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2946
|
+
...env ? { env: { ...process.env, ...env } } : {}
|
|
2869
2947
|
});
|
|
2948
|
+
const settle = (result) => {
|
|
2949
|
+
if (settled) return;
|
|
2950
|
+
settled = true;
|
|
2951
|
+
clearTimeout(timer);
|
|
2952
|
+
resolve4(result);
|
|
2953
|
+
};
|
|
2954
|
+
const timer = setTimeout(() => {
|
|
2955
|
+
child.kill("SIGTERM");
|
|
2956
|
+
setTimeout(() => child.kill("SIGKILL"), KILL_GRACE_MS).unref();
|
|
2957
|
+
const seconds = Math.round(timeoutMs / 1e3);
|
|
2958
|
+
settle({
|
|
2959
|
+
code: 1,
|
|
2960
|
+
output: `${output}
|
|
2961
|
+
Timed out after ${seconds}s: ${command} ${args.join(" ")}`.trim(),
|
|
2962
|
+
timedOut: true
|
|
2963
|
+
});
|
|
2964
|
+
}, timeoutMs);
|
|
2870
2965
|
child.stdout?.on("data", (d) => output += d);
|
|
2871
2966
|
child.stderr?.on("data", (d) => output += d);
|
|
2872
2967
|
child.on(
|
|
2873
2968
|
"error",
|
|
2874
|
-
(err) =>
|
|
2969
|
+
(err) => settle({
|
|
2970
|
+
code: 1,
|
|
2971
|
+
output: `Failed to run ${command}: ${err.message}`,
|
|
2972
|
+
timedOut: false
|
|
2973
|
+
})
|
|
2974
|
+
);
|
|
2975
|
+
child.on(
|
|
2976
|
+
"close",
|
|
2977
|
+
(code) => settle({ code: code ?? 1, output, timedOut: false })
|
|
2875
2978
|
);
|
|
2876
|
-
child.on("close", (code) => resolve4({ code: code ?? 1, output }));
|
|
2877
2979
|
});
|
|
2878
2980
|
}
|
|
2879
2981
|
|
|
2880
2982
|
// src/lib/tools/repoVerification.ts
|
|
2881
2983
|
var VERIFICATION_SCRIPT_CANDIDATES = ["lint", "typecheck", "check"];
|
|
2882
2984
|
async function runCheck(command, binary, args) {
|
|
2883
|
-
const { code, output } = await runCommand(binary, args
|
|
2985
|
+
const { code, output } = await runCommand(binary, args, {
|
|
2986
|
+
timeoutMs: VERIFY_TIMEOUT_MS
|
|
2987
|
+
});
|
|
2884
2988
|
return { command, exitCode: code, ok: code === 0, output: output.trim() };
|
|
2885
2989
|
}
|
|
2886
2990
|
async function javascriptChecks() {
|
|
@@ -2914,7 +3018,7 @@ async function runRepoVerificationCheck(languages = [DEFAULT_LANGUAGE_ID]) {
|
|
|
2914
3018
|
const checks = [];
|
|
2915
3019
|
const limitations = [];
|
|
2916
3020
|
for (const id of ids) {
|
|
2917
|
-
if (id ===
|
|
3021
|
+
if (id === JAVASCRIPT) {
|
|
2918
3022
|
const result = await javascriptChecks();
|
|
2919
3023
|
if ("checks" in result) checks.push(...result.checks);
|
|
2920
3024
|
else limitations.push(result.limitation);
|
|
@@ -3085,7 +3189,11 @@ var DEFAULT_TOOL_LIMITS = {
|
|
|
3085
3189
|
read: 20,
|
|
3086
3190
|
match: 100
|
|
3087
3191
|
};
|
|
3088
|
-
function createToolContext(
|
|
3192
|
+
function createToolContext({
|
|
3193
|
+
limits = DEFAULT_TOOL_LIMITS,
|
|
3194
|
+
cwd = process.cwd(),
|
|
3195
|
+
languages = [DEFAULT_LANGUAGE_ID]
|
|
3196
|
+
} = {}) {
|
|
3089
3197
|
return {
|
|
3090
3198
|
root: cwd,
|
|
3091
3199
|
cwd,
|
|
@@ -3164,11 +3272,7 @@ async function runAgent(req) {
|
|
|
3164
3272
|
baseURL: PROXY_BASE_URL,
|
|
3165
3273
|
fetch: proxyFetch
|
|
3166
3274
|
});
|
|
3167
|
-
const toolContext = createToolContext(
|
|
3168
|
-
void 0,
|
|
3169
|
-
void 0,
|
|
3170
|
-
req.languages
|
|
3171
|
-
);
|
|
3275
|
+
const toolContext = createToolContext({ languages: req.languages });
|
|
3172
3276
|
const readTools = ["readFile", "searchFiles", "listFiles"];
|
|
3173
3277
|
const hasReadTools = !req.tools || req.tools.some((t) => readTools.includes(t));
|
|
3174
3278
|
const instructions = [
|
|
@@ -3363,7 +3467,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3363
3467
|
// package.json
|
|
3364
3468
|
var package_default = {
|
|
3365
3469
|
name: "@algolia/wizard",
|
|
3366
|
-
version: "0.6.0-rc.51.
|
|
3470
|
+
version: "0.6.0-rc.51.26",
|
|
3367
3471
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3368
3472
|
type: "module",
|
|
3369
3473
|
engines: {
|
|
@@ -3473,17 +3577,6 @@ var confirmLanguageSchema = z19.object({
|
|
|
3473
3577
|
languages: detectLanguageSchema.shape.languages
|
|
3474
3578
|
});
|
|
3475
3579
|
var OTHER_OPTION = "Other";
|
|
3476
|
-
var CURATED_LANGUAGES = Object.values(LANGUAGE_PROFILES).map(
|
|
3477
|
-
(profile2) => profile2.displayName
|
|
3478
|
-
);
|
|
3479
|
-
function isSameLanguage(a, b) {
|
|
3480
|
-
const x = resolveLanguageProfile(a);
|
|
3481
|
-
const y = resolveLanguageProfile(b);
|
|
3482
|
-
if (x && y) return x.id === y.id;
|
|
3483
|
-
if (x || y) return false;
|
|
3484
|
-
const fold = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3485
|
-
return fold(a) !== "" && fold(a) === fold(b);
|
|
3486
|
-
}
|
|
3487
3580
|
function confirmed(languages) {
|
|
3488
3581
|
track("AI Wizard Language Confirmed", { languages });
|
|
3489
3582
|
return { languages };
|
|
@@ -3612,6 +3705,27 @@ function resolveSearchStrategy(frameworkName, hasJavaScriptInStack) {
|
|
|
3612
3705
|
function searchDocKey(strategy) {
|
|
3613
3706
|
return strategy === "cdn-template" ? "templates" : strategy;
|
|
3614
3707
|
}
|
|
3708
|
+
function bundlesJavaScript(strategy) {
|
|
3709
|
+
return strategy !== "cdn-template" && strategy !== "none";
|
|
3710
|
+
}
|
|
3711
|
+
function canScaffoldSearchUI(strategy) {
|
|
3712
|
+
return strategy !== "none";
|
|
3713
|
+
}
|
|
3714
|
+
var ENV_PREFIXES = [
|
|
3715
|
+
{ aliases: ["next", "nextjs"], prefix: "NEXT_PUBLIC_" },
|
|
3716
|
+
{ aliases: ["nuxt", "nuxtjs"], prefix: "NUXT_PUBLIC_" },
|
|
3717
|
+
{ aliases: ["astro"], prefix: "PUBLIC_" },
|
|
3718
|
+
{ aliases: ["vite"], prefix: "VITE_" }
|
|
3719
|
+
];
|
|
3720
|
+
var DEFAULT_ENV_PREFIX = "PUBLIC_";
|
|
3721
|
+
function publicEnvPrefix(frameworkNames, strategy) {
|
|
3722
|
+
if (!bundlesJavaScript(strategy)) return "";
|
|
3723
|
+
const present = new Set(frameworkNames.map(normalize));
|
|
3724
|
+
for (const { aliases, prefix } of ENV_PREFIXES) {
|
|
3725
|
+
if (aliases.some((alias) => present.has(alias))) return prefix;
|
|
3726
|
+
}
|
|
3727
|
+
return DEFAULT_ENV_PREFIX;
|
|
3728
|
+
}
|
|
3615
3729
|
function describeSearchTarget(strategy, frameworkName) {
|
|
3616
3730
|
switch (strategy) {
|
|
3617
3731
|
case "react":
|
|
@@ -3819,7 +3933,7 @@ ${JSON.stringify(s.output, null, 2)}`
|
|
|
3819
3933
|
}
|
|
3820
3934
|
function formatReviewSummary(result) {
|
|
3821
3935
|
const nextStepLines = result.nextSteps.map((step) => {
|
|
3822
|
-
const isIngestCommand = step.includes("
|
|
3936
|
+
const isIngestCommand = step.includes("algolia-wizard/") || step.includes("AlgoliaWizardIngest");
|
|
3823
3937
|
const isWorktreeCommand = step.includes("/worktrees/");
|
|
3824
3938
|
return {
|
|
3825
3939
|
text: `\u2192 ${step}`,
|
|
@@ -3861,7 +3975,7 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3861
3975
|
import z24 from "zod";
|
|
3862
3976
|
|
|
3863
3977
|
// src/lib/worktree.ts
|
|
3864
|
-
import { execFile
|
|
3978
|
+
import { execFile } from "node:child_process";
|
|
3865
3979
|
import { existsSync as existsSync4 } from "node:fs";
|
|
3866
3980
|
import { copyFile, mkdir as mkdir6, readdir as readdir4, readFile as readFile8, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3867
3981
|
import {
|
|
@@ -3934,28 +4048,12 @@ async function createWorktree(repoRoot) {
|
|
|
3934
4048
|
await git(["-C", repoRoot, "worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3935
4049
|
return { path, branch };
|
|
3936
4050
|
}
|
|
3937
|
-
function spawnStep(worktreePath, argv) {
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
cwd: worktreePath,
|
|
3942
|
-
shell: false,
|
|
3943
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3944
|
-
});
|
|
3945
|
-
child.stdout?.on("data", (d) => output += d);
|
|
3946
|
-
child.stderr?.on("data", (d) => output += d);
|
|
3947
|
-
child.on(
|
|
3948
|
-
"error",
|
|
3949
|
-
(err) => resolve4({
|
|
3950
|
-
ok: false,
|
|
3951
|
-
output: `Failed to run ${argv.join(" ")}: ${err.message}`
|
|
3952
|
-
})
|
|
3953
|
-
);
|
|
3954
|
-
child.on(
|
|
3955
|
-
"close",
|
|
3956
|
-
(code) => resolve4({ ok: code === 0, output: output.trim() })
|
|
3957
|
-
);
|
|
4051
|
+
async function spawnStep(worktreePath, argv) {
|
|
4052
|
+
const { code, output } = await runCommand(argv[0], [...argv.slice(1)], {
|
|
4053
|
+
cwd: worktreePath,
|
|
4054
|
+
timeoutMs: INSTALL_TIMEOUT_MS
|
|
3958
4055
|
});
|
|
4056
|
+
return { ok: code === 0, output: output.trim() };
|
|
3959
4057
|
}
|
|
3960
4058
|
async function installWorktreeDeps(worktreePath, toolchain) {
|
|
3961
4059
|
const { profile: profile2, installSteps, packageManager } = toolchain;
|
|
@@ -4050,29 +4148,12 @@ async function runIngestScript(worktreePath, toolchain, entrypoint, env = {}) {
|
|
|
4050
4148
|
};
|
|
4051
4149
|
}
|
|
4052
4150
|
const argv = resolveIngestArgv(ingest, entrypoint);
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
shell: false,
|
|
4058
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
4059
|
-
env: { ...process.env, ...env }
|
|
4060
|
-
});
|
|
4061
|
-
child.stdout?.on("data", (d) => output += d);
|
|
4062
|
-
child.stderr?.on("data", (d) => output += d);
|
|
4063
|
-
child.on(
|
|
4064
|
-
"error",
|
|
4065
|
-
(err) => resolveRun({
|
|
4066
|
-
ran: true,
|
|
4067
|
-
ok: false,
|
|
4068
|
-
output: `Failed to run ${argv.join(" ")}: ${err.message}`
|
|
4069
|
-
})
|
|
4070
|
-
);
|
|
4071
|
-
child.on(
|
|
4072
|
-
"close",
|
|
4073
|
-
(code) => resolveRun({ ran: true, ok: code === 0, output: output.trim() })
|
|
4074
|
-
);
|
|
4151
|
+
const { code, output } = await runCommand(argv[0], argv.slice(1), {
|
|
4152
|
+
cwd: worktreePath,
|
|
4153
|
+
env,
|
|
4154
|
+
timeoutMs: INGEST_TIMEOUT_MS
|
|
4075
4155
|
});
|
|
4156
|
+
return { ran: true, ok: code === 0, output: output.trim() };
|
|
4076
4157
|
}
|
|
4077
4158
|
async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourcePath) {
|
|
4078
4159
|
const trimmed = sourcePath.trim();
|
|
@@ -4299,28 +4380,11 @@ var verificationOutputSchema = z24.object({
|
|
|
4299
4380
|
});
|
|
4300
4381
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
4301
4382
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
(framework) => framework.name.toLowerCase()
|
|
4383
|
+
function buildSearchEnvVars(language, strategy, appId, searchKey) {
|
|
4384
|
+
const prefix = publicEnvPrefix(
|
|
4385
|
+
language.frameworks.map((framework) => framework.name),
|
|
4386
|
+
strategy
|
|
4307
4387
|
);
|
|
4308
|
-
if (frameworkNames.some((name) => name.includes("next"))) {
|
|
4309
|
-
return "NEXT_PUBLIC_";
|
|
4310
|
-
}
|
|
4311
|
-
if (frameworkNames.some((name) => name.includes("nuxt"))) {
|
|
4312
|
-
return "NUXT_PUBLIC_";
|
|
4313
|
-
}
|
|
4314
|
-
if (frameworkNames.some((name) => name.includes("astro"))) {
|
|
4315
|
-
return "PUBLIC_";
|
|
4316
|
-
}
|
|
4317
|
-
if (frameworkNames.some((name) => name.includes("vite"))) {
|
|
4318
|
-
return "VITE_";
|
|
4319
|
-
}
|
|
4320
|
-
return "PUBLIC_";
|
|
4321
|
-
}
|
|
4322
|
-
function searchEnvVars(language, strategy, appId, searchKey) {
|
|
4323
|
-
const prefix = publicEnvPrefix(language, strategy);
|
|
4324
4388
|
return [
|
|
4325
4389
|
{
|
|
4326
4390
|
name: `${prefix}ALGOLIA_APP_ID`,
|
|
@@ -4333,17 +4397,12 @@ function searchEnvVars(language, strategy, appId, searchKey) {
|
|
|
4333
4397
|
];
|
|
4334
4398
|
}
|
|
4335
4399
|
async function resolveIngestionProfile(ctx, language, repoRoot) {
|
|
4336
|
-
const
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
const candidates = [
|
|
4341
|
-
...new Map(
|
|
4342
|
-
confirmed3.filter((p) => onDiskIds.has(p.id)).map((p) => [p.id, p])
|
|
4343
|
-
).values()
|
|
4344
|
-
];
|
|
4400
|
+
const { candidates, confirmed: confirmed3, onDisk } = await pickIngestionCandidates(
|
|
4401
|
+
repoRoot,
|
|
4402
|
+
language.languages.map((l) => l.name)
|
|
4403
|
+
);
|
|
4345
4404
|
if (candidates.length === 0) {
|
|
4346
|
-
const chosen = confirmed3[0] ?? onDisk[0] ??
|
|
4405
|
+
const chosen = confirmed3[0] ?? onDisk[0] ?? LANGUAGE_PROFILES[DEFAULT_LANGUAGE_ID];
|
|
4347
4406
|
logger.warn(
|
|
4348
4407
|
{
|
|
4349
4408
|
confirmed: language.languages.map((l) => l.name),
|
|
@@ -4355,9 +4414,10 @@ async function resolveIngestionProfile(ctx, language, repoRoot) {
|
|
|
4355
4414
|
return chosen;
|
|
4356
4415
|
}
|
|
4357
4416
|
if (candidates.length === 1) return candidates[0];
|
|
4358
|
-
const backends = candidates.filter(
|
|
4417
|
+
const backends = candidates.filter(isBackendLanguage);
|
|
4359
4418
|
if (backends.length === 1) return backends[0];
|
|
4360
4419
|
if (backends.length === 0) return candidates[0];
|
|
4420
|
+
if (isBackendLanguage(candidates[0])) return candidates[0];
|
|
4361
4421
|
const options = backends.map((p) => p.displayName);
|
|
4362
4422
|
const selection = await ctx.requestUserInput({
|
|
4363
4423
|
prompt: "Which language should the ingestion script use?",
|
|
@@ -4449,6 +4509,9 @@ function searchInstructions(input) {
|
|
|
4449
4509
|
];
|
|
4450
4510
|
}
|
|
4451
4511
|
function verificationInstructions(input) {
|
|
4512
|
+
const protectedDirs = [
|
|
4513
|
+
.../* @__PURE__ */ new Set([input.ingestDir, ingestScriptDir(input.ingestionProfile)])
|
|
4514
|
+
];
|
|
4452
4515
|
return [
|
|
4453
4516
|
"Verify the Algolia implementation changes in the current worktree.",
|
|
4454
4517
|
`Verification tools found in the codebase: ${JSON.stringify(input.findings.verification ?? [])}.`,
|
|
@@ -4456,7 +4519,7 @@ function verificationInstructions(input) {
|
|
|
4456
4519
|
"For issues caused by the implementation, make minimal fixes with writeFile and re-run verifyImplementation.",
|
|
4457
4520
|
"Do not make speculative fixes when verifyImplementation cannot run, no checks exist, or failures are unrelated to these changes \u2014 note the limitation in your summary.",
|
|
4458
4521
|
"Do not add new Algolia functionality here; only validate and make minimal correctness fixes.",
|
|
4459
|
-
`Do not modify "${
|
|
4522
|
+
`Do not modify ${protectedDirs.map((dir) => `"${dir}/"`).join(" or ")} unless verifyImplementation reports an actionable issue in its files.`,
|
|
4460
4523
|
"Always call reportStatus with status=success once verification has run, even when sufficient=false.",
|
|
4461
4524
|
"Set sufficient=true only when the implementation is complete and checks pass (or fail for a clearly unrelated reason).",
|
|
4462
4525
|
"Set sufficient=false when the implementation is incomplete or has implementation-caused failures; include concrete additionalInstructions for the next pass."
|
|
@@ -4465,14 +4528,17 @@ function verificationInstructions(input) {
|
|
|
4465
4528
|
var IMPLEMENT_CONFIG = {
|
|
4466
4529
|
ingestion: {
|
|
4467
4530
|
title: "Algolia ingestion",
|
|
4531
|
+
label: "Ingestion",
|
|
4468
4532
|
buildInstructions: ingestionInstructions
|
|
4469
4533
|
},
|
|
4470
4534
|
search: {
|
|
4471
4535
|
title: "Algolia search",
|
|
4536
|
+
label: "Search",
|
|
4472
4537
|
buildInstructions: searchInstructions
|
|
4473
4538
|
},
|
|
4474
4539
|
verification: {
|
|
4475
4540
|
title: "Algolia verification",
|
|
4541
|
+
label: "Verification",
|
|
4476
4542
|
buildInstructions: verificationInstructions
|
|
4477
4543
|
}
|
|
4478
4544
|
};
|
|
@@ -4504,8 +4570,7 @@ function buildAgentInstructions(useCase, input, extraInstructions = []) {
|
|
|
4504
4570
|
];
|
|
4505
4571
|
}
|
|
4506
4572
|
function formatSummary(useCase, summary) {
|
|
4507
|
-
|
|
4508
|
-
return `${label}: ${summary}`;
|
|
4573
|
+
return `${IMPLEMENT_CONFIG[useCase].label}: ${summary}`;
|
|
4509
4574
|
}
|
|
4510
4575
|
function buildIngestCommand(worktree, toolchain, entrypoint) {
|
|
4511
4576
|
return `cd ${shellQuote(worktree)} && ${describeIngestCommand(toolchain.ingest, entrypoint)}`;
|
|
@@ -4614,7 +4679,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4614
4679
|
const copied = await copyUploadIntoWorktree(
|
|
4615
4680
|
repoRoot,
|
|
4616
4681
|
worktree,
|
|
4617
|
-
|
|
4682
|
+
INGEST_DIR,
|
|
4618
4683
|
uploadSourcePath ?? ""
|
|
4619
4684
|
);
|
|
4620
4685
|
if (copied.ok) {
|
|
@@ -4643,7 +4708,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4643
4708
|
const frameworkName = language.frameworks[0]?.name;
|
|
4644
4709
|
const searchStrategy = resolveSearchStrategy(
|
|
4645
4710
|
frameworkName,
|
|
4646
|
-
verificationLanguages.includes(
|
|
4711
|
+
verificationLanguages.includes(JAVASCRIPT)
|
|
4647
4712
|
);
|
|
4648
4713
|
logger.info(
|
|
4649
4714
|
{
|
|
@@ -4663,8 +4728,13 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4663
4728
|
language,
|
|
4664
4729
|
appId,
|
|
4665
4730
|
searchKey,
|
|
4666
|
-
searchEnvVars:
|
|
4667
|
-
|
|
4731
|
+
searchEnvVars: buildSearchEnvVars(
|
|
4732
|
+
language,
|
|
4733
|
+
searchStrategy,
|
|
4734
|
+
appId,
|
|
4735
|
+
searchKey
|
|
4736
|
+
),
|
|
4737
|
+
ingestDir: INGEST_DIR,
|
|
4668
4738
|
ingestionSource,
|
|
4669
4739
|
uploadFilePath,
|
|
4670
4740
|
searchStrategy,
|
|
@@ -4673,10 +4743,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4673
4743
|
toolchain,
|
|
4674
4744
|
verificationLanguages
|
|
4675
4745
|
};
|
|
4676
|
-
const searchToolchain = searchStrategy
|
|
4677
|
-
worktree,
|
|
4678
|
-
LANGUAGE_PROFILES[DEFAULT_LANGUAGE_ID]
|
|
4679
|
-
);
|
|
4746
|
+
const searchToolchain = !bundlesJavaScript(searchStrategy) ? void 0 : ingestionProfile.id === JAVASCRIPT ? toolchain : await resolveToolchain(worktree, LANGUAGE_PROFILES[JAVASCRIPT]);
|
|
4680
4747
|
const toolchainForUseCase = (useCase) => useCase === "search" ? searchToolchain : toolchain;
|
|
4681
4748
|
const summaries = [];
|
|
4682
4749
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
@@ -4685,7 +4752,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4685
4752
|
let ingestScriptRan = false;
|
|
4686
4753
|
let ingestRecordCount;
|
|
4687
4754
|
let ingestDurationMs;
|
|
4688
|
-
|
|
4755
|
+
const failedInstalls = /* @__PURE__ */ new Set();
|
|
4689
4756
|
let ingestOutcomeMessage;
|
|
4690
4757
|
async function runImplementationUseCase(currentUseCase, extraInstructions = []) {
|
|
4691
4758
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
@@ -4711,7 +4778,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4711
4778
|
const install = await installWorktreeDeps(worktree, useCaseToolchain);
|
|
4712
4779
|
ctx.logEnd(installLogId, install.ok ? "success" : "error");
|
|
4713
4780
|
if (!install.ok) {
|
|
4714
|
-
|
|
4781
|
+
failedInstalls.add(useCaseToolchain.profile.displayName);
|
|
4715
4782
|
logger.warn(
|
|
4716
4783
|
{ useCase: currentUseCase, output: install.output },
|
|
4717
4784
|
"implement: dependency install in worktree failed; generated commands may not run until deps are installed"
|
|
@@ -4734,7 +4801,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4734
4801
|
const { summary, entrypoint } = await runImplementationUseCase("ingestion");
|
|
4735
4802
|
summaries.push(formatSummary("ingestion", summary));
|
|
4736
4803
|
ingestEntrypoint = entrypoint;
|
|
4737
|
-
if (ingestEntrypoint && toolchain.ingest.kind === "auto" &&
|
|
4804
|
+
if (ingestEntrypoint && toolchain.ingest.kind === "auto" && failedInstalls.size === 0) {
|
|
4738
4805
|
ctx.clearNotices();
|
|
4739
4806
|
const runNow = await ctx.requestUserInput({
|
|
4740
4807
|
prompt: `Run the ingestion script now? This writes records to the "${targetIndex}" index.`,
|
|
@@ -4838,7 +4905,7 @@ ${run.output}` : status;
|
|
|
4838
4905
|
messages: ingestOutcomeMessage ? [ingestOutcomeMessage, ...commandMessages] : commandMessages
|
|
4839
4906
|
});
|
|
4840
4907
|
}
|
|
4841
|
-
const skipSearch = useCases.includes("search") && input.searchStrategy
|
|
4908
|
+
const skipSearch = useCases.includes("search") && !canScaffoldSearchUI(input.searchStrategy);
|
|
4842
4909
|
if (skipSearch) {
|
|
4843
4910
|
const target = describeSearchTarget(
|
|
4844
4911
|
input.searchStrategy,
|
|
@@ -4847,7 +4914,6 @@ ${run.output}` : status;
|
|
|
4847
4914
|
summaries.push(
|
|
4848
4915
|
`Search UI skipped: the wizard can't scaffold a native search UI for ${target}. Your records are in the "${targetIndex}" index \u2014 build the UI with Algolia's mobile InstantSearch libraries (https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/ios/ for iOS, .../android for Android).`
|
|
4849
4916
|
);
|
|
4850
|
-
ctx.setUserInput("implementation", "success");
|
|
4851
4917
|
track("AI Wizard Search UI Skipped", {
|
|
4852
4918
|
framework: input.frameworkName ?? "unknown"
|
|
4853
4919
|
});
|
|
@@ -4923,9 +4989,9 @@ ${run.output}` : status;
|
|
|
4923
4989
|
"implement: agent reported success but no files changed in the worktree"
|
|
4924
4990
|
);
|
|
4925
4991
|
}
|
|
4926
|
-
if (
|
|
4992
|
+
if (failedInstalls.size > 0) {
|
|
4927
4993
|
summaries.push(
|
|
4928
|
-
`\u26A0\uFE0F Dependency install in the worktree failed. Install the ${
|
|
4994
|
+
`\u26A0\uFE0F Dependency install in the worktree failed. Install the ${[...failedInstalls].join(" and ")} dependencies in the worktree before the command below, or it will fail on a missing package.`
|
|
4929
4995
|
);
|
|
4930
4996
|
}
|
|
4931
4997
|
return {
|
|
@@ -57,8 +57,3 @@ Django, Laravel, Symfony — and any backend with no JavaScript build.
|
|
|
57
57
|
- For InstantSearch, import the client from `algoliasearch/lite` (smaller bundle and
|
|
58
58
|
correct types).
|
|
59
59
|
- Every record needs an `objectID`. `saveObjects` auto-batches in groups of 1,000.
|
|
60
|
-
|
|
61
|
-
## Other files
|
|
62
|
-
|
|
63
|
-
- `search-single-index.md` — direct/manual search via the core client
|
|
64
|
-
(`searchSingleIndex`), for cases where InstantSearch is not used.
|
package/package.json
CHANGED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Direct search with the core client (v5)
|
|
2
|
-
|
|
3
|
-
Use this only when NOT using InstantSearch (e.g. a custom search box, a server
|
|
4
|
-
route, or programmatic queries). For UI, prefer `instantsearch-setup-<framework>.md`.
|
|
5
|
-
|
|
6
|
-
## Client
|
|
7
|
-
|
|
8
|
-
```ts
|
|
9
|
-
import { algoliasearch } from 'algoliasearch'
|
|
10
|
-
|
|
11
|
-
const client = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
In v5 there is no `client.initIndex(...)`. Index methods take the index name as a
|
|
15
|
-
parameter on the client. Every method takes a single options object.
|
|
16
|
-
|
|
17
|
-
## Search a single index
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
const { hits, nbHits } = await client.searchSingleIndex({
|
|
21
|
-
indexName: 'INDEX_NAME',
|
|
22
|
-
searchParams: { query: 'shoes', hitsPerPage: 20, page: 0 },
|
|
23
|
-
})
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Search multiple indices / queries in one request
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
const { results } = await client.search({
|
|
30
|
-
requests: [
|
|
31
|
-
{ indexName: 'INDEX_NAME', query: 'shoes' },
|
|
32
|
-
{ indexName: 'OTHER_INDEX', query: 'shoes' },
|
|
33
|
-
],
|
|
34
|
-
})
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Notes
|
|
38
|
-
|
|
39
|
-
- `searchSingleIndex` returns up to 1,000 hits. For larger exports use the `browse`
|
|
40
|
-
operation instead.
|
|
41
|
-
- Keep using a **search-only** key for any client-exposed search. Use an admin key
|
|
42
|
-
only in trusted server-side code.
|