@algolia/wizard 0.33.0-rc.126.239 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +34 -204
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -253,7 +253,6 @@ var useWizard = create((set, get) => ({
253
253
  cliOutput: [],
254
254
  targetIndex: null,
255
255
  writtenFiles: [],
256
- approvedCommands: /* @__PURE__ */ new Set(),
257
256
  logs: [],
258
257
  error: null,
259
258
  inputReq: null,
@@ -349,10 +348,6 @@ var useWizard = create((set, get) => ({
349
348
  setTargetIndex: (index) => set({ targetIndex: index }),
350
349
  recordWrittenFile: (path) => set((s) => ({ writtenFiles: [...s.writtenFiles, path] })),
351
350
  clearWrittenFiles: () => set({ writtenFiles: [] }),
352
- isCommandApproved: (command, cwd) => get().approvedCommands.has(`${cwd}\0${command}`),
353
- approveCommand: (command, cwd) => set((s) => ({
354
- approvedCommands: new Set(s.approvedCommands).add(`${cwd}\0${command}`)
355
- })),
356
351
  logStart: (kind, name, input) => {
357
352
  const id = nanoid();
358
353
  set((s) => ({
@@ -401,7 +396,6 @@ var useWizard = create((set, get) => ({
401
396
  cliOutput: [],
402
397
  targetIndex: null,
403
398
  writtenFiles: [],
404
- approvedCommands: /* @__PURE__ */ new Set(),
405
399
  logs: [],
406
400
  error: null,
407
401
  inputReq: null,
@@ -1026,6 +1020,7 @@ function EnterToContinuePrompt({
1026
1020
  function PromptInput() {
1027
1021
  const { phase, inputReq, submitInput } = useWizard();
1028
1022
  const [draft, setDraft] = useState6("");
1023
+ const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1029
1024
  if (phase === "done" || phase === "error") {
1030
1025
  return /* @__PURE__ */ jsx7(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text9, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1031
1026
  }
@@ -1044,7 +1039,8 @@ function PromptInput() {
1044
1039
  cancelable: inputReq.cancelable,
1045
1040
  error: inputReq.error,
1046
1041
  onSelect: submitInput
1047
- }
1042
+ },
1043
+ promptKey
1048
1044
  ) });
1049
1045
  }
1050
1046
  if (inputReq.promptType === "multiSelect") {
@@ -1059,7 +1055,8 @@ function PromptInput() {
1059
1055
  options: inputReq.options,
1060
1056
  error: inputReq.error,
1061
1057
  onSelect: submitInput
1062
- }
1058
+ },
1059
+ promptKey
1063
1060
  ) });
1064
1061
  }
1065
1062
  if (inputReq.promptType === "notice") {
@@ -1070,7 +1067,8 @@ function PromptInput() {
1070
1067
  messages: inputReq.messages,
1071
1068
  options: ["Continue"],
1072
1069
  onSelect: submitInput
1073
- }
1070
+ },
1071
+ promptKey
1074
1072
  ) });
1075
1073
  }
1076
1074
  if (inputReq.promptType === "enterToContinue") {
@@ -1096,7 +1094,8 @@ function PromptInput() {
1096
1094
  options: labels,
1097
1095
  secondary: inputReq.secondary,
1098
1096
  onSelect: (value) => submitInput(value === labels[0])
1099
- }
1097
+ },
1098
+ promptKey
1100
1099
  ) });
1101
1100
  }
1102
1101
  return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
@@ -2716,8 +2715,8 @@ var selectIndexStep = async (ctx) => {
2716
2715
  };
2717
2716
 
2718
2717
  // src/lib/agent.ts
2719
- import { ToolLoopAgent, hasToolCall, Output as Output3 } from "ai";
2720
- import { createAnthropic as createAnthropic3 } from "@ai-sdk/anthropic";
2718
+ import { ToolLoopAgent, hasToolCall, Output as Output2 } from "ai";
2719
+ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
2721
2720
  import "zod";
2722
2721
 
2723
2722
  // src/lib/tools/index.ts
@@ -3399,8 +3398,7 @@ function searchFilesTool(ctx) {
3399
3398
  }
3400
3399
 
3401
3400
  // src/lib/tools/runShell.ts
3402
- import { tool as tool8, generateText, Output } from "ai";
3403
- import { createAnthropic } from "@ai-sdk/anthropic";
3401
+ import { tool as tool8 } from "ai";
3404
3402
  import z15 from "zod";
3405
3403
  import { relative as relative4 } from "node:path";
3406
3404
 
@@ -3509,10 +3507,8 @@ function runShell(command, opts) {
3509
3507
  // src/lib/tools/runShell.ts
3510
3508
  function storeApproval(root) {
3511
3509
  return async (req) => {
3512
- const store = useWizard.getState();
3513
- if (store.isCommandApproved(req.command, req.cwd)) return "approve";
3514
3510
  const rel = relative4(root, req.cwd);
3515
- const answer = await store.requestUserInput({
3511
+ const answer = await useWizard.getState().requestUserInput({
3516
3512
  prompt: "Run this command?",
3517
3513
  promptType: "commandApproval",
3518
3514
  options: [],
@@ -3521,159 +3517,20 @@ function storeApproval(root) {
3521
3517
  cwd: rel === "" || rel.startsWith("..") ? req.cwd : rel
3522
3518
  }
3523
3519
  });
3524
- if (answer !== "approve") return "reject";
3525
- store.approveCommand(req.command, req.cwd);
3526
- return "approve";
3520
+ return answer === "approve" ? "approve" : "reject";
3527
3521
  };
3528
3522
  }
3529
3523
  var EXPLORATORY_COMMANDS = /* @__PURE__ */ new Set(["ls", "find", "tree", "dir"]);
3530
- function commandSegments(command) {
3531
- return command.split(/&&|;|\|/).map((segment) => segment.trim());
3532
- }
3533
3524
  function isExploratoryCommand(command) {
3534
- return commandSegments(command).map((segment) => segment.split(/\s+/)[0]).some((word) => word !== void 0 && EXPLORATORY_COMMANDS.has(word));
3535
- }
3536
- var READ_ONLY_BINARIES = /* @__PURE__ */ new Set([
3537
- "cat",
3538
- "head",
3539
- "tail",
3540
- "wc",
3541
- "pwd",
3542
- "echo",
3543
- "date",
3544
- "whoami",
3545
- "hostname",
3546
- "uname",
3547
- "which",
3548
- "file",
3549
- "stat",
3550
- "grep",
3551
- "egrep",
3552
- "fgrep",
3553
- "rg",
3554
- "diff"
3555
- ]);
3556
- var READ_ONLY_NO_ARGS_BINARIES = /* @__PURE__ */ new Set(["env", "printenv"]);
3557
- var GIT_READ_ONLY_SUBCOMMANDS = /* @__PURE__ */ new Set([
3558
- "status",
3559
- "log",
3560
- "diff",
3561
- "show",
3562
- "describe",
3563
- "blame",
3564
- "ls-files",
3565
- "rev-parse",
3566
- "cat-file",
3567
- "shortlog",
3568
- "ls-remote"
3569
- ]);
3570
- var VERSION_CHECK_BINARIES = /* @__PURE__ */ new Set([
3571
- "node",
3572
- "tsc",
3573
- "npm",
3574
- "pnpm",
3575
- "yarn",
3576
- "python",
3577
- "python3",
3578
- "ruby",
3579
- "go",
3580
- "cargo",
3581
- "rustc",
3582
- "php",
3583
- "composer",
3584
- "java",
3585
- "mvn",
3586
- "gradle",
3587
- "bundle",
3588
- "git"
3589
- ]);
3590
- var VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v", "-V"]);
3591
- function hasFileRedirect(segment) {
3592
- return segment.replace(/\d>&\d/g, "").includes(">");
3593
- }
3594
- function hasShellInjectionRisk(segment) {
3595
- if (segment.includes("$(") || segment.includes("<(") || segment.includes("`")) {
3596
- return true;
3597
- }
3598
- return segment.replace(/\d>&\d/g, "").includes("&");
3599
- }
3600
- function fastPathSegmentSafety(segment) {
3601
- if (!segment) return true;
3602
- if (hasFileRedirect(segment)) return false;
3603
- if (hasShellInjectionRisk(segment)) return false;
3604
- const [cmd0, ...rest] = segment.split(/\s+/);
3605
- if (cmd0 === void 0) return true;
3606
- if (VERSION_CHECK_BINARIES.has(cmd0) && rest.length === 1 && VERSION_FLAGS.has(rest[0])) {
3607
- return true;
3608
- }
3609
- if (READ_ONLY_BINARIES.has(cmd0)) return true;
3610
- if (READ_ONLY_NO_ARGS_BINARIES.has(cmd0)) {
3611
- return rest.length === 0 ? true : void 0;
3612
- }
3613
- if (cmd0 === "git") {
3614
- return GIT_READ_ONLY_SUBCOMMANDS.has(rest[0] ?? "") ? true : void 0;
3615
- }
3616
- return void 0;
3617
- }
3618
- function fastPathSafety(command) {
3619
- const results = commandSegments(command).map(fastPathSegmentSafety);
3620
- if (results.some((r) => r === false)) return false;
3621
- if (results.every((r) => r === true)) return true;
3622
- return void 0;
3623
- }
3624
- var CLASSIFIER_MODEL = "claude-haiku-4-5";
3625
- var commandSafetySchema = z15.object({
3626
- safe: z15.boolean(),
3627
- reason: z15.string().describe("One short sentence explaining the verdict.")
3628
- });
3629
- function defaultCreateModel() {
3630
- const token = getAuthToken();
3631
- if (!token) {
3632
- throw new Error("Not authenticated: no user token available");
3633
- }
3634
- return createAnthropic({
3635
- apiKey: token,
3636
- baseURL: PROXY_BASE_URL,
3637
- fetch: proxyFetch
3638
- });
3525
+ return command.split(/&&|;|\|/).map((segment) => segment.trim().split(/\s+/)[0]).some((word) => word !== void 0 && EXPLORATORY_COMMANDS.has(word));
3639
3526
  }
3640
- async function classifyCommandSafety(createModel, command, cwd, explanation) {
3641
- try {
3642
- const anthropic = createModel();
3643
- const { output } = await generateText({
3644
- model: anthropic(CLASSIFIER_MODEL),
3645
- temperature: 0,
3646
- output: Output.object({ schema: commandSafetySchema }),
3647
- prompt: [
3648
- "A coding agent wants to run this shell command in a user's project without asking for approval first. The command may be in any programming language or ecosystem.",
3649
- "Judge it SAFE only if it cannot modify, delete, move, rename, or install anything the project cares about, cannot push/publish/deploy/commit anything, and cannot make a network request that changes remote state.",
3650
- "Three broad categories are safe, and most commands you will see fall into one of them:",
3651
- "(1) Reporting or listing existing state, with no side effect \u2014 e.g. `git status`, `npm ls`, `npm outdated`, `pip show`, `pip freeze`, `cargo tree`, `docker ps`, `docker images`, `kubectl get pods`, `terraform state list`. This includes a read-only GET-style query to a remote registry or API that only fetches public metadata and changes nothing remote (e.g. `npm view <package>`, `pip index versions <package>`, `curl` with no -X/--request other than GET and no -d/--data) \u2014 safe because nothing changes, not because it stays local.",
3652
- '(2) A "preview" or "check" mode that reports what a change WOULD do, or reports a problem, without making the change. This is a general pattern, not a fixed list \u2014 ANY command that takes a dry-run/check/plan/validate/diff/list-only flag is safe when that flag is present, regardless of whether the same flag would normally appear on a "safe" kind of command: `terraform plan`, `terraform validate`, `terraform fmt -check`, `black --check`, `prettier --check`, `isort --check`, `gofmt -l` (list-only), `stylelint` with no `--fix`, and equally `git push --dry-run`, `npm publish --dry-run`, `kubectl apply --dry-run=client` \u2014 these last three are otherwise-unsafe operations (push, publish, cluster changes) that the dry-run flag turns into a report. The corresponding command WITHOUT that flag (e.g. `terraform apply`, `black .`, `gofmt -w`, `git push`) is a different, unsafe command \u2014 the flag is what makes the difference, for any tool, not just the ones named here. This cuts both ways, so do not assume a bare invocation is the safe one: `cargo fmt`, `black`, `prettier`, `isort`, `rustfmt`, and `terraform fmt` all REWRITE FILES by default and need an explicit check/diff/dry-run flag to become safe, while `gofmt`, `eslint`, `stylelint`, `rubocop`, and `ruff check` default to a safe check-only mode and need an explicit fix/write flag to become unsafe \u2014 the same-looking bare command is safe for one group and unsafe for the other, so judge each by what its own flags actually say, not by resemblance to a tool you already judged.',
3653
- "(3) Running the project's own tests, type checker, or linter, as long as it is not passed an autofix/write/update flag (e.g. --fix, -u, --write, rubocop -a) \u2014 this holds even for a compiled language, where running tests or a linter incidentally compiles code and writes build/cache/coverage output (e.g. a target/, build/, or __pycache__ directory): CREATING that incidental output does not make the command unsafe.",
3654
- "Deleting or removing anything is unsafe, even something in a cache or build directory (e.g. `rm -rf __pycache__`, `cargo clean`, `git clean`), and even if the command otherwise fits one of the three categories above. Installing, uninstalling, or upgrading a dependency, changing a database schema, or writing to a path outside the project is also unsafe. A command chained with && or ; is only safe if every part of the chain is safe on its own.",
3655
- "One specific, narrow exception to all of the above: when the command literally invokes one of these runner programs BY NAME as the leading command \u2014 npx, bunx, pnpm dlx, yarn dlx, pipx run, uvx \u2014 it is unsafe regardless of what it then runs, even something that looks like a harmless test/lint/typecheck command (e.g. `npx tsc --noEmit`, `uvx ruff check .`), because that runner can fetch and execute a different, unreviewed version of a package from a registry each time. This exception is about that specific syntax, not a general doubt about whether a tool is installed: a plain `ruff check .`, `pytest`, or any other bare command name is NOT this exception merely because you cannot verify from the string alone that it is installed \u2014 judge those the same as any other already-installed project tool, per the categories above. If you are unsure, judge it unsafe.",
3656
- `Command: ${command}`,
3657
- `Working directory: ${cwd}`,
3658
- `Stated purpose: ${explanation}`
3659
- ].join("\n")
3660
- });
3661
- if (!output.safe) {
3662
- logger.info(
3663
- { command, reason: output.reason },
3664
- "runShell: classifier judged command unsafe, requiring approval"
3665
- );
3666
- }
3667
- return output.safe;
3668
- } catch (err) {
3669
- logger.warn(
3670
- { err, command },
3671
- "runShell: command safety classifier failed, requiring approval"
3672
- );
3673
- return false;
3527
+ async function approveAndRun(ctx, command, cwd, explanation) {
3528
+ const decision = await ctx.shell.approve({ command, cwd, explanation });
3529
+ if (decision === "reject") {
3530
+ ctx.shell.executions.push({ command, cwd, approved: false });
3531
+ logger.info({ command }, "runShell: user rejected the command");
3532
+ return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
3674
3533
  }
3675
- }
3676
- async function runAndRecord(ctx, command, cwd) {
3677
3534
  useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
3678
3535
  const env = await ctx.shell.env().catch((err) => {
3679
3536
  logger.warn({ err, command }, "runShell: could not resolve command env");
@@ -3709,18 +3566,9 @@ async function runAndRecord(ctx, command, cwd) {
3709
3566
  output: run2.output
3710
3567
  };
3711
3568
  }
3712
- async function approveAndRun(ctx, command, cwd, explanation) {
3713
- const decision = await ctx.shell.approve({ command, cwd, explanation });
3714
- if (decision === "reject") {
3715
- ctx.shell.executions.push({ command, cwd, approved: false });
3716
- logger.info({ command }, "runShell: user rejected the command");
3717
- return "The user rejected this command. Do not retry it. Propose a different command, or report the limitation via reportStatus.";
3718
- }
3719
- return runAndRecord(ctx, command, cwd);
3720
- }
3721
- function runShellTool(ctx, createModel = defaultCreateModel) {
3569
+ function runShellTool(ctx) {
3722
3570
  return tool8({
3723
- description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. Do not use this to find or read files \u2014 use listFiles, searchFiles, and readFile instead of ls/find/cat/head/tail/grep/rg. This tool refuses ls/find/tree/dir outright; a read command like cat is not refused (some read-only commands run without approval, see below), but it's still the wrong tool for reading a file \u2014 the dedicated tools exist for that and won't count against this tool's command budget. The user approves any command that could change the project before it runs, so write a clear `explanation`. A command judged read-only (inspection, or running tests/typecheck/lint without an autofix flag, in any language) runs immediately without approval. If the user rejects a command, do not retry it \u2014 propose a different approach.",
3571
+ description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. To inspect the project, use listFiles or searchFiles instead of ls/find \u2014 this tool refuses those. The user sees and approves every command before it runs, so write a clear `explanation`. If the user rejects a command, do not retry it \u2014 propose a different approach.",
3724
3572
  inputSchema: z15.object({
3725
3573
  command: z15.string().describe(
3726
3574
  "The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
@@ -3739,27 +3587,9 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
3739
3587
  const resolved2 = resolveInRoot(ctx, cwd ?? ".");
3740
3588
  if (!resolved2.ok) return resolved2.error;
3741
3589
  if (isExploratoryCommand(command)) {
3742
- return "Refused: use listFiles or searchFiles to find files, and readFile to read one, instead of ls/find/tree/dir.";
3590
+ return "Refused: use listFiles or searchFiles to inspect the project instead of ls/find/tree.";
3743
3591
  }
3744
3592
  logger.info({ command, cwd: resolved2.target }, "called runShell tool");
3745
- const fast = fastPathSafety(command);
3746
- let isSafe = fast;
3747
- if (isSafe === void 0) {
3748
- isSafe = useWizard.getState().isCommandApproved(command, resolved2.target);
3749
- if (!isSafe) {
3750
- isSafe = await classifyCommandSafety(
3751
- createModel,
3752
- command,
3753
- resolved2.target,
3754
- explanation
3755
- );
3756
- }
3757
- }
3758
- if (isSafe) {
3759
- return serializePrompt(
3760
- () => runAndRecord(ctx, command, resolved2.target)
3761
- );
3762
- }
3763
3593
  return serializePrompt(
3764
3594
  () => approveAndRun(ctx, command, resolved2.target, explanation)
3765
3595
  );
@@ -3809,8 +3639,8 @@ function reviewScriptTool(ctx) {
3809
3639
  }
3810
3640
 
3811
3641
  // src/lib/tools/generateRecord.ts
3812
- import { tool as tool10, generateText as generateText2, Output as Output2, NoObjectGeneratedError } from "ai";
3813
- import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
3642
+ import { tool as tool10, generateText, Output, NoObjectGeneratedError } from "ai";
3643
+ import { createAnthropic } from "@ai-sdk/anthropic";
3814
3644
  import { nanoid as nanoid2 } from "nanoid";
3815
3645
  import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
3816
3646
  import { dirname as dirname6 } from "node:path";
@@ -3820,18 +3650,18 @@ var RECORD_MODEL = "claude-haiku-4-5";
3820
3650
  var MAX_RECORDS = 100;
3821
3651
  var BATCH_SIZE = 10;
3822
3652
  var MAX_BATCH_ATTEMPTS = 3;
3823
- function defaultCreateModel2() {
3653
+ function defaultCreateModel() {
3824
3654
  const token = getAuthToken();
3825
3655
  if (!token) {
3826
3656
  throw new Error("Not authenticated: no user token available");
3827
3657
  }
3828
- return createAnthropic2({
3658
+ return createAnthropic({
3829
3659
  apiKey: token,
3830
3660
  baseURL: PROXY_BASE_URL,
3831
3661
  fetch: proxyFetch
3832
3662
  });
3833
3663
  }
3834
- function generateRecordTool(ctx, createModel = defaultCreateModel2) {
3664
+ function generateRecordTool(ctx, createModel = defaultCreateModel) {
3835
3665
  return tool10({
3836
3666
  description: "Generate realistic sample records for an entity and write them to a JSON file. 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.",
3837
3667
  inputSchema: z17.object({
@@ -3852,9 +3682,9 @@ function generateRecordTool(ctx, createModel = defaultCreateModel2) {
3852
3682
  let lastError;
3853
3683
  for (let attempt = 1; attempt <= MAX_BATCH_ATTEMPTS; attempt++) {
3854
3684
  try {
3855
- const { output } = await generateText2({
3685
+ const { output } = await generateText({
3856
3686
  model: anthropic(RECORD_MODEL),
3857
- output: Output2.object({
3687
+ output: Output.object({
3858
3688
  schema: z17.object({
3859
3689
  records: z17.array(recordSchema).length(batchCount)
3860
3690
  })
@@ -4019,7 +3849,7 @@ async function runAgentAttempt(req, attempt) {
4019
3849
  if (!token) {
4020
3850
  throw new Error("Not authenticated: no user token available");
4021
3851
  }
4022
- const anthropic = createAnthropic3({
3852
+ const anthropic = createAnthropic2({
4023
3853
  apiKey: token,
4024
3854
  baseURL: PROXY_BASE_URL,
4025
3855
  fetch: proxyFetch
@@ -4056,7 +3886,7 @@ async function runAgentAttempt(req, attempt) {
4056
3886
  }
4057
3887
  };
4058
3888
  }),
4059
- output: Output3.object({ schema: req.outputSchema }),
3889
+ output: Output2.object({ schema: req.outputSchema }),
4060
3890
  tools: createTools(toolContext, {
4061
3891
  output: req.outputSchema,
4062
3892
  tools: req.tools
@@ -4239,7 +4069,7 @@ async function runAnalysis(mode, extraInstructions = []) {
4239
4069
  // package.json
4240
4070
  var package_default = {
4241
4071
  name: "@algolia/wizard",
4242
- version: "0.33.0-rc.126.239",
4072
+ version: "0.33.0",
4243
4073
  description: "Magically implement Algolia functionality in your codebase",
4244
4074
  type: "module",
4245
4075
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.33.0-rc.126.239",
3
+ "version": "0.33.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {