@wrongstack/tools 0.273.0 → 0.274.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/pack.js CHANGED
@@ -1,17 +1,18 @@
1
1
  import { spawn, execFileSync } from 'node:child_process';
2
2
  import * as Core from '@wrongstack/core';
3
- import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage as toErrorMessage$1, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
3
+ import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, toErrorMessage as toErrorMessage$2, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
4
4
  import * as fs from 'node:fs';
5
5
  import { statSync, mkdirSync, createWriteStream } from 'node:fs';
6
6
  import * as fs2 from 'node:fs/promises';
7
7
  import * as path3 from 'node:path';
8
8
  import { resolve, sep, dirname, join } from 'node:path';
9
9
  import * as os2 from 'node:os';
10
- import { toErrorMessage as toErrorMessage$2 } from '@wrongstack/core/utils';
10
+ import { toErrorMessage as toErrorMessage$3 } from '@wrongstack/core/utils';
11
11
  import { createRequire } from 'node:module';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { Worker } from 'node:worker_threads';
14
14
  import * as ts from 'typescript';
15
+ import { toErrorMessage as toErrorMessage$1 } from '@wrongstack/core/utils/error';
15
16
  import * as dns from 'node:dns/promises';
16
17
  import * as net from 'node:net';
17
18
  import { Agent } from 'undici';
@@ -60,7 +61,7 @@ function createOutputSpool(opts) {
60
61
  let filePath = null;
61
62
  let failed = false;
62
63
  let finalized = false;
63
- const open = () => {
64
+ const open2 = () => {
64
65
  if (stream || failed) return;
65
66
  try {
66
67
  const dir = toolOutputDir();
@@ -93,7 +94,7 @@ function createOutputSpool(opts) {
93
94
  return;
94
95
  }
95
96
  head += text;
96
- open();
97
+ open2();
97
98
  head = "";
98
99
  return;
99
100
  }
@@ -660,6 +661,21 @@ function resolveWin32Command(cmd) {
660
661
  }
661
662
  return cmd;
662
663
  }
664
+ function resolvePowerShell(cmd) {
665
+ if (process.platform !== "win32") return cmd;
666
+ const lower = cmd.toLowerCase();
667
+ if (lower !== "pwsh" && lower !== "powershell" && lower !== "pwsh.exe" && lower !== "powershell.exe") {
668
+ return resolveWin32Command(cmd);
669
+ }
670
+ const primary = lower.startsWith("pwsh") ? "pwsh.exe" : "powershell.exe";
671
+ const fallback = lower.startsWith("pwsh") ? "powershell.exe" : "pwsh.exe";
672
+ const resolved = resolveWin32Command(primary);
673
+ if (resolved !== primary) {
674
+ const fb = resolveWin32Command(fallback);
675
+ return fb === fallback ? cmd : fb;
676
+ }
677
+ return resolved;
678
+ }
663
679
  var WIN32_SHELL_META = /[&|<>\r\n\0]/;
664
680
  function assertSafeWin32ShellArgs(args) {
665
681
  for (const a of args) {
@@ -1659,6 +1675,110 @@ async function checkAndBlockKillCommand(command) {
1659
1675
  }
1660
1676
  return { blocked: false };
1661
1677
  }
1678
+ function pickShell(platform3, command, env) {
1679
+ const override = env.get("WRONGSTACK_SHELL")?.trim().toLowerCase();
1680
+ if (override === "cmd" || override === "cmd.exe") return "cmd";
1681
+ if (override === "powershell" || override === "powershell.exe") return "powershell";
1682
+ if (override === "pwsh" || override === "pwsh.exe") return "pwsh";
1683
+ if (looksLikePowerShell(command)) return "pwsh";
1684
+ return "cmd";
1685
+ }
1686
+ function looksLikePowerShell(command) {
1687
+ if (!command) return false;
1688
+ const trimmed = command.trimStart();
1689
+ if (/\.ps1\b/i.test(trimmed)) return true;
1690
+ if (/^\s*#requires\s/i.test(trimmed)) return true;
1691
+ if (/^\s*param\s*\(/i.test(trimmed)) return true;
1692
+ if (/\$[\w:{]/i.test(trimmed)) return true;
1693
+ if (/\$\(/.test(trimmed)) return true;
1694
+ if (/@\s*['"]/.test(trimmed)) return true;
1695
+ if (/&\s+\$/.test(trimmed)) return true;
1696
+ if (/(^|\s)@\s*\(/.test(trimmed)) return true;
1697
+ if (/(^|\s)@\{/.test(trimmed)) return true;
1698
+ if (/(?:^|[\s\[\(\{,;])(?:-eq|-ne|-lt|-gt|-le|-ge|-like|-notlike|-match|-notmatch|-contains|-notcontains|-in|-notin|-and|-or|-not|-band|-bor|-bxor|-replace|-isplit|-csplit|-osplit|-join|-is|-as|-f)(?:$|[\s\]\)\},;])/i.test(trimmed)) {
1699
+ return true;
1700
+ }
1701
+ if (PS_VERB_RE.test(trimmed)) return true;
1702
+ if (/(?:^|[\s;&|])(gci|gi|gp|gcm|gps|sl|rm|cat|cp|mv)\b/i.test(trimmed)) {
1703
+ return true;
1704
+ }
1705
+ if (looksLikePowerShellExtended(command)) return true;
1706
+ return false;
1707
+ }
1708
+ function looksLikePowerShellExtended(command) {
1709
+ if (!command) return false;
1710
+ const trimmed = command.trimStart();
1711
+ if (/(?:^|\s)[-/](?:WhatIf|Confirm|ErrorAction)(?::[^\s]+|\s|=|$)/i.test(trimmed)) {
1712
+ return true;
1713
+ }
1714
+ if (/(?:^|[\s;&|])(Where-Object|ForEach-Object|Select-Object|Sort-Object|Group-Object|Measure-Object|Compare-Object|Tee-Object)(?:\s|$)/i.test(trimmed)) {
1715
+ return true;
1716
+ }
1717
+ if (/\bWrite-(?:Host|Output|Error|Warning|Verbose|Debug|Information)(?:\s|$)/i.test(trimmed)) {
1718
+ return true;
1719
+ }
1720
+ if (/HK(?:LM|CU|CR|U|CC|DD|PD):\\/i.test(trimmed)) return true;
1721
+ if (/\[(?:string|int|bool|xml|double|float|decimal|char|byte|long|System\.)/i.test(trimmed)) {
1722
+ return true;
1723
+ }
1724
+ if (/^\s*<#|#>\s*$/m.test(trimmed)) return true;
1725
+ if (/(?:^|\s)[-/\/](?:AsPlainText|PipelineVariable|pv|FilterHashtable|OutVariable|ov)(?:\s|=|$)/i.test(trimmed)) {
1726
+ return true;
1727
+ }
1728
+ return false;
1729
+ }
1730
+ function wrapPowerShellScript(command) {
1731
+ const bootstrap = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;$ConfirmPreference='None';$WhatIfPreference=$false";
1732
+ return "\uFEFF" + bootstrap + "\ntry {\n" + command + "\n} finally { exit $LASTEXITCODE }";
1733
+ }
1734
+ var PS_VERB_RE = new RegExp(
1735
+ // Boundaries: start-of-string, whitespace, `;`, `&`, `|`, `(`, `{`, `,`.
1736
+ "(?:^|[\\s;&|\\(\\{,])(?:Get|Set|New|Remove|Add|Clear|Copy|Move|Rename|Test|Update|Write|Read|Push|Pop|Invoke|Start|Stop|Wait|Out|Format|Group|Measure|Compare|Resolve|ConvertTo|ConvertFrom|Convert|Import|Export|Select|Where|ForEach|Sort|Tee|Split|Join|Limit|Skip|Step|Trace|Debug|Register|Unregister|Enable|Disable|Restart|Suspend|Resume|Save|Open|Close|Lock|Unlock|Mount|Dismount|Enter|Exit|Use|Show|Hide|Find|Search|Watch|Initialize|Optimize|Compress|Expand|Merge|Checkpoint|Undo|Redo|Approve|Deny|Block|Grant|Revoke|Assert|Confirm|Receive|Send|Connect|Disconnect|Reset|Backup|Restore|Publish|Unpublish|Install|Uninstall|Build|Rebuild|Deploy|Submit|Process|Complete|Approve|Revoke|Pay|Refund|Decline|Receive|Send)-[A-Za-z][A-Za-z0-9]+(?:[\\-\\+][A-Za-z][A-Za-z0-9]+)*(?:$|[\\s\\-\\;\\&\\|\\(\\)\\{\\},])",
1737
+ "i"
1738
+ );
1739
+ function shellArgs(shell) {
1740
+ if (shell === "powershell" || shell === "pwsh") {
1741
+ return ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"];
1742
+ }
1743
+ return ["/c"];
1744
+ }
1745
+ function diagnoseBashism(command, shell) {
1746
+ if (!command) return void 0;
1747
+ const isCmd = shell === "cmd";
1748
+ const hints = [];
1749
+ const add = (h) => {
1750
+ if (!hints.includes(h)) hints.push(h);
1751
+ };
1752
+ if (/\/dev\/null/.test(command)) {
1753
+ add(
1754
+ isCmd ? "use `nul` instead of `/dev/null` (e.g. `2>nul`)" : "use `$null` instead of `/dev/null` (e.g. `2>$null`)"
1755
+ );
1756
+ }
1757
+ if (/(^|[;&|]\s*)export\s+[A-Za-z_]\w*=/.test(command)) {
1758
+ add(
1759
+ isCmd ? "set env vars with `set NAME=value`, not `export`" : "set env vars with `$env:NAME = 'value'`, not `export`"
1760
+ );
1761
+ }
1762
+ if (/<<-?\s*['"]?[A-Za-z_]\w*/.test(command)) {
1763
+ add(
1764
+ isCmd ? "cmd has no heredocs \u2014 write the content to a file or use multiple `echo` lines" : "PowerShell has no heredocs \u2014 use a single-quoted here-string `@'\u2026'@` (closing `'@` at column 0)"
1765
+ );
1766
+ }
1767
+ if (shell === "powershell" && /(&&|\|\|)/.test(command)) {
1768
+ add("Windows PowerShell 5.1 has no `&&`/`||` \u2014 separate commands with `;` (check `$LASTEXITCODE`)");
1769
+ }
1770
+ if (/\brm\s+-[A-Za-z]*[rf]/.test(command)) {
1771
+ add(
1772
+ isCmd ? "`rm` is not a cmd builtin \u2014 use `del` (files) or `rmdir /s /q` (dirs)" : "use `Remove-Item -Recurse -Force` \u2014 the `rm -rf` bash flags don't exist in PowerShell"
1773
+ );
1774
+ }
1775
+ if (/\bwhich\s+\S/.test(command)) {
1776
+ add(isCmd ? "use `where <cmd>` instead of `which`" : "use `Get-Command <cmd>` instead of `which`");
1777
+ }
1778
+ if (hints.length === 0) return void 0;
1779
+ const label = isCmd ? "cmd.exe" : shell === "pwsh" ? "PowerShell 7" : "Windows PowerShell";
1780
+ return `[wrongstack] This command failed and contains bash/POSIX syntax that ${label} does not accept \u2014 ${hints.join("; ")}. Rewrite it in ${isCmd ? "cmd" : "PowerShell"} syntax and retry.`;
1781
+ }
1662
1782
 
1663
1783
  // src/bash.ts
1664
1784
  var MAX_OUTPUT = 32768;
@@ -1755,18 +1875,36 @@ var bashTool = {
1755
1875
  }
1756
1876
  const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT_MS, 6e5));
1757
1877
  const isWin3 = os2.platform() === "win32";
1758
- const shell = (() => {
1759
- const explicit = process.env[isWin3 ? "WRONGSTACK_COMSPEC" : "WRONGSTACK_SHELL"];
1760
- if (explicit) return explicit;
1761
- if (isWin3) return process.env["COMSPEC"] ?? "cmd.exe";
1762
- const fromEnv = process.env["SHELL"];
1763
- if (fromEnv) {
1764
- const name = fromEnv.split("/").pop() ?? "";
1765
- if (["bash", "zsh", "sh", "dash", "fish"].includes(name)) return fromEnv;
1766
- }
1767
- return "/bin/bash";
1768
- })();
1769
- const args = isWin3 ? ["/c", input.command] : ["-c", input.command];
1878
+ let plan;
1879
+ let winShellKind;
1880
+ if (isWin3) {
1881
+ const shell2 = pickShell("win32", input.command, {
1882
+ get: (k) => process.env[k]
1883
+ });
1884
+ winShellKind = shell2;
1885
+ const bin = shell2 === "powershell" ? resolvePowerShell("powershell.exe") : shell2 === "pwsh" ? resolvePowerShell("pwsh.exe") : process.env["COMSPEC"] ?? "cmd.exe";
1886
+ plan = {
1887
+ bin,
1888
+ argv: shellArgs(shell2),
1889
+ useStdin: shell2 === "powershell" || shell2 === "pwsh",
1890
+ stdinBody: shell2 === "powershell" || shell2 === "pwsh" ? wrapPowerShellScript(input.command) : void 0
1891
+ };
1892
+ } else {
1893
+ const explicit = process.env["WRONGSTACK_SHELL"];
1894
+ let bin;
1895
+ if (explicit) bin = explicit;
1896
+ else {
1897
+ const fromEnv = process.env["SHELL"];
1898
+ if (fromEnv) {
1899
+ const name = fromEnv.split("/").pop() ?? "";
1900
+ if (["bash", "zsh", "sh", "dash", "fish"].includes(name)) bin = fromEnv;
1901
+ else bin = "/bin/bash";
1902
+ } else bin = "/bin/bash";
1903
+ }
1904
+ plan = { bin, argv: ["-c"], useStdin: false, stdinBody: void 0 };
1905
+ }
1906
+ const shell = plan.bin;
1907
+ const args = plan.useStdin ? [...plan.argv] : [...plan.argv, input.command];
1770
1908
  const env = buildChildEnv(ctx.session?.id);
1771
1909
  const detached = !isWin3;
1772
1910
  const startedAt = Date.now();
@@ -1776,7 +1914,9 @@ var bashTool = {
1776
1914
  const child2 = spawn(shell, args, {
1777
1915
  cwd: ctx.projectRoot,
1778
1916
  env,
1779
- stdio: ["ignore", "pipe", "pipe"],
1917
+ // PowerShell takes the script on stdin (no argv quoting); cmd.exe
1918
+ // and POSIX shells ignore stdin when given the command inline.
1919
+ stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
1780
1920
  // win32: CreateProcess IGNORES CREATE_NO_WINDOW (windowsHide) when
1781
1921
  // DETACHED_PROCESS (detached: true) is set, so the console-less
1782
1922
  // cmd.exe's grandchildren (node, dev servers) each allocate a fresh
@@ -1787,6 +1927,13 @@ var bashTool = {
1787
1927
  detached: !isWin3,
1788
1928
  windowsHide: true
1789
1929
  });
1930
+ if (plan.useStdin) {
1931
+ try {
1932
+ child2.stdin?.write(plan.stdinBody ?? input.command);
1933
+ child2.stdin?.end();
1934
+ } catch {
1935
+ }
1936
+ }
1790
1937
  const pid2 = child2.pid;
1791
1938
  if (typeof pid2 === "number") {
1792
1939
  registry.register({
@@ -1841,11 +1988,20 @@ var bashTool = {
1841
1988
  const child = spawn(shell, args, {
1842
1989
  cwd: ctx.projectRoot,
1843
1990
  env,
1844
- stdio: ["ignore", "pipe", "pipe"],
1991
+ // PowerShell takes the script on stdin (no argv quoting); cmd.exe
1992
+ // and POSIX shells ignore stdin when given the command inline.
1993
+ stdio: [plan.useStdin ? "pipe" : "ignore", "pipe", "pipe"],
1845
1994
  detached,
1846
1995
  windowsHide: true,
1847
1996
  ...isWin3 ? {} : { signal: opts.signal }
1848
1997
  });
1998
+ if (plan.useStdin) {
1999
+ try {
2000
+ child.stdin?.write(plan.stdinBody ?? input.command);
2001
+ child.stdin?.end();
2002
+ } catch {
2003
+ }
2004
+ }
1849
2005
  const pid = child.pid;
1850
2006
  if (typeof pid === "number") {
1851
2007
  registry.register({
@@ -1996,10 +2152,13 @@ var bashTool = {
1996
2152
  yield { type: "partial_output", text: remainder };
1997
2153
  }
1998
2154
  const spooled = spool.finalize();
2155
+ const hint = !timedOut && typeof c.code === "number" && c.code !== 0 && winShellKind ? diagnoseBashism(input.command, winShellKind) : void 0;
1999
2156
  yield {
2000
2157
  type: "final",
2001
2158
  output: {
2002
- output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : ""),
2159
+ output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + (hint ? `
2160
+
2161
+ ${hint}` : ""),
2003
2162
  exit_code: c.code,
2004
2163
  timed_out: timedOut
2005
2164
  }
@@ -2368,7 +2527,7 @@ function loadDatabaseSync() {
2368
2527
  DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
2369
2528
  } catch (err) {
2370
2529
  throw new Error(
2371
- `The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$2(err)}`
2530
+ `The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${toErrorMessage$3(err)}`
2372
2531
  );
2373
2532
  }
2374
2533
  return DatabaseSyncCtor;
@@ -2495,6 +2654,8 @@ var IndexStore = class {
2495
2654
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)");
2496
2655
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)");
2497
2656
  this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)");
2657
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)");
2658
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)");
2498
2659
  this.db.exec(`
2499
2660
  CREATE TABLE IF NOT EXISTS refs (
2500
2661
  id INTEGER PRIMARY KEY,
@@ -3407,9 +3568,9 @@ async function syncGoParse(filePath, content, lang) {
3407
3568
  }
3408
3569
  }
3409
3570
  async function parseSymbols3(opts) {
3410
- const { file, lang } = opts;
3571
+ const { file, content, lang } = opts;
3411
3572
  try {
3412
- return await syncPyParse(file, lang);
3573
+ return await syncPyParse(file, content, lang);
3413
3574
  } catch {
3414
3575
  return { file, lang, symbols: [], mtimeMs: Date.now() };
3415
3576
  }
@@ -3477,8 +3638,7 @@ syms = []
3477
3638
  errors = []
3478
3639
 
3479
3640
  try:
3480
- with open(sys.argv[1], "r", encoding="utf-8") as f:
3481
- source = f.read()
3641
+ source = sys.stdin.read()
3482
3642
  tree = ast.parse(source, filename=sys.argv[1])
3483
3643
  except Exception as e:
3484
3644
  errors.append(str(e))
@@ -3618,16 +3778,21 @@ visitor.visit(tree)
3618
3778
 
3619
3779
  print(json.dumps([s.to_dict() for s in syms]))
3620
3780
  `;
3621
- async function syncPyParse(filePath, lang) {
3781
+ var _cachedScriptPath = null;
3782
+ async function syncPyParse(filePath, content, lang) {
3622
3783
  try {
3623
- const tmpDir = path3.join(os2.tmpdir(), "ws-py-parse");
3624
- await fs2.mkdir(tmpDir, { recursive: true });
3625
- const scriptPath = path3.join(tmpDir, "parse.py");
3626
- await fs2.writeFile(scriptPath, PY_PARSE_SCRIPT, "utf8");
3627
- const proc = spawn("python", [scriptPath, filePath], {
3784
+ if (!_cachedScriptPath) {
3785
+ const tmpDir = path3.join(os2.tmpdir(), "ws-py-parse");
3786
+ await fs2.mkdir(tmpDir, { recursive: true });
3787
+ _cachedScriptPath = path3.join(tmpDir, "parse.py");
3788
+ await fs2.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
3789
+ }
3790
+ const proc = spawn("python", [_cachedScriptPath, filePath], {
3628
3791
  stdio: ["pipe", "pipe", "pipe"],
3629
3792
  windowsHide: true
3630
3793
  });
3794
+ proc.stdin?.write(content);
3795
+ proc.stdin?.end();
3631
3796
  let stdout = "";
3632
3797
  proc.stdout?.on("data", (chunk) => {
3633
3798
  stdout += chunk.toString();
@@ -4294,7 +4459,7 @@ async function parseFile(file, content, lang) {
4294
4459
  case "go":
4295
4460
  return parseSymbols2({ file, content, lang: "go" });
4296
4461
  case "py":
4297
- return parseSymbols3({ file, lang: "py" });
4462
+ return parseSymbols3({ file, content, lang: "py" });
4298
4463
  case "rs":
4299
4464
  return parseSymbols4({ file, content, lang: "rs" });
4300
4465
  case "json":
@@ -4325,10 +4490,12 @@ async function runIndexerWithStore(store, opts) {
4325
4490
  let symbolsIndexed = 0;
4326
4491
  const isGitIgnored = await loadGitignoreMatcher(projectRoot);
4327
4492
  let files;
4493
+ let discoveredFiles = null;
4328
4494
  if (opts.files && opts.files.length > 0) {
4329
4495
  files = opts.files.map((f) => path3.resolve(projectRoot, f)).filter((f) => !isGitIgnored(path3.relative(projectRoot, f).replace(/\\/g, "/"), false));
4330
4496
  } else {
4331
4497
  files = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
4498
+ discoveredFiles = new Set(files);
4332
4499
  }
4333
4500
  if (langs && langs.length > 0) {
4334
4501
  const langSet = new Set(langs);
@@ -4446,11 +4613,11 @@ async function runIndexerWithStore(store, opts) {
4446
4613
  filesIndexed++;
4447
4614
  }
4448
4615
  }
4449
- for (const [file_] of existingMeta) {
4450
- try {
4451
- await fs2.stat(file_);
4452
- } catch {
4453
- store.deleteFile(file_);
4616
+ if (discoveredFiles) {
4617
+ for (const [file_] of existingMeta) {
4618
+ if (!discoveredFiles.has(file_)) {
4619
+ store.deleteFile(file_);
4620
+ }
4454
4621
  }
4455
4622
  }
4456
4623
  const durationMs = Date.now() - startMs;
@@ -5385,49 +5552,71 @@ function findSimilarity(haystack, needle) {
5385
5552
  return line;
5386
5553
  }
5387
5554
  var isWin2 = process.platform === "win32";
5388
- var ALLOWED_COMMANDS = {
5389
- node: ["--version", "-r", "--input-type=module"],
5390
- npm: ["--version", "list", "pkg", "doctor", "view", "outdated", "audit"],
5391
- pnpm: ["--version", "remove", "list", "view", "outdated", "audit"],
5392
- npx: ["--version"],
5393
- git: [
5394
- "--version",
5395
- "status",
5396
- "log",
5397
- "diff",
5398
- "branch",
5399
- "checkout",
5400
- "stash",
5401
- "add",
5402
- "commit",
5403
- "push",
5404
- "pull"
5405
- ],
5406
- ls: ["-la", "-l", "-a"],
5407
- cat: [],
5408
- head: ["-n"],
5409
- tail: ["-n"],
5410
- wc: ["-l", "-w", "-c"],
5411
- grep: [],
5412
- find: [],
5413
- echo: [],
5414
- mkdir: ["-p"],
5415
- cp: ["-r"],
5416
- mv: [],
5417
- rm: ["-rf"],
5418
- touch: [],
5419
- bun: ["--version"],
5420
- tsc: ["--version", "--noEmit", "--project"],
5421
- vitest: ["--version", "run", "--coverage"],
5422
- biome: ["--version", "lint", "format", "check"],
5423
- cargo: ["--version", "build", "test", "check"],
5424
- rustc: ["--version"],
5425
- go: ["version", "run", "build", "test"],
5426
- python: ["--version"],
5427
- pip: ["--version", "list"],
5428
- docker: ["--version", "ps", "images"],
5429
- kubectl: ["version", "get", "describe", "logs"]
5430
- };
5555
+ var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
5556
+ // JS / TS toolchain
5557
+ "node",
5558
+ "npm",
5559
+ "pnpm",
5560
+ "yarn",
5561
+ "npx",
5562
+ "bun",
5563
+ "deno",
5564
+ "tsc",
5565
+ "vitest",
5566
+ "jest",
5567
+ "biome",
5568
+ "eslint",
5569
+ "prettier",
5570
+ // version control
5571
+ "git",
5572
+ // Rust
5573
+ "cargo",
5574
+ "rustc",
5575
+ // Go
5576
+ "go",
5577
+ // Python
5578
+ "python",
5579
+ "python3",
5580
+ "pip",
5581
+ "pip3",
5582
+ // Ruby
5583
+ "ruby",
5584
+ "gem",
5585
+ "bundle",
5586
+ // JVM
5587
+ "java",
5588
+ "javac",
5589
+ "mvn",
5590
+ "gradle",
5591
+ "gradlew",
5592
+ // .NET
5593
+ "dotnet",
5594
+ // C / C++ / native build
5595
+ "make",
5596
+ "cmake",
5597
+ // containers / orchestration (read-only subcommands; see BLOCKED_ARG_PATTERNS)
5598
+ "docker",
5599
+ "kubectl",
5600
+ // common POSIX file/text utilities
5601
+ "ls",
5602
+ "cat",
5603
+ "head",
5604
+ "tail",
5605
+ "wc",
5606
+ "grep",
5607
+ "find",
5608
+ "echo",
5609
+ "mkdir",
5610
+ "cp",
5611
+ "mv",
5612
+ "rm",
5613
+ "touch"
5614
+ ]);
5615
+ var allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
5616
+ var normalizeCmd = (c) => c.trim();
5617
+ function isExecCommandAllowed(cmd) {
5618
+ return allowedCommands.has(normalizeCmd(cmd));
5619
+ }
5431
5620
  var MAX_ARGS = 20;
5432
5621
  var MAX_OUTPUT2 = 2e5;
5433
5622
  var DEFAULT_TIMEOUT_MS2 = 3e4;
@@ -5489,7 +5678,7 @@ var execTool = {
5489
5678
  name: "exec",
5490
5679
  category: "Shell",
5491
5680
  description: "Execute a **whitelisted, restricted set of commands** with strict argument validation. This is the **preferred and safer** alternative to the `bash` tool for running development tools (node, npm, pnpm, tsc, git, tests, linters, etc.). It prevents arbitrary command injection and limits what the model can do.",
5492
- usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be one of the allowed commands (node, npm, pnpm, git, tsc, eslint, vitest, etc.).\n- Arguments are passed as a clean array (no shell interpretation).\n- `cwd` is validated to stay inside the project.\n- For anything that requires real shell features (pipes, complex redirection, arbitrary commands), fall back to `bash` (with strong justification).\nThis tool significantly reduces the risk compared to full shell access.",
5681
+ usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be in the allowlist. Defaults cover JS (node/npm/pnpm/yarn/bun/deno/tsc/vitest/eslint/biome), Go (`go build`/`go test`), Rust (cargo), Python (python/pip), Ruby (gem/bundle), JVM (java/mvn/gradle), .NET (dotnet), native (make/cmake), and git. Users can extend it via `tools.exec.allow` in config.\n- Arguments are passed as a clean array (no shell interpretation).\n- `cwd` is validated to stay inside the project.\n- If a command is not allowlisted, the error explains how to add it; for one-off arbitrary commands, fall back to `bash` (with strong justification).\nThis tool significantly reduces the risk compared to full shell access.",
5493
5682
  permission: "confirm",
5494
5683
  mutating: true,
5495
5684
  riskTier: "standard",
@@ -5543,12 +5732,12 @@ var execTool = {
5543
5732
  truncated: false,
5544
5733
  allowed: false
5545
5734
  };
5546
- if (!(cmd in ALLOWED_COMMANDS)) {
5735
+ if (!isExecCommandAllowed(cmd)) {
5547
5736
  return {
5548
5737
  command: cmd,
5549
5738
  args: input.args ?? [],
5550
5739
  stdout: "",
5551
- stderr: `Command "${cmd}" not in allowlist. Use the bash tool for arbitrary commands.`,
5740
+ stderr: `Command "${cmd}" not in allowlist. Add it to your ~/.wrongstack/config.json under "tools": { "exec": { "allow": ["${cmd}"] } }, or use the bash tool for one-off arbitrary commands.`,
5552
5741
  exitCode: 1,
5553
5742
  truncated: false,
5554
5743
  allowed: false
@@ -5591,19 +5780,58 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
5591
5780
  let stdout = "";
5592
5781
  let stderr = "";
5593
5782
  let killed = false;
5783
+ const resolvedOnce = { value: false };
5784
+ const finish = (result) => {
5785
+ if (resolvedOnce.value) return;
5786
+ resolvedOnce.value = true;
5787
+ resolve6(result);
5788
+ };
5594
5789
  const startedAt = Date.now();
5595
5790
  const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
5596
5791
  const resolved = resolveWin32Command(cmd);
5597
5792
  const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
5598
5793
  const spawnCmd = needsShell ? cmd : resolved;
5599
5794
  if (needsShell) assertSafeWin32ShellArgs(args);
5600
- const child = spawn(spawnCmd, args, {
5601
- cwd,
5602
- env: buildChildEnv(sessionId),
5603
- stdio: ["ignore", "pipe", "pipe"],
5604
- windowsHide: true,
5605
- ...isWin2 ? {} : { signal },
5606
- ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
5795
+ let child;
5796
+ try {
5797
+ child = spawn(spawnCmd, args, {
5798
+ cwd,
5799
+ env: buildChildEnv(sessionId),
5800
+ stdio: ["ignore", "pipe", "pipe"],
5801
+ windowsHide: true,
5802
+ ...isWin2 ? {} : { signal },
5803
+ ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
5804
+ });
5805
+ } catch (err) {
5806
+ spool.finalize();
5807
+ finish({
5808
+ command: cmd,
5809
+ args,
5810
+ stdout: "",
5811
+ stderr: `spawn failed: ${toErrorMessage$1(err)}`,
5812
+ exitCode: 1,
5813
+ truncated: false,
5814
+ allowed: true
5815
+ });
5816
+ return;
5817
+ }
5818
+ child.on("error", (err) => {
5819
+ const isAbort = err && err.code === "ABORT_ERR";
5820
+ const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
5821
+ clearTimeout(timer);
5822
+ if (isWin2) signal.removeEventListener("abort", onAbort);
5823
+ if (typeof pid === "number") registry.unregister(pid);
5824
+ registry.afterCall(Date.now() - startedAt, true);
5825
+ spool.finalize();
5826
+ finish({
5827
+ command: cmd,
5828
+ args,
5829
+ stdout: normalizeCommandOutput(stdout),
5830
+ stderr: stderrText,
5831
+ exitCode: isAbort ? 124 : 1,
5832
+ truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
5833
+ allowed: true
5834
+ });
5607
5835
  });
5608
5836
  const registry = getProcessRegistry();
5609
5837
  const pid = child.pid;
@@ -5643,7 +5871,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
5643
5871
  const exitCode = killed ? 124 : code ?? 1;
5644
5872
  registry.afterCall(durationMs, exitCode !== 0);
5645
5873
  const spooled = spool.finalize();
5646
- resolve6({
5874
+ finish({
5647
5875
  command: cmd,
5648
5876
  args,
5649
5877
  stdout: normalizeCommandOutput(stdout) + (spooled ? spoolNote(spooled) : ""),
@@ -5653,22 +5881,6 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
5653
5881
  allowed: true
5654
5882
  });
5655
5883
  });
5656
- child.on("error", (err) => {
5657
- clearTimeout(timer);
5658
- if (isWin2) signal.removeEventListener("abort", onAbort);
5659
- if (typeof pid === "number") registry.unregister(pid);
5660
- registry.afterCall(Date.now() - startedAt, true);
5661
- spool.finalize();
5662
- resolve6({
5663
- command: cmd,
5664
- args,
5665
- stdout: normalizeCommandOutput(stdout),
5666
- stderr: err.message,
5667
- exitCode: 1,
5668
- truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
5669
- allowed: true
5670
- });
5671
- });
5672
5884
  });
5673
5885
  }
5674
5886
  var TD = new TurndownService({
@@ -6326,7 +6538,27 @@ function runGit2(args, cwd, signal) {
6326
6538
  });
6327
6539
  });
6328
6540
  }
6541
+
6542
+ // src/_concurrency.ts
6543
+ async function mapWithConcurrency(items, limit, fn) {
6544
+ if (items.length === 0) return [];
6545
+ const effectiveLimit = Math.max(1, Math.min(limit | 0, items.length));
6546
+ const results = new Array(items.length);
6547
+ let nextIndex = 0;
6548
+ const worker2 = async () => {
6549
+ while (true) {
6550
+ const i = nextIndex++;
6551
+ if (i >= items.length) return;
6552
+ results[i] = await fn(items[i]);
6553
+ }
6554
+ };
6555
+ await Promise.all(Array.from({ length: effectiveLimit }, worker2));
6556
+ return results;
6557
+ }
6558
+
6559
+ // src/glob.ts
6329
6560
  var DEFAULT_IGNORE2 = ["node_modules", ".git", "dist", "build", ".next", "coverage", ".turbo"];
6561
+ var WALK_CONCURRENCY = 16;
6330
6562
  var globTool = {
6331
6563
  name: "glob",
6332
6564
  category: "Filesystem",
@@ -6364,6 +6596,22 @@ var globTool = {
6364
6596
  const re = compileGlob(input.pattern);
6365
6597
  const results = [];
6366
6598
  let truncated = false;
6599
+ const pushResult = async (full) => {
6600
+ if (truncated || results.length >= limit) {
6601
+ truncated = true;
6602
+ return;
6603
+ }
6604
+ try {
6605
+ const st = await fs2.stat(full);
6606
+ if (truncated || results.length >= limit) {
6607
+ truncated = true;
6608
+ return;
6609
+ }
6610
+ results.push({ rel: full, mtime: st.mtimeMs });
6611
+ if (results.length >= limit) truncated = true;
6612
+ } catch {
6613
+ }
6614
+ };
6367
6615
  const walk = async (dir, relPrefix) => {
6368
6616
  if (results.length >= limit) {
6369
6617
  truncated = true;
@@ -6375,6 +6623,8 @@ var globTool = {
6375
6623
  } catch {
6376
6624
  return;
6377
6625
  }
6626
+ const subdirs = [];
6627
+ const matchedFiles = [];
6378
6628
  for (const e of entries) {
6379
6629
  const name = e.name;
6380
6630
  if (DEFAULT_IGNORE2.includes(name)) continue;
@@ -6382,22 +6632,36 @@ var globTool = {
6382
6632
  const rel = relPrefix ? `${relPrefix}/${name}` : name;
6383
6633
  const full = path3.join(dir, name);
6384
6634
  if (e.isDirectory()) {
6385
- await walk(full, rel);
6386
- if (truncated) return;
6635
+ subdirs.push({ full, rel });
6387
6636
  } else if (e.isFile()) {
6388
- if (re.test(rel) || re.test(name)) {
6389
- try {
6390
- const st = await fs2.stat(full);
6391
- results.push({ rel: full, mtime: st.mtimeMs });
6392
- if (results.length >= limit) {
6393
- truncated = true;
6394
- return;
6395
- }
6396
- } catch {
6637
+ re.lastIndex = 0;
6638
+ const relMatch = re.test(rel);
6639
+ re.lastIndex = 0;
6640
+ const nameMatch = re.test(name);
6641
+ if (relMatch || nameMatch) {
6642
+ matchedFiles.push(full);
6643
+ }
6644
+ } else if (e.isSymbolicLink()) {
6645
+ try {
6646
+ const st = await fs2.stat(full);
6647
+ if (st.isDirectory()) {
6648
+ subdirs.push({ full, rel });
6649
+ } else if (st.isFile()) {
6650
+ re.lastIndex = 0;
6651
+ const relMatch = re.test(rel);
6652
+ re.lastIndex = 0;
6653
+ const nameMatch = re.test(name);
6654
+ if (relMatch || nameMatch) matchedFiles.push(full);
6397
6655
  }
6656
+ } catch {
6398
6657
  }
6399
6658
  }
6659
+ if (truncated) return;
6400
6660
  }
6661
+ await mapWithConcurrency(matchedFiles, WALK_CONCURRENCY, pushResult);
6662
+ if (truncated) return;
6663
+ const remainingSubdirs = truncated ? [] : subdirs;
6664
+ await mapWithConcurrency(remainingSubdirs, WALK_CONCURRENCY, ({ full, rel }) => walk(full, rel));
6401
6665
  };
6402
6666
  await walk(base, "");
6403
6667
  results.sort((a, b) => b.mtime - a.mtime);
@@ -6461,6 +6725,8 @@ function capSubject(line) {
6461
6725
  // src/grep.ts
6462
6726
  var DEFAULT_IGNORE3 = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
6463
6727
  var NATIVE_SCAN_CONCURRENCY = 32;
6728
+ var NATIVE_READ_CHUNK_BYTES = 64 * 1024;
6729
+ var NATIVE_MAX_FILE_BYTES = 1e6;
6464
6730
  var grepTool = {
6465
6731
  name: "grep",
6466
6732
  category: "Search",
@@ -6685,7 +6951,8 @@ async function runNative(input, base, mode, limit, signal) {
6685
6951
  const re = compiled.regex;
6686
6952
  const globRe = input.glob ? compileGlob(input.glob) : null;
6687
6953
  const matches = [];
6688
- const fileMatches = /* @__PURE__ */ new Map();
6954
+ const countOnlyFirstHit = mode === "count" && limit === 1;
6955
+ const maxBytes = mode === "content" ? NATIVE_MAX_FILE_BYTES : Math.min(NATIVE_MAX_FILE_BYTES, 256 * 1024);
6689
6956
  let total = 0;
6690
6957
  let stopped = false;
6691
6958
  const scanFile = async (full, name) => {
@@ -6694,34 +6961,79 @@ async function runNative(input, base, mode, limit, signal) {
6694
6961
  if (globRe) globRe.lastIndex = 0;
6695
6962
  try {
6696
6963
  const stat11 = await fs2.stat(full);
6697
- if (stat11.size > 1e6 || stopped || signal.aborted) return;
6698
- const head = await fs2.readFile(full);
6699
- if (isBinaryBuffer(head) || stopped || signal.aborted) return;
6700
- const text = head.toString("utf8");
6701
- const lines = text.split(/\r?\n/);
6702
- let fileHits = 0;
6703
- for (let i = 0; i < lines.length; i++) {
6704
- if (stopped || signal.aborted) break;
6705
- const ln = capSubject(lines[i] ?? "");
6706
- re.lastIndex = 0;
6707
- if (re.test(ln)) {
6708
- fileHits++;
6709
- total++;
6710
- if (mode === "content" && matches.length < limit) {
6711
- matches.push(`${full}:${i + 1}:${ln}`);
6964
+ if (!stat11.isFile() || stat11.size > maxBytes || stopped || signal.aborted) return;
6965
+ const file = await fs2.open(full, "r");
6966
+ try {
6967
+ let bytesReadTotal = 0;
6968
+ let lineNumber = 0;
6969
+ let fileHits = 0;
6970
+ let leftover = "";
6971
+ let binaryChecked = false;
6972
+ const buffer = Buffer.allocUnsafe(Math.min(NATIVE_READ_CHUNK_BYTES, maxBytes));
6973
+ while (!stopped && !signal.aborted && bytesReadTotal < maxBytes) {
6974
+ const remaining = maxBytes - bytesReadTotal;
6975
+ const { bytesRead } = await file.read(buffer, 0, Math.min(buffer.length, remaining), null);
6976
+ if (bytesRead === 0) break;
6977
+ const chunk = buffer.subarray(0, bytesRead);
6978
+ if (!binaryChecked) {
6979
+ binaryChecked = true;
6980
+ if (isBinaryBuffer(chunk)) return;
6981
+ }
6982
+ bytesReadTotal += bytesRead;
6983
+ const text = leftover + chunk.toString("utf8");
6984
+ const lines = text.split(/\r?\n/);
6985
+ leftover = lines.pop() ?? "";
6986
+ for (const rawLine of lines) {
6987
+ if (stopped || signal.aborted) break;
6988
+ lineNumber++;
6989
+ const ln = capSubject(rawLine);
6990
+ re.lastIndex = 0;
6991
+ if (!re.test(ln)) continue;
6992
+ fileHits++;
6993
+ total++;
6994
+ if (mode === "content") {
6995
+ if (matches.length < limit) matches.push(`${full}:${lineNumber}:${ln}`);
6996
+ } else if (mode === "files_with_matches") {
6997
+ if (fileHits === 1 && matches.length < limit) matches.push(full);
6998
+ break;
6999
+ } else if (fileHits === 1 && matches.length < limit) {
7000
+ matches.push(`${full}:${countOnlyFirstHit ? 1 : 0}`);
7001
+ }
7002
+ if (countOnlyFirstHit || mode !== "content" && matches.length >= limit) {
7003
+ stopped = true;
7004
+ break;
7005
+ }
6712
7006
  }
7007
+ if (mode === "files_with_matches" && fileHits > 0) break;
6713
7008
  }
6714
- }
6715
- if (fileHits > 0) {
6716
- fileMatches.set(full, fileHits);
6717
- if (mode === "files_with_matches" && matches.length < limit) {
6718
- matches.push(full);
7009
+ if (!stopped && !signal.aborted && leftover.length > 0) {
7010
+ lineNumber++;
7011
+ const ln = capSubject(leftover);
7012
+ re.lastIndex = 0;
7013
+ if (re.test(ln)) {
7014
+ fileHits++;
7015
+ total++;
7016
+ if (mode === "content") {
7017
+ if (matches.length < limit) matches.push(`${full}:${lineNumber}:${ln}`);
7018
+ } else if (mode === "files_with_matches") {
7019
+ if (matches.length < limit) matches.push(full);
7020
+ } else if (matches.length < limit) {
7021
+ matches.push(`${full}:${countOnlyFirstHit ? 1 : fileHits}`);
7022
+ }
7023
+ }
6719
7024
  }
6720
- if (mode === "count" && matches.length < limit) {
6721
- matches.push(`${full}:${fileHits}`);
7025
+ if (fileHits > 0) {
7026
+ if (mode === "count") {
7027
+ const idx = matches.findIndex((entry) => entry.startsWith(`${full}:`));
7028
+ if (idx !== -1) matches[idx] = `${full}:${fileHits}`;
7029
+ }
7030
+ if (mode === "files_with_matches" && matches.length >= limit) stopped = true;
6722
7031
  }
7032
+ if (mode === "content" && matches.length >= limit) stopped = true;
7033
+ if (mode === "count" && matches.length >= limit && (countOnlyFirstHit || mode !== "count")) stopped = true;
7034
+ } finally {
7035
+ await file.close();
6723
7036
  }
6724
- if (matches.length >= limit) stopped = true;
6725
7037
  } catch {
6726
7038
  }
6727
7039
  };
@@ -6734,18 +7046,20 @@ async function runNative(input, base, mode, limit, signal) {
6734
7046
  return;
6735
7047
  }
6736
7048
  const files = [];
7049
+ const subdirs = [];
6737
7050
  for (const e of entries) {
6738
7051
  if (stopped) return;
6739
7052
  if (DEFAULT_IGNORE3.includes(e.name)) continue;
6740
7053
  if (e.isSymbolicLink()) continue;
6741
7054
  const full = path3.join(dir, e.name);
6742
7055
  if (e.isDirectory()) {
6743
- await walk(full);
7056
+ subdirs.push(full);
6744
7057
  } else if (e.isFile()) {
6745
7058
  files.push({ full, name: e.name });
6746
7059
  }
6747
7060
  }
6748
7061
  await mapWithConcurrency(files, NATIVE_SCAN_CONCURRENCY, ({ full, name }) => scanFile(full, name));
7062
+ await mapWithConcurrency(subdirs, Math.min(16, NATIVE_SCAN_CONCURRENCY), walk);
6749
7063
  };
6750
7064
  await walk(base);
6751
7065
  return {
@@ -6755,20 +7069,6 @@ async function runNative(input, base, mode, limit, signal) {
6755
7069
  used: "native"
6756
7070
  };
6757
7071
  }
6758
- async function mapWithConcurrency(items, concurrency, fn) {
6759
- if (items.length === 0) return;
6760
- let next = 0;
6761
- const workerCount = Math.min(Math.max(1, concurrency), items.length);
6762
- const workers = Array.from({ length: workerCount }, async () => {
6763
- for (; ; ) {
6764
- const idx = next++;
6765
- if (idx >= items.length) return;
6766
- const item = items[idx];
6767
- if (item !== void 0) await fn(item);
6768
- }
6769
- });
6770
- await Promise.all(workers);
6771
- }
6772
7072
  var installTool = {
6773
7073
  name: "install",
6774
7074
  category: "Package Management",
@@ -7813,13 +8113,13 @@ ${formatTaskList(taskFile.tasks)}`
7813
8113
  }
7814
8114
  };
7815
8115
  function mkResult(plan, ok, message, todos) {
7816
- const open = plan.items.filter((i) => i.status !== "done").length;
8116
+ const open2 = plan.items.filter((i) => i.status !== "done").length;
7817
8117
  const result = {
7818
8118
  ok,
7819
8119
  message,
7820
8120
  plan: formatPlan(plan),
7821
8121
  count: plan.items.length,
7822
- open
8122
+ open: open2
7823
8123
  };
7824
8124
  if (todos !== void 0) result.todos = todos;
7825
8125
  return result;
@@ -7868,7 +8168,7 @@ var readTool = {
7868
8168
  } catch (err) {
7869
8169
  const code = err.code;
7870
8170
  if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
7871
- throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage$1(err)}`);
8171
+ throw new Error(`read: failed to stat "${input.path}": ${toErrorMessage$2(err)}`);
7872
8172
  }
7873
8173
  if (!stat11.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
7874
8174
  if (stat11.size > MAX_BYTES2) {
@@ -8464,7 +8764,7 @@ async function duckduckgoSearch(query2, num, signal) {
8464
8764
  truncated: results.length >= num
8465
8765
  };
8466
8766
  } catch (err) {
8467
- console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$2(err) }));
8767
+ console.log(JSON.stringify({ level: "debug", event: "search_failed", query: query2, error: toErrorMessage$3(err) }));
8468
8768
  return {
8469
8769
  query: query2,
8470
8770
  results: [{ title: "Search unavailable", url: "", snippet: "Could not reach DuckDuckGo" }],
@@ -8628,7 +8928,7 @@ var setWorkingDirTool = {
8628
8928
  } catch (err) {
8629
8929
  return {
8630
8930
  current: ctx.workingDir,
8631
- error: toErrorMessage$2(err)
8931
+ error: toErrorMessage$3(err)
8632
8932
  };
8633
8933
  }
8634
8934
  try {