@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/audit.js.map +1 -1
- package/dist/bash.js +193 -14
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +461 -161
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.js +24 -16
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/worker.js +24 -16
- package/dist/codebase-index/worker.js.map +1 -1
- package/dist/exec.d.ts +20 -1
- package/dist/exec.js +132 -71
- package/dist/exec.js.map +1 -1
- package/dist/format.js.map +1 -1
- package/dist/glob.js +61 -11
- package/dist/glob.js.map +1 -1
- package/dist/grep.js +92 -39
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +111 -2
- package/dist/index.js +532 -191
- package/dist/index.js.map +1 -1
- package/dist/install.js.map +1 -1
- package/dist/lint.js.map +1 -1
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +461 -161
- package/dist/pack.js.map +1 -1
- package/dist/test.js.map +1 -1
- package/dist/typecheck.js.map +1 -1
- package/package.json +2 -2
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$
|
|
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$
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
3781
|
+
var _cachedScriptPath = null;
|
|
3782
|
+
async function syncPyParse(filePath, content, lang) {
|
|
3622
3783
|
try {
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
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
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
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
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6386
|
-
if (truncated) return;
|
|
6635
|
+
subdirs.push({ full, rel });
|
|
6387
6636
|
} else if (e.isFile()) {
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
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
|
|
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 >
|
|
6698
|
-
const
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
const
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
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
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
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 (
|
|
6721
|
-
|
|
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
|
-
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
8931
|
+
error: toErrorMessage$3(err)
|
|
8632
8932
|
};
|
|
8633
8933
|
}
|
|
8634
8934
|
try {
|