@algolia/wizard 0.15.0 → 0.17.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 +288 -114
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2246,7 +2246,7 @@ async function ensureApplication() {
|
|
|
2246
2246
|
}
|
|
2247
2247
|
|
|
2248
2248
|
// src/workflows/default.ts
|
|
2249
|
-
import { z as
|
|
2249
|
+
import { z as z29 } from "zod";
|
|
2250
2250
|
|
|
2251
2251
|
// src/actions/listIndices.ts
|
|
2252
2252
|
import { z as z5 } from "zod";
|
|
@@ -2507,8 +2507,8 @@ function writeFileTool(ctx) {
|
|
|
2507
2507
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2508
2508
|
import { tool as tool6 } from "ai";
|
|
2509
2509
|
import z13 from "zod";
|
|
2510
|
-
import { mkdir as mkdir4, readFile as
|
|
2511
|
-
import { dirname as dirname5 } from "node:path";
|
|
2510
|
+
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
2511
|
+
import { dirname as dirname5, relative as relative3 } from "node:path";
|
|
2512
2512
|
|
|
2513
2513
|
// src/lib/algoliaApiKey.ts
|
|
2514
2514
|
import { z as z12 } from "zod";
|
|
@@ -2690,6 +2690,79 @@ async function resolveSearchOnlyKey(index, appId, envKey) {
|
|
|
2690
2690
|
);
|
|
2691
2691
|
}
|
|
2692
2692
|
|
|
2693
|
+
// src/lib/gitignore.ts
|
|
2694
|
+
import { execFile } from "node:child_process";
|
|
2695
|
+
import { lstat as lstat2, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
2696
|
+
import { join as join8, relative as relative2 } from "node:path";
|
|
2697
|
+
var GIT_ENV_OVERRIDES = [
|
|
2698
|
+
"GIT_DIR",
|
|
2699
|
+
"GIT_WORK_TREE",
|
|
2700
|
+
"GIT_INDEX_FILE",
|
|
2701
|
+
"GIT_OBJECT_DIRECTORY",
|
|
2702
|
+
"GIT_COMMON_DIR"
|
|
2703
|
+
];
|
|
2704
|
+
function gitSucceeds(root, args) {
|
|
2705
|
+
const env = { ...process.env };
|
|
2706
|
+
for (const key of GIT_ENV_OVERRIDES) delete env[key];
|
|
2707
|
+
return new Promise((resolve4) => {
|
|
2708
|
+
execFile("git", ["-C", root, ...args], { env }, (err) => {
|
|
2709
|
+
if (!err) return resolve4(true);
|
|
2710
|
+
resolve4(
|
|
2711
|
+
err.code === 1 ? false : void 0
|
|
2712
|
+
);
|
|
2713
|
+
});
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
function isIgnoredByRule(root, relPath) {
|
|
2717
|
+
return gitSucceeds(root, ["check-ignore", "-q", "--no-index", "--", relPath]);
|
|
2718
|
+
}
|
|
2719
|
+
function isTracked(root, relPath) {
|
|
2720
|
+
return gitSucceeds(root, ["ls-files", "--error-unmatch", "--", relPath]);
|
|
2721
|
+
}
|
|
2722
|
+
async function inspect(root, target) {
|
|
2723
|
+
const relPath = relative2(root, target);
|
|
2724
|
+
if (!relPath || relPath.startsWith("..")) {
|
|
2725
|
+
return { ignoredByRule: void 0, tracked: false };
|
|
2726
|
+
}
|
|
2727
|
+
const ignoredByRule = await isIgnoredByRule(root, relPath);
|
|
2728
|
+
if (ignoredByRule === void 0) {
|
|
2729
|
+
logger.warn(
|
|
2730
|
+
{ root, relPath },
|
|
2731
|
+
"gitignore: git check-ignore could not answer; not reporting on this path"
|
|
2732
|
+
);
|
|
2733
|
+
return { ignoredByRule, tracked: false };
|
|
2734
|
+
}
|
|
2735
|
+
return { ignoredByRule, tracked: await isTracked(root, relPath) };
|
|
2736
|
+
}
|
|
2737
|
+
async function ensureGitIgnored(root, target) {
|
|
2738
|
+
const { ignoredByRule, tracked } = await inspect(root, target);
|
|
2739
|
+
if (ignoredByRule === void 0) return "unknown";
|
|
2740
|
+
if (ignoredByRule) return tracked ? "tracked" : "covered";
|
|
2741
|
+
const pattern = relative2(root, target);
|
|
2742
|
+
const gitIgnore = join8(root, ".gitignore");
|
|
2743
|
+
try {
|
|
2744
|
+
const link = await lstat2(gitIgnore).catch(() => null);
|
|
2745
|
+
if (link?.isSymbolicLink()) {
|
|
2746
|
+
logger.warn(
|
|
2747
|
+
{ gitIgnore },
|
|
2748
|
+
"gitignore: root .gitignore is a symlink; not writing to it"
|
|
2749
|
+
);
|
|
2750
|
+
return "unknown";
|
|
2751
|
+
}
|
|
2752
|
+
const existing = link ? await readFile5(gitIgnore, "utf8") : "";
|
|
2753
|
+
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
2754
|
+
await writeFile4(gitIgnore, `${existing}${prefix}${pattern}
|
|
2755
|
+
`, "utf8");
|
|
2756
|
+
return tracked ? "tracked" : "added";
|
|
2757
|
+
} catch (err) {
|
|
2758
|
+
logger.warn(
|
|
2759
|
+
{ gitIgnore, pattern, err: err.message },
|
|
2760
|
+
"gitignore: could not add the pattern; continuing"
|
|
2761
|
+
);
|
|
2762
|
+
return "unknown";
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2693
2766
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2694
2767
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2695
2768
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
@@ -2722,7 +2795,7 @@ function upsertEnv(content, name, value) {
|
|
|
2722
2795
|
}
|
|
2723
2796
|
function writeCredentialsTool(ctx) {
|
|
2724
2797
|
return tool6({
|
|
2725
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file. ${INDEX_NAME_VAR} is always set to this run's target index, replacing any value already there. An ${APP_ID_VAR} or ${API_KEY_VAR} the file already gives a value is left untouched; a missing or blank one is filled in when it can be paired with the selected application.`,
|
|
2798
|
+
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file. ${INDEX_NAME_VAR} is always set to this run's target index, replacing any value already there. An ${APP_ID_VAR} or ${API_KEY_VAR} the file already gives a value is left untouched; a missing or blank one is filled in when it can be paired with the selected application. The env file is added to .gitignore automatically; do not edit .gitignore yourself.`,
|
|
2726
2799
|
inputSchema: z13.object({
|
|
2727
2800
|
filePath: z13.string().describe(
|
|
2728
2801
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
@@ -2743,7 +2816,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2743
2816
|
return `Refused: ${resolved2.target} is outside the repo root (${ctx.root}).`;
|
|
2744
2817
|
}
|
|
2745
2818
|
try {
|
|
2746
|
-
existing = await
|
|
2819
|
+
existing = await readFile6(resolved2.target, "utf8");
|
|
2747
2820
|
} catch (err) {
|
|
2748
2821
|
if (err.code !== "ENOENT") throw err;
|
|
2749
2822
|
}
|
|
@@ -2782,6 +2855,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2782
2855
|
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2783
2856
|
}
|
|
2784
2857
|
}
|
|
2858
|
+
let wrote;
|
|
2785
2859
|
try {
|
|
2786
2860
|
const updated = [
|
|
2787
2861
|
...credentials,
|
|
@@ -2793,7 +2867,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2793
2867
|
existing
|
|
2794
2868
|
);
|
|
2795
2869
|
await mkdir4(dirname5(resolved2.target), { recursive: true });
|
|
2796
|
-
await
|
|
2870
|
+
await writeFile5(resolved2.target, updated, "utf8");
|
|
2797
2871
|
const sentences = [
|
|
2798
2872
|
`Wrote ${[...credentials.map(([name]) => name), INDEX_NAME_VAR].join(", ")} to ${filePath}.`
|
|
2799
2873
|
];
|
|
@@ -2807,19 +2881,33 @@ function writeCredentialsTool(ctx) {
|
|
|
2807
2881
|
`Skipped ${present.join(" and ")}: already defined there.`
|
|
2808
2882
|
);
|
|
2809
2883
|
}
|
|
2810
|
-
|
|
2884
|
+
wrote = [...sentences, ...notes].join(" ");
|
|
2811
2885
|
} catch (err) {
|
|
2812
2886
|
return `Error writing credentials to ${filePath}: ${err.message}`;
|
|
2813
2887
|
}
|
|
2888
|
+
return wrote + await gitIgnoreOutcome(ctx, resolved2.target);
|
|
2814
2889
|
}
|
|
2815
2890
|
});
|
|
2816
2891
|
}
|
|
2892
|
+
async function gitIgnoreOutcome(ctx, target) {
|
|
2893
|
+
const name = relative3(ctx.root, target);
|
|
2894
|
+
switch (await ensureGitIgnored(ctx.root, target)) {
|
|
2895
|
+
case "added":
|
|
2896
|
+
return ` Added "${name}" to .gitignore so the credentials are not stageable.`;
|
|
2897
|
+
case "tracked":
|
|
2898
|
+
return ` Warning: ${name} is already tracked by git, and a .gitignore rule cannot un-stage it. Tell the user with notifyUser to run "git rm --cached ${name}" before committing.`;
|
|
2899
|
+
case "unknown":
|
|
2900
|
+
return ` Warning: could not confirm ${name} is gitignored. Tell the user with notifyUser to check before committing.`;
|
|
2901
|
+
case "covered":
|
|
2902
|
+
return "";
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2817
2905
|
|
|
2818
2906
|
// src/lib/tools/searchFiles.ts
|
|
2819
2907
|
import { tool as tool7 } from "ai";
|
|
2820
2908
|
import z14 from "zod";
|
|
2821
|
-
import { readdir as readdir2, readFile as
|
|
2822
|
-
import { join as
|
|
2909
|
+
import { readdir as readdir2, readFile as readFile7 } from "node:fs/promises";
|
|
2910
|
+
import { join as join9 } from "node:path";
|
|
2823
2911
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2824
2912
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2825
2913
|
"node_modules",
|
|
@@ -2834,7 +2922,7 @@ async function walkFiles(dir) {
|
|
|
2834
2922
|
const out = [];
|
|
2835
2923
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2836
2924
|
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
2837
|
-
const full =
|
|
2925
|
+
const full = join9(dir, e.name);
|
|
2838
2926
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2839
2927
|
else if (e.isFile()) out.push(full);
|
|
2840
2928
|
}
|
|
@@ -2867,7 +2955,7 @@ function searchFilesTool(ctx) {
|
|
|
2867
2955
|
for (const file of await walkFiles(resolved2.target)) {
|
|
2868
2956
|
let content;
|
|
2869
2957
|
try {
|
|
2870
|
-
content = await
|
|
2958
|
+
content = await readFile7(file, "utf8");
|
|
2871
2959
|
} catch {
|
|
2872
2960
|
continue;
|
|
2873
2961
|
}
|
|
@@ -2890,7 +2978,16 @@ function searchFilesTool(ctx) {
|
|
|
2890
2978
|
// src/lib/tools/runShell.ts
|
|
2891
2979
|
import { tool as tool8 } from "ai";
|
|
2892
2980
|
import z15 from "zod";
|
|
2893
|
-
import { relative as
|
|
2981
|
+
import { relative as relative4 } from "node:path";
|
|
2982
|
+
|
|
2983
|
+
// src/lib/tools/utils/prompt.ts
|
|
2984
|
+
var chain = Promise.resolve();
|
|
2985
|
+
function serializePrompt(work) {
|
|
2986
|
+
const result = chain.then(work);
|
|
2987
|
+
chain = result.catch(() => {
|
|
2988
|
+
});
|
|
2989
|
+
return result;
|
|
2990
|
+
}
|
|
2894
2991
|
|
|
2895
2992
|
// src/lib/tools/utils/runShell.ts
|
|
2896
2993
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -2922,7 +3019,8 @@ function createToolContext(limits = DEFAULT_TOOL_LIMITS, cwd = process.cwd(), sh
|
|
|
2922
3019
|
cwd,
|
|
2923
3020
|
limits: { ...limits },
|
|
2924
3021
|
counts: { list: 0, search: 0, read: 0, shell: 0 },
|
|
2925
|
-
shell: shell2
|
|
3022
|
+
shell: shell2,
|
|
3023
|
+
reviewed: []
|
|
2926
3024
|
};
|
|
2927
3025
|
}
|
|
2928
3026
|
|
|
@@ -2987,7 +3085,7 @@ function runShell(command, opts) {
|
|
|
2987
3085
|
// src/lib/tools/runShell.ts
|
|
2988
3086
|
function storeApproval(root) {
|
|
2989
3087
|
return async (req) => {
|
|
2990
|
-
const rel =
|
|
3088
|
+
const rel = relative4(root, req.cwd);
|
|
2991
3089
|
const answer = await useWizard.getState().requestUserInput({
|
|
2992
3090
|
prompt: "Run this command?",
|
|
2993
3091
|
promptType: "commandApproval",
|
|
@@ -3000,13 +3098,6 @@ function storeApproval(root) {
|
|
|
3000
3098
|
return answer === "approve" ? "approve" : "reject";
|
|
3001
3099
|
};
|
|
3002
3100
|
}
|
|
3003
|
-
var shellChain = Promise.resolve();
|
|
3004
|
-
function serialize(work) {
|
|
3005
|
-
const result = shellChain.then(work);
|
|
3006
|
-
shellChain = result.catch(() => {
|
|
3007
|
-
});
|
|
3008
|
-
return result;
|
|
3009
|
-
}
|
|
3010
3101
|
async function approveAndRun(ctx, command, cwd, explanation) {
|
|
3011
3102
|
const decision = await ctx.shell.approve({ command, cwd, explanation });
|
|
3012
3103
|
if (decision === "reject") {
|
|
@@ -3070,20 +3161,81 @@ function runShellTool(ctx) {
|
|
|
3070
3161
|
const resolved2 = resolveInRoot(ctx, cwd ?? ".");
|
|
3071
3162
|
if (!resolved2.ok) return resolved2.error;
|
|
3072
3163
|
logger.info({ command, cwd: resolved2.target }, "called runShell tool");
|
|
3073
|
-
return
|
|
3164
|
+
return serializePrompt(
|
|
3074
3165
|
() => approveAndRun(ctx, command, resolved2.target, explanation)
|
|
3075
3166
|
);
|
|
3076
3167
|
}
|
|
3077
3168
|
});
|
|
3078
3169
|
}
|
|
3079
3170
|
|
|
3171
|
+
// src/lib/tools/reviewScript.ts
|
|
3172
|
+
import { tool as tool9 } from "ai";
|
|
3173
|
+
import z16 from "zod";
|
|
3174
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
3175
|
+
import { relative as relative5 } from "node:path";
|
|
3176
|
+
|
|
3177
|
+
// src/lib/editor.ts
|
|
3178
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3179
|
+
function openInEditor(filePath) {
|
|
3180
|
+
const { command, args } = process.platform === "darwin" ? (
|
|
3181
|
+
// -t forces the default *text* editor; plain `open` on an extension
|
|
3182
|
+
// with no handler (.mjs, .py) pops a "choose an application" dialog.
|
|
3183
|
+
{ command: "open", args: ["-t", filePath] }
|
|
3184
|
+
) : process.platform === "win32" ? { command: "cmd", args: ["/c", "start", "", filePath] } : { command: "xdg-open", args: [filePath] };
|
|
3185
|
+
try {
|
|
3186
|
+
const child = spawn3(command, args, { stdio: "ignore", detached: true });
|
|
3187
|
+
child.on("error", () => {
|
|
3188
|
+
});
|
|
3189
|
+
child.unref();
|
|
3190
|
+
} catch {
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
// src/lib/tools/reviewScript.ts
|
|
3195
|
+
function reviewScriptTool(ctx) {
|
|
3196
|
+
return tool9({
|
|
3197
|
+
description: "Show a script you have written to the user and wait while they read it. Call this as soon as the script is finished and before you run it for the first time, so nothing with a side effect happens behind their back. It opens the file in their editor and returns once they have confirmed.",
|
|
3198
|
+
inputSchema: z16.object({
|
|
3199
|
+
filePath: z16.string().describe(
|
|
3200
|
+
"Path to the script to review, relative to the project root."
|
|
3201
|
+
)
|
|
3202
|
+
}),
|
|
3203
|
+
execute: async ({ filePath }) => {
|
|
3204
|
+
logger.info({ filePath }, "called reviewScript tool");
|
|
3205
|
+
const resolved2 = resolveInRoot(ctx, filePath);
|
|
3206
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
3207
|
+
const isFile = await stat2(resolved2.target).then(
|
|
3208
|
+
(s) => s.isFile(),
|
|
3209
|
+
() => false
|
|
3210
|
+
);
|
|
3211
|
+
if (!isFile) {
|
|
3212
|
+
return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
|
|
3213
|
+
}
|
|
3214
|
+
return serializePrompt(async () => {
|
|
3215
|
+
openInEditor(resolved2.target);
|
|
3216
|
+
await useWizard.getState().requestUserInput({
|
|
3217
|
+
prompt: "",
|
|
3218
|
+
promptType: "enterToContinue",
|
|
3219
|
+
options: [],
|
|
3220
|
+
messages: [
|
|
3221
|
+
`Please review ${relative5(ctx.root, resolved2.target)} before it runs:`,
|
|
3222
|
+
resolved2.target
|
|
3223
|
+
]
|
|
3224
|
+
});
|
|
3225
|
+
ctx.reviewed.push(resolved2.target);
|
|
3226
|
+
return "The user has reviewed the script. You may run it now.";
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3080
3232
|
// src/lib/tools/generateRecord.ts
|
|
3081
|
-
import { tool as
|
|
3233
|
+
import { tool as tool10, generateText, Output, NoObjectGeneratedError } from "ai";
|
|
3082
3234
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
3083
3235
|
import { nanoid as nanoid2 } from "nanoid";
|
|
3084
|
-
import { mkdir as mkdir5, writeFile as
|
|
3236
|
+
import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
|
|
3085
3237
|
import { dirname as dirname6 } from "node:path";
|
|
3086
|
-
import
|
|
3238
|
+
import z17 from "zod";
|
|
3087
3239
|
var DATA_DIR = ".algolia-wizard/data";
|
|
3088
3240
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
3089
3241
|
var MAX_RECORDS = 100;
|
|
@@ -3093,19 +3245,19 @@ var anthropic = createAnthropic({
|
|
|
3093
3245
|
apiKey: process.env.PROVIDER_API_KEY ?? ""
|
|
3094
3246
|
});
|
|
3095
3247
|
function generateRecordTool(ctx) {
|
|
3096
|
-
return
|
|
3248
|
+
return tool10({
|
|
3097
3249
|
description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
|
|
3098
|
-
inputSchema:
|
|
3099
|
-
entityName:
|
|
3100
|
-
attributes:
|
|
3101
|
-
count:
|
|
3102
|
-
hint:
|
|
3250
|
+
inputSchema: z17.object({
|
|
3251
|
+
entityName: z17.string().describe("Name of the entity to generate records for."),
|
|
3252
|
+
attributes: z17.array(z17.string()).describe("Attribute names each record must contain."),
|
|
3253
|
+
count: z17.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
3254
|
+
hint: z17.string().optional().describe("Optional context to steer realistic values.")
|
|
3103
3255
|
}),
|
|
3104
3256
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
3105
3257
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
3106
3258
|
try {
|
|
3107
|
-
const value =
|
|
3108
|
-
const recordSchema =
|
|
3259
|
+
const value = z17.union([z17.string(), z17.number(), z17.boolean(), z17.null()]);
|
|
3260
|
+
const recordSchema = z17.object(
|
|
3109
3261
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
3110
3262
|
);
|
|
3111
3263
|
const generateBatch = async (batchCount) => {
|
|
@@ -3115,8 +3267,8 @@ function generateRecordTool(ctx) {
|
|
|
3115
3267
|
const { output } = await generateText({
|
|
3116
3268
|
model: anthropic(RECORD_MODEL),
|
|
3117
3269
|
output: Output.object({
|
|
3118
|
-
schema:
|
|
3119
|
-
records:
|
|
3270
|
+
schema: z17.object({
|
|
3271
|
+
records: z17.array(recordSchema).length(batchCount)
|
|
3120
3272
|
})
|
|
3121
3273
|
}),
|
|
3122
3274
|
prompt: [
|
|
@@ -3158,7 +3310,7 @@ function generateRecordTool(ctx) {
|
|
|
3158
3310
|
return `Refused: ${resolved2.target} is outside the repo root (${ctx.root}).`;
|
|
3159
3311
|
}
|
|
3160
3312
|
await mkdir5(dirname6(resolved2.target), { recursive: true });
|
|
3161
|
-
await
|
|
3313
|
+
await writeFile6(resolved2.target, JSON.stringify(records, null, 2), "utf8");
|
|
3162
3314
|
logger.info({ entityName, count: records.length, relPath }, "generateRecord wrote records to disk");
|
|
3163
3315
|
return {
|
|
3164
3316
|
filePath: relPath,
|
|
@@ -3173,13 +3325,13 @@ function generateRecordTool(ctx) {
|
|
|
3173
3325
|
}
|
|
3174
3326
|
|
|
3175
3327
|
// src/lib/tools/notifyUser.ts
|
|
3176
|
-
import { tool as
|
|
3177
|
-
import
|
|
3328
|
+
import { tool as tool11 } from "ai";
|
|
3329
|
+
import z18 from "zod";
|
|
3178
3330
|
function notifyUserTool() {
|
|
3179
|
-
return
|
|
3331
|
+
return tool11({
|
|
3180
3332
|
description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
|
|
3181
|
-
inputSchema:
|
|
3182
|
-
message:
|
|
3333
|
+
inputSchema: z18.object({
|
|
3334
|
+
message: z18.string().describe(
|
|
3183
3335
|
"Short, plain-language description of what you are doing now."
|
|
3184
3336
|
)
|
|
3185
3337
|
}),
|
|
@@ -3223,6 +3375,7 @@ function createTools(ctx, { output, tools }) {
|
|
|
3223
3375
|
),
|
|
3224
3376
|
searchFiles: withLogging("searchFiles", searchFilesTool(ctx)),
|
|
3225
3377
|
runShell: withLogging("runShell", runShellTool(ctx)),
|
|
3378
|
+
reviewScript: withLogging("reviewScript", reviewScriptTool(ctx)),
|
|
3226
3379
|
generateRecord: withLogging("generateRecord", generateRecordTool(ctx)),
|
|
3227
3380
|
notifyUser: withLogging("notifyUser", notifyUserTool())
|
|
3228
3381
|
};
|
|
@@ -3344,10 +3497,10 @@ async function runAgent(req) {
|
|
|
3344
3497
|
}
|
|
3345
3498
|
|
|
3346
3499
|
// src/actions/detectLanguage.ts
|
|
3347
|
-
import
|
|
3348
|
-
var detectLanguageSchema =
|
|
3349
|
-
languages:
|
|
3350
|
-
frameworks:
|
|
3500
|
+
import z21 from "zod";
|
|
3501
|
+
var detectLanguageSchema = z21.object({
|
|
3502
|
+
languages: z21.array(z21.object({ name: z21.string(), version: z21.string() })),
|
|
3503
|
+
frameworks: z21.array(z21.object({ name: z21.string(), version: z21.string() }))
|
|
3351
3504
|
});
|
|
3352
3505
|
var detectLanguage = () => runAgent({
|
|
3353
3506
|
instructions: [
|
|
@@ -3365,31 +3518,31 @@ var detectLanguage = () => runAgent({
|
|
|
3365
3518
|
});
|
|
3366
3519
|
|
|
3367
3520
|
// src/actions/analyzeCodebase.ts
|
|
3368
|
-
import
|
|
3521
|
+
import z22 from "zod";
|
|
3369
3522
|
var READONLY_TOOLS = [
|
|
3370
3523
|
"listFiles",
|
|
3371
3524
|
"changeDirectory",
|
|
3372
3525
|
"readFile",
|
|
3373
3526
|
"searchFiles"
|
|
3374
3527
|
];
|
|
3375
|
-
var ingestionAnalysisSchema =
|
|
3376
|
-
ingestionAnalysis:
|
|
3377
|
-
|
|
3378
|
-
name:
|
|
3379
|
-
paths:
|
|
3528
|
+
var ingestionAnalysisSchema = z22.object({
|
|
3529
|
+
ingestionAnalysis: z22.array(
|
|
3530
|
+
z22.object({
|
|
3531
|
+
name: z22.string(),
|
|
3532
|
+
paths: z22.array(z22.string()),
|
|
3380
3533
|
// indexable fields the agent found for this entity
|
|
3381
|
-
attributes:
|
|
3534
|
+
attributes: z22.array(z22.string())
|
|
3382
3535
|
})
|
|
3383
3536
|
)
|
|
3384
3537
|
});
|
|
3385
|
-
var searchImplementationAnalysisSchema =
|
|
3386
|
-
searchImplementationAnalysis:
|
|
3538
|
+
var searchImplementationAnalysisSchema = z22.object({
|
|
3539
|
+
searchImplementationAnalysis: z22.string()
|
|
3387
3540
|
});
|
|
3388
|
-
var verificationSchema =
|
|
3389
|
-
verification:
|
|
3541
|
+
var verificationSchema = z22.object({
|
|
3542
|
+
verification: z22.array(z22.string())
|
|
3390
3543
|
});
|
|
3391
3544
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
3392
|
-
var analyzeCodebaseSchema =
|
|
3545
|
+
var analyzeCodebaseSchema = z22.object({
|
|
3393
3546
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3394
3547
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
3395
3548
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -3451,7 +3604,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3451
3604
|
// package.json
|
|
3452
3605
|
var package_default = {
|
|
3453
3606
|
name: "@algolia/wizard",
|
|
3454
|
-
version: "0.
|
|
3607
|
+
version: "0.17.0",
|
|
3455
3608
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3456
3609
|
type: "module",
|
|
3457
3610
|
engines: {
|
|
@@ -3570,8 +3723,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
3570
3723
|
}
|
|
3571
3724
|
|
|
3572
3725
|
// src/actions/confirmLanguage.ts
|
|
3573
|
-
import
|
|
3574
|
-
var confirmLanguageSchema =
|
|
3726
|
+
import z24 from "zod";
|
|
3727
|
+
var confirmLanguageSchema = z24.object({
|
|
3575
3728
|
languages: detectLanguageSchema.shape.languages
|
|
3576
3729
|
});
|
|
3577
3730
|
async function confirmLanguage(ctx) {
|
|
@@ -3592,8 +3745,8 @@ async function confirmLanguage(ctx) {
|
|
|
3592
3745
|
}
|
|
3593
3746
|
|
|
3594
3747
|
// src/actions/confirmFramework.ts
|
|
3595
|
-
import
|
|
3596
|
-
var confirmFrameworkSchema =
|
|
3748
|
+
import z25 from "zod";
|
|
3749
|
+
var confirmFrameworkSchema = z25.object({
|
|
3597
3750
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
3598
3751
|
});
|
|
3599
3752
|
var CURATED_FRAMEWORKS = [
|
|
@@ -3726,8 +3879,8 @@ async function promptUser(ctx, params) {
|
|
|
3726
3879
|
}
|
|
3727
3880
|
|
|
3728
3881
|
// src/actions/confirmEntities.ts
|
|
3729
|
-
import
|
|
3730
|
-
var confirmEntitiesSchema =
|
|
3882
|
+
import z26 from "zod";
|
|
3883
|
+
var confirmEntitiesSchema = z26.object({
|
|
3731
3884
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
3732
3885
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3733
3886
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3797,15 +3950,15 @@ async function confirmEntities(ctx) {
|
|
|
3797
3950
|
}
|
|
3798
3951
|
|
|
3799
3952
|
// src/actions/review.ts
|
|
3800
|
-
import { z as
|
|
3801
|
-
var reviewSchema =
|
|
3953
|
+
import { z as z27 } from "zod";
|
|
3954
|
+
var reviewSchema = z27.object({
|
|
3802
3955
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3803
3956
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3804
3957
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3805
3958
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3806
|
-
summaryPoints:
|
|
3807
|
-
reviewPrompt:
|
|
3808
|
-
nextSteps:
|
|
3959
|
+
summaryPoints: z27.array(z27.string()),
|
|
3960
|
+
reviewPrompt: z27.string(),
|
|
3961
|
+
nextSteps: z27.array(z27.string())
|
|
3809
3962
|
});
|
|
3810
3963
|
function formatCompletedSteps(steps) {
|
|
3811
3964
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3856,18 +4009,19 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3856
4009
|
};
|
|
3857
4010
|
|
|
3858
4011
|
// src/actions/implement.ts
|
|
3859
|
-
import
|
|
4012
|
+
import z28 from "zod";
|
|
4013
|
+
import { join as join12 } from "node:path";
|
|
3860
4014
|
|
|
3861
4015
|
// src/lib/worktree.ts
|
|
3862
|
-
import { execFile } from "node:child_process";
|
|
3863
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3864
|
-
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as
|
|
4016
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4017
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile8, stat as stat3, writeFile as writeFile7 } from "node:fs/promises";
|
|
4018
|
+
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "node:path";
|
|
3865
4019
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
3866
4020
|
var MAX_WIZARD_WORKTREES = 3;
|
|
3867
4021
|
var WIZARD_BRANCH_PREFIX = "wizard/implement-";
|
|
3868
4022
|
function git(args) {
|
|
3869
4023
|
return new Promise((resolve4, reject) => {
|
|
3870
|
-
|
|
4024
|
+
execFile2("git", args, { maxBuffer: MAX_BUFFER }, (err, stdout, stderr) => {
|
|
3871
4025
|
if (err)
|
|
3872
4026
|
return reject(
|
|
3873
4027
|
new Error(
|
|
@@ -3892,7 +4046,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3892
4046
|
return out.trim().length > 0;
|
|
3893
4047
|
}
|
|
3894
4048
|
async function pruneOldWorktrees(repoRoot) {
|
|
3895
|
-
const dir =
|
|
4049
|
+
const dir = join10(stateDir(repoRoot), "worktrees");
|
|
3896
4050
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3897
4051
|
for (const slug of stale) {
|
|
3898
4052
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3903,7 +4057,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3903
4057
|
"worktree",
|
|
3904
4058
|
"remove",
|
|
3905
4059
|
"--force",
|
|
3906
|
-
|
|
4060
|
+
join10(dir, slug)
|
|
3907
4061
|
]);
|
|
3908
4062
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3909
4063
|
} catch (err) {
|
|
@@ -3917,7 +4071,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3917
4071
|
async function createWorktree(repoRoot) {
|
|
3918
4072
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3919
4073
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3920
|
-
const path =
|
|
4074
|
+
const path = join10(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3921
4075
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3922
4076
|
await pruneOldWorktrees(repoRoot);
|
|
3923
4077
|
await mkdir6(dirname7(path), { recursive: true });
|
|
@@ -3931,14 +4085,14 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3931
4085
|
}
|
|
3932
4086
|
const source = isAbsolute2(trimmed) ? trimmed : resolve3(repoRoot, trimmed);
|
|
3933
4087
|
try {
|
|
3934
|
-
if (!(await
|
|
4088
|
+
if (!(await stat3(source)).isFile()) {
|
|
3935
4089
|
return { ok: false, reason: `"${sourcePath}" is not a file` };
|
|
3936
4090
|
}
|
|
3937
4091
|
} catch {
|
|
3938
4092
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3939
4093
|
}
|
|
3940
|
-
const relPath =
|
|
3941
|
-
const dest =
|
|
4094
|
+
const relPath = join10(ingestDir, basename2(source));
|
|
4095
|
+
const dest = join10(worktreePath, relPath);
|
|
3942
4096
|
try {
|
|
3943
4097
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3944
4098
|
await copyFile(source, dest);
|
|
@@ -3956,7 +4110,7 @@ function hasEnvVar(content, name) {
|
|
|
3956
4110
|
async function readEnvVar(worktreePath, name) {
|
|
3957
4111
|
let content;
|
|
3958
4112
|
try {
|
|
3959
|
-
content = await
|
|
4113
|
+
content = await readFile8(join10(worktreePath, ".env"), "utf8");
|
|
3960
4114
|
} catch (err) {
|
|
3961
4115
|
if (err.code !== "ENOENT") throw err;
|
|
3962
4116
|
return void 0;
|
|
@@ -3971,10 +4125,10 @@ async function readEnvVar(worktreePath, name) {
|
|
|
3971
4125
|
return value;
|
|
3972
4126
|
}
|
|
3973
4127
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3974
|
-
const target =
|
|
4128
|
+
const target = join10(worktreePath, ".env");
|
|
3975
4129
|
let existing = "";
|
|
3976
4130
|
try {
|
|
3977
|
-
existing = await
|
|
4131
|
+
existing = await readFile8(target, "utf8");
|
|
3978
4132
|
} catch (err) {
|
|
3979
4133
|
if (err.code !== "ENOENT") throw err;
|
|
3980
4134
|
}
|
|
@@ -3983,7 +4137,7 @@ async function writeSearchEnvValues(worktreePath, vars) {
|
|
|
3983
4137
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
3984
4138
|
const lines = missing.map(({ name, value }) => `${name}=${value}
|
|
3985
4139
|
`).join("");
|
|
3986
|
-
await
|
|
4140
|
+
await writeFile7(target, existing + prefix + lines, "utf8");
|
|
3987
4141
|
return missing.map((v) => v.name);
|
|
3988
4142
|
}
|
|
3989
4143
|
async function listChangedFiles(worktreePath) {
|
|
@@ -4044,13 +4198,13 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
4044
4198
|
|
|
4045
4199
|
// src/lib/algoliaDocs.ts
|
|
4046
4200
|
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
4047
|
-
import { dirname as dirname8, join as
|
|
4201
|
+
import { dirname as dirname8, join as join11 } from "node:path";
|
|
4048
4202
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4049
|
-
var DOCS_SUBPATH =
|
|
4203
|
+
var DOCS_SUBPATH = join11("docs", "algolia-sdk");
|
|
4050
4204
|
function findDocsDir() {
|
|
4051
4205
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
4052
4206
|
for (; ; ) {
|
|
4053
|
-
const candidate =
|
|
4207
|
+
const candidate = join11(dir, DOCS_SUBPATH);
|
|
4054
4208
|
if (existsSync(candidate)) return candidate;
|
|
4055
4209
|
const parent = dirname8(dir);
|
|
4056
4210
|
if (parent === dir) return void 0;
|
|
@@ -4073,7 +4227,7 @@ function loadAlgoliaDoc(language) {
|
|
|
4073
4227
|
);
|
|
4074
4228
|
return "";
|
|
4075
4229
|
}
|
|
4076
|
-
return readFileSync(
|
|
4230
|
+
return readFileSync(join11(docsDir, files[0]), "utf8").trim();
|
|
4077
4231
|
}
|
|
4078
4232
|
function getNamedDoc(name, language) {
|
|
4079
4233
|
const docsDir = findDocsDir();
|
|
@@ -4081,7 +4235,7 @@ function getNamedDoc(name, language) {
|
|
|
4081
4235
|
logger.warn("docs/algolia-sdk not found");
|
|
4082
4236
|
return "";
|
|
4083
4237
|
}
|
|
4084
|
-
const file =
|
|
4238
|
+
const file = join11(docsDir, `${name}-${language}.md`);
|
|
4085
4239
|
if (!existsSync(file)) {
|
|
4086
4240
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
4087
4241
|
return "";
|
|
@@ -4106,30 +4260,30 @@ function shellQuote(value) {
|
|
|
4106
4260
|
}
|
|
4107
4261
|
|
|
4108
4262
|
// src/actions/implement.ts
|
|
4109
|
-
var implementSchema =
|
|
4110
|
-
filesChanged:
|
|
4111
|
-
summary:
|
|
4112
|
-
worktreePath:
|
|
4113
|
-
ingestCommand:
|
|
4114
|
-
ingestScriptRan:
|
|
4115
|
-
ingestRecordCount:
|
|
4116
|
-
ingestDurationMs:
|
|
4117
|
-
ingestionSource:
|
|
4118
|
-
searchEnvVars:
|
|
4119
|
-
|
|
4120
|
-
name:
|
|
4121
|
-
value:
|
|
4263
|
+
var implementSchema = z28.object({
|
|
4264
|
+
filesChanged: z28.array(z28.string()),
|
|
4265
|
+
summary: z28.string(),
|
|
4266
|
+
worktreePath: z28.string().optional(),
|
|
4267
|
+
ingestCommand: z28.string().optional(),
|
|
4268
|
+
ingestScriptRan: z28.boolean().optional(),
|
|
4269
|
+
ingestRecordCount: z28.number().optional(),
|
|
4270
|
+
ingestDurationMs: z28.number().optional(),
|
|
4271
|
+
ingestionSource: z28.enum(["local", "fileUpload", "generated"]),
|
|
4272
|
+
searchEnvVars: z28.array(
|
|
4273
|
+
z28.object({
|
|
4274
|
+
name: z28.string(),
|
|
4275
|
+
value: z28.string()
|
|
4122
4276
|
})
|
|
4123
4277
|
).optional()
|
|
4124
4278
|
});
|
|
4125
|
-
var implementationOutputSchema =
|
|
4126
|
-
summary:
|
|
4127
|
-
ingestCommand:
|
|
4279
|
+
var implementationOutputSchema = z28.object({
|
|
4280
|
+
summary: z28.string(),
|
|
4281
|
+
ingestCommand: z28.string().optional()
|
|
4128
4282
|
});
|
|
4129
|
-
var verificationOutputSchema =
|
|
4130
|
-
summary:
|
|
4131
|
-
sufficient:
|
|
4132
|
-
additionalInstructions:
|
|
4283
|
+
var verificationOutputSchema = z28.object({
|
|
4284
|
+
summary: z28.string(),
|
|
4285
|
+
sufficient: z28.boolean(),
|
|
4286
|
+
additionalInstructions: z28.string().optional()
|
|
4133
4287
|
});
|
|
4134
4288
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
4135
4289
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -4262,6 +4416,7 @@ function ingestionInstructions(input) {
|
|
|
4262
4416
|
"After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
|
|
4263
4417
|
...algoliaClientDoc(input),
|
|
4264
4418
|
"Install the Algolia client with the project's own package manager via runShell, declaring it in whatever manifest the project uses (e.g. package.json, requirements.txt, Gemfile, go.mod, composer.json) so the dependency is not just installed ad hoc.",
|
|
4419
|
+
"When the script is finished, call reviewScript with its path and wait: running it writes records to a live index, so the developer reads it first. Do not run it before that call returns.",
|
|
4265
4420
|
'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
|
|
4266
4421
|
"The summary should be extremely concise.",
|
|
4267
4422
|
...sourceSpecificInstructions(input)
|
|
@@ -4332,6 +4487,7 @@ var useCaseToolMap = {
|
|
|
4332
4487
|
"writeFile",
|
|
4333
4488
|
"writeCredentials",
|
|
4334
4489
|
"runShell",
|
|
4490
|
+
"reviewScript",
|
|
4335
4491
|
"notifyUser"
|
|
4336
4492
|
],
|
|
4337
4493
|
search: [...FS_READ_TOOLS, "writeFile", "runShell", "notifyUser"],
|
|
@@ -4586,7 +4742,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4586
4742
|
const result = await runImplementationUseCase("ingestion");
|
|
4587
4743
|
summaries.push(formatSummary("ingestion", result.summary));
|
|
4588
4744
|
ingestCommand = result.ingestCommand;
|
|
4589
|
-
const
|
|
4745
|
+
const ingestionContext = ingestionTools ?? searchTools;
|
|
4746
|
+
const executions = ingestionContext.shell.executions;
|
|
4590
4747
|
const {
|
|
4591
4748
|
run: ingestRun,
|
|
4592
4749
|
attempt: ingestAttempt,
|
|
@@ -4595,6 +4752,15 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4595
4752
|
ingestScriptRan = ingestRun != null;
|
|
4596
4753
|
ingestRecordCount = recordCount;
|
|
4597
4754
|
ingestDurationMs = ingestRun?.durationMs;
|
|
4755
|
+
if (ingestScriptRan && ingestionContext.reviewed.length === 0) {
|
|
4756
|
+
summaries.push(
|
|
4757
|
+
"\u26A0\uFE0F The ingestion script ran without being shown to you for review. Read it in the worktree before trusting the index contents."
|
|
4758
|
+
);
|
|
4759
|
+
logger.warn(
|
|
4760
|
+
{ ingestCommand },
|
|
4761
|
+
"implement: ingestion ran without a reviewScript call"
|
|
4762
|
+
);
|
|
4763
|
+
}
|
|
4598
4764
|
if (ingestScriptRan) {
|
|
4599
4765
|
ingestOutcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
4600
4766
|
if (ingestRecordCount != null) {
|
|
@@ -4728,6 +4894,14 @@ ${detail}` : ""}`
|
|
|
4728
4894
|
if (written.length > 0) {
|
|
4729
4895
|
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
4730
4896
|
}
|
|
4897
|
+
const ignored = await ensureGitIgnored(worktree, join12(worktree, ".env"));
|
|
4898
|
+
if (ignored === "added") {
|
|
4899
|
+
summaries.push("Added .env to .gitignore.");
|
|
4900
|
+
} else if (ignored === "tracked") {
|
|
4901
|
+
summaries.push(
|
|
4902
|
+
'\u26A0\uFE0F .env is tracked by git, so a .gitignore rule cannot un-stage it. Run "git rm --cached .env" before committing, or the credentials go into history.'
|
|
4903
|
+
);
|
|
4904
|
+
}
|
|
4731
4905
|
const stale = [];
|
|
4732
4906
|
for (const v of resolvedSearchEnvVars) {
|
|
4733
4907
|
if (written.includes(v.name)) continue;
|
|
@@ -4816,8 +4990,8 @@ var defaultWorkflow = {
|
|
|
4816
4990
|
defineStep({
|
|
4817
4991
|
id: "select-index",
|
|
4818
4992
|
title: "Set up index",
|
|
4819
|
-
outputSchema:
|
|
4820
|
-
selection:
|
|
4993
|
+
outputSchema: z29.object({
|
|
4994
|
+
selection: z29.string()
|
|
4821
4995
|
}),
|
|
4822
4996
|
run: (ctx) => selectIndexStep(ctx)
|
|
4823
4997
|
}),
|
|
@@ -5100,7 +5274,7 @@ function parseCliArgs(argv) {
|
|
|
5100
5274
|
|
|
5101
5275
|
// src/lib/resetState.ts
|
|
5102
5276
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
5103
|
-
import { join as
|
|
5277
|
+
import { join as join13 } from "node:path";
|
|
5104
5278
|
var KEEP = ["wizard.log"];
|
|
5105
5279
|
async function resetProjectState() {
|
|
5106
5280
|
const dir = stateDir();
|
|
@@ -5114,7 +5288,7 @@ async function resetProjectState() {
|
|
|
5114
5288
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
5115
5289
|
await Promise.all(
|
|
5116
5290
|
targets.map(
|
|
5117
|
-
(name) => rm2(
|
|
5291
|
+
(name) => rm2(join13(dir, name), { recursive: true, force: true })
|
|
5118
5292
|
)
|
|
5119
5293
|
);
|
|
5120
5294
|
return { dir, removed: targets };
|