@wrongstack/tools 0.141.0 → 0.155.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 +34 -8
- package/dist/audit.js.map +1 -1
- package/dist/{background-indexer-C2014mH0.d.ts → background-indexer-CtbgPExj.d.ts} +1 -0
- package/dist/builtin.js +168 -116
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.d.ts +9 -2
- package/dist/codebase-index/index.js +33 -10
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/exec.js +26 -2
- package/dist/exec.js.map +1 -1
- package/dist/format.js +34 -8
- package/dist/format.js.map +1 -1
- package/dist/grep.js +1 -1
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +80 -27
- package/dist/index.js.map +1 -1
- package/dist/install.js +34 -8
- package/dist/install.js.map +1 -1
- package/dist/lint.js +34 -8
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +1 -1
- package/dist/logs.js.map +1 -1
- package/dist/outdated.js +24 -1
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +168 -116
- package/dist/pack.js.map +1 -1
- package/dist/replace.js +1 -1
- package/dist/replace.js.map +1 -1
- package/dist/test.js +37 -11
- package/dist/test.js.map +1 -1
- package/dist/typecheck.js +36 -10
- package/dist/typecheck.js.map +1 -1
- package/package.json +2 -2
package/dist/builtin.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import * as Core from '@wrongstack/core';
|
|
3
3
|
import { buildChildEnv, expectDefined, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, compileGlob, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, loadTasks, emptyTaskFile, saveTasks, computeTaskItemProgress, formatTaskList, resolveWstackPaths } from '@wrongstack/core';
|
|
4
|
-
import * as
|
|
5
|
-
import
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import { statSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import * as path2 from 'node:path';
|
|
6
7
|
import { resolve, sep, dirname } from 'node:path';
|
|
8
|
+
import * as fs13 from 'node:fs/promises';
|
|
7
9
|
import * as os from 'node:os';
|
|
8
10
|
import { createRequire } from 'node:module';
|
|
9
|
-
import * as fs from 'node:fs';
|
|
10
|
-
import { statSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
11
11
|
import * as ts from 'typescript';
|
|
12
12
|
import * as dns from 'node:dns/promises';
|
|
13
13
|
import * as net from 'node:net';
|
|
14
14
|
import { Agent } from 'undici';
|
|
15
15
|
|
|
16
|
+
// src/_spawn-stream.ts
|
|
17
|
+
function resolveWin32Command(cmd) {
|
|
18
|
+
if (process.platform !== "win32") return cmd;
|
|
19
|
+
if (cmd.includes("/") || cmd.includes("\\") || path2.extname(cmd)) {
|
|
20
|
+
return cmd;
|
|
21
|
+
}
|
|
22
|
+
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
23
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path2.delimiter);
|
|
24
|
+
for (const dir of pathDirs) {
|
|
25
|
+
const base = path2.join(dir, cmd);
|
|
26
|
+
for (const ext of pathext) {
|
|
27
|
+
const full = `${base}${ext}`;
|
|
28
|
+
try {
|
|
29
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
30
|
+
return full;
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return cmd;
|
|
36
|
+
}
|
|
37
|
+
|
|
16
38
|
// src/_spawn-stream.ts
|
|
17
39
|
async function* spawnStream(opts) {
|
|
18
40
|
const max = opts.maxBytes ?? 2e5;
|
|
@@ -21,11 +43,14 @@ async function* spawnStream(opts) {
|
|
|
21
43
|
let stderr = "";
|
|
22
44
|
let pending = "";
|
|
23
45
|
let error;
|
|
24
|
-
const
|
|
46
|
+
const cmd = resolveWin32Command(opts.cmd);
|
|
47
|
+
const needsShell = process.platform === "win32" && (cmd.endsWith(".cmd") || cmd.endsWith(".bat"));
|
|
48
|
+
const child = spawn(cmd, opts.args, {
|
|
25
49
|
cwd: opts.cwd,
|
|
26
50
|
signal: opts.signal,
|
|
27
51
|
env: buildChildEnv(),
|
|
28
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
52
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
53
|
+
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
29
54
|
});
|
|
30
55
|
const queue = [];
|
|
31
56
|
let waiter;
|
|
@@ -107,13 +132,13 @@ async function detectPackageManager(cwd) {
|
|
|
107
132
|
return "npm";
|
|
108
133
|
}
|
|
109
134
|
function resolvePath(input, ctx) {
|
|
110
|
-
return
|
|
135
|
+
return path2.isAbsolute(input) ? path2.normalize(input) : path2.resolve(ctx.cwd, input);
|
|
111
136
|
}
|
|
112
137
|
function ensureInsideRoot(absPath, ctx) {
|
|
113
|
-
const root =
|
|
114
|
-
const target =
|
|
115
|
-
const rel =
|
|
116
|
-
if (rel.startsWith("..") ||
|
|
138
|
+
const root = path2.resolve(ctx.projectRoot);
|
|
139
|
+
const target = path2.resolve(absPath);
|
|
140
|
+
const rel = path2.relative(root, target);
|
|
141
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
117
142
|
throw new Error(`Path "${absPath}" is outside project root "${root}"`);
|
|
118
143
|
}
|
|
119
144
|
return target;
|
|
@@ -122,23 +147,23 @@ function safeResolve(input, ctx) {
|
|
|
122
147
|
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
123
148
|
}
|
|
124
149
|
async function assertRealInsideRoot(absPath, ctx) {
|
|
125
|
-
const realRoot = await
|
|
150
|
+
const realRoot = await fs13.realpath(ctx.projectRoot).catch(() => path2.resolve(ctx.projectRoot));
|
|
126
151
|
let probe = absPath;
|
|
127
152
|
for (; ; ) {
|
|
128
153
|
let real;
|
|
129
154
|
try {
|
|
130
|
-
real = await
|
|
155
|
+
real = await fs13.realpath(probe);
|
|
131
156
|
} catch (err) {
|
|
132
157
|
if (err.code === "ENOENT") {
|
|
133
|
-
const parent =
|
|
158
|
+
const parent = path2.dirname(probe);
|
|
134
159
|
if (parent === probe) return;
|
|
135
160
|
probe = parent;
|
|
136
161
|
continue;
|
|
137
162
|
}
|
|
138
163
|
throw err;
|
|
139
164
|
}
|
|
140
|
-
const rel =
|
|
141
|
-
if (rel.startsWith("..") ||
|
|
165
|
+
const rel = path2.relative(realRoot, real);
|
|
166
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
142
167
|
throw new Error(
|
|
143
168
|
`Path "${absPath}" resolves through a symlink outside project root "${realRoot}"`
|
|
144
169
|
);
|
|
@@ -1142,7 +1167,7 @@ var IndexStore = class {
|
|
|
1142
1167
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
1143
1168
|
fs.mkdirSync(this.indexDir, { recursive: true });
|
|
1144
1169
|
const Database = loadDatabaseSync();
|
|
1145
|
-
this.db = new Database(
|
|
1170
|
+
this.db = new Database(path2.join(this.indexDir, DB_FILE));
|
|
1146
1171
|
this.initSchema();
|
|
1147
1172
|
}
|
|
1148
1173
|
initSchema() {
|
|
@@ -1440,7 +1465,7 @@ var IndexStore = class {
|
|
|
1440
1465
|
}));
|
|
1441
1466
|
}
|
|
1442
1467
|
sizeBytes() {
|
|
1443
|
-
const dbPath =
|
|
1468
|
+
const dbPath = path2.join(this.indexDir, DB_FILE);
|
|
1444
1469
|
try {
|
|
1445
1470
|
return fs.statSync(dbPath).size;
|
|
1446
1471
|
} catch {
|
|
@@ -1879,10 +1904,10 @@ func formatType(t ast.Expr) string {
|
|
|
1879
1904
|
}
|
|
1880
1905
|
`;
|
|
1881
1906
|
function syncGoParse(filePath, content, lang) {
|
|
1882
|
-
const tmpDir =
|
|
1907
|
+
const tmpDir = path2.join(os.tmpdir(), "ws-go-parse");
|
|
1883
1908
|
try {
|
|
1884
1909
|
mkdirSync(tmpDir, { recursive: true });
|
|
1885
|
-
const scriptPath =
|
|
1910
|
+
const scriptPath = path2.join(tmpDir, "parse.go");
|
|
1886
1911
|
writeFileSync(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
1887
1912
|
const stdout = execFileSync("go", ["run", scriptPath], {
|
|
1888
1913
|
input: content,
|
|
@@ -2126,9 +2151,9 @@ print(json.dumps([s.to_dict() for s in syms]))
|
|
|
2126
2151
|
`;
|
|
2127
2152
|
function syncPyParse(filePath, lang) {
|
|
2128
2153
|
try {
|
|
2129
|
-
const tmpDir =
|
|
2154
|
+
const tmpDir = path2.join(os.tmpdir(), "ws-py-parse");
|
|
2130
2155
|
mkdirSync(tmpDir, { recursive: true });
|
|
2131
|
-
const scriptPath =
|
|
2156
|
+
const scriptPath = path2.join(tmpDir, "parse.py");
|
|
2132
2157
|
writeFileSync(scriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
2133
2158
|
const stdout = execFileSync("python", [scriptPath, filePath], {
|
|
2134
2159
|
timeout: 15e3,
|
|
@@ -2169,7 +2194,7 @@ function parseSymbols4(opts) {
|
|
|
2169
2194
|
function checkNativeParser() {
|
|
2170
2195
|
try {
|
|
2171
2196
|
execFileSync("rustc", ["--version"], { stdio: "pipe" });
|
|
2172
|
-
const toolsDir =
|
|
2197
|
+
const toolsDir = path2.join(process.cwd(), "tools");
|
|
2173
2198
|
try {
|
|
2174
2199
|
execFileSync(
|
|
2175
2200
|
"cargo",
|
|
@@ -2179,7 +2204,7 @@ function checkNativeParser() {
|
|
|
2179
2204
|
"--format-version",
|
|
2180
2205
|
"1",
|
|
2181
2206
|
"--manifest-path",
|
|
2182
|
-
|
|
2207
|
+
path2.join(toolsDir, "Cargo.toml")
|
|
2183
2208
|
],
|
|
2184
2209
|
{ stdio: "pipe" }
|
|
2185
2210
|
);
|
|
@@ -2193,13 +2218,13 @@ function checkNativeParser() {
|
|
|
2193
2218
|
}
|
|
2194
2219
|
function tryNativeParse(file, content) {
|
|
2195
2220
|
try {
|
|
2196
|
-
const toolsDir =
|
|
2197
|
-
const crateDir =
|
|
2198
|
-
const tmpFile =
|
|
2221
|
+
const toolsDir = path2.join(process.cwd(), "tools");
|
|
2222
|
+
const crateDir = path2.join(toolsDir, "syn-parser");
|
|
2223
|
+
const tmpFile = path2.join(crateDir, "src", "input.rs");
|
|
2199
2224
|
writeFileSync(tmpFile, content, "utf8");
|
|
2200
2225
|
const result = spawnSync(
|
|
2201
2226
|
"cargo",
|
|
2202
|
-
["run", "--manifest-path",
|
|
2227
|
+
["run", "--manifest-path", path2.join(toolsDir, "Cargo.toml")],
|
|
2203
2228
|
{
|
|
2204
2229
|
cwd: process.cwd(),
|
|
2205
2230
|
encoding: "utf8",
|
|
@@ -2297,7 +2322,7 @@ function parseSymbols5(opts) {
|
|
|
2297
2322
|
function regexParse2(opts) {
|
|
2298
2323
|
const { file, content, lang } = opts;
|
|
2299
2324
|
const symbols = [];
|
|
2300
|
-
const basename2 =
|
|
2325
|
+
const basename2 = path2.basename(file).toLowerCase();
|
|
2301
2326
|
const isPackageJson = basename2 === "package.json";
|
|
2302
2327
|
const isTsconfig = basename2 === "tsconfig.json" || basename2 === "tsconfig.build.json";
|
|
2303
2328
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -2323,11 +2348,11 @@ function regexParse2(opts) {
|
|
|
2323
2348
|
const line = lineFromOffset(offset);
|
|
2324
2349
|
symbols.push(
|
|
2325
2350
|
makeSymbol({
|
|
2326
|
-
name:
|
|
2351
|
+
name: path2.basename(file),
|
|
2327
2352
|
kind: "object",
|
|
2328
2353
|
line,
|
|
2329
2354
|
col: 0,
|
|
2330
|
-
signature: `"${
|
|
2355
|
+
signature: `"${path2.basename(file)}" = { ... }`,
|
|
2331
2356
|
file,
|
|
2332
2357
|
lang
|
|
2333
2358
|
})
|
|
@@ -2681,7 +2706,7 @@ function compileGitignore(lines) {
|
|
|
2681
2706
|
async function loadGitignoreMatcher(projectRoot) {
|
|
2682
2707
|
let lines = [];
|
|
2683
2708
|
try {
|
|
2684
|
-
const raw = await
|
|
2709
|
+
const raw = await fs13.readFile(path2.join(projectRoot, ".gitignore"), "utf8");
|
|
2685
2710
|
lines = raw.split("\n");
|
|
2686
2711
|
} catch {
|
|
2687
2712
|
}
|
|
@@ -2723,6 +2748,16 @@ var YIELD_EVERY_N = 50;
|
|
|
2723
2748
|
function yieldEventLoop() {
|
|
2724
2749
|
return new Promise((resolve7) => setImmediate(resolve7));
|
|
2725
2750
|
}
|
|
2751
|
+
function throwIfAborted(signal) {
|
|
2752
|
+
if (!signal?.aborted) return;
|
|
2753
|
+
if (signal.reason instanceof Error) throw signal.reason;
|
|
2754
|
+
throw new Error(
|
|
2755
|
+
typeof signal.reason === "string" ? signal.reason : "Indexing cancelled"
|
|
2756
|
+
);
|
|
2757
|
+
}
|
|
2758
|
+
function isAbortError(err) {
|
|
2759
|
+
return err instanceof DOMException && err.name === "AbortError";
|
|
2760
|
+
}
|
|
2726
2761
|
var DEFAULT_IGNORE = [
|
|
2727
2762
|
"node_modules",
|
|
2728
2763
|
".git",
|
|
@@ -2734,7 +2769,7 @@ var DEFAULT_IGNORE = [
|
|
|
2734
2769
|
"__snapshots__",
|
|
2735
2770
|
".nyc_output"
|
|
2736
2771
|
];
|
|
2737
|
-
async function findSourceFiles(projectRoot, ignore, isGitIgnored) {
|
|
2772
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored, signal) {
|
|
2738
2773
|
const results = [];
|
|
2739
2774
|
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
2740
2775
|
const globs = [
|
|
@@ -2749,23 +2784,30 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored) {
|
|
|
2749
2784
|
{ ext: ".yaml", pat: compileGlob("**/*.yaml") },
|
|
2750
2785
|
{ ext: ".yml", pat: compileGlob("**/*.yml") }
|
|
2751
2786
|
];
|
|
2787
|
+
let dirCount = 0;
|
|
2752
2788
|
const walk = async (dir) => {
|
|
2789
|
+
throwIfAborted(signal);
|
|
2790
|
+
if (dirCount > 0 && dirCount % YIELD_EVERY_N === 0) {
|
|
2791
|
+
await yieldEventLoop();
|
|
2792
|
+
throwIfAborted(signal);
|
|
2793
|
+
}
|
|
2753
2794
|
let entries;
|
|
2754
2795
|
try {
|
|
2755
|
-
entries = await
|
|
2796
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
2756
2797
|
} catch {
|
|
2757
2798
|
return;
|
|
2758
2799
|
}
|
|
2800
|
+
dirCount++;
|
|
2759
2801
|
for (const e of entries) {
|
|
2760
2802
|
if (ignoreSet.has(e.name)) continue;
|
|
2761
|
-
const full =
|
|
2762
|
-
const rel =
|
|
2803
|
+
const full = path2.join(dir, e.name);
|
|
2804
|
+
const rel = path2.relative(projectRoot, full).replace(/\\/g, "/");
|
|
2763
2805
|
if (e.isDirectory()) {
|
|
2764
2806
|
if (isGitIgnored(rel, true)) continue;
|
|
2765
2807
|
await walk(full);
|
|
2766
2808
|
} else if (e.isFile()) {
|
|
2767
2809
|
if (isGitIgnored(rel, false)) continue;
|
|
2768
|
-
const ext =
|
|
2810
|
+
const ext = path2.extname(e.name);
|
|
2769
2811
|
for (const { ext: extName, pat } of globs) {
|
|
2770
2812
|
if (ext === extName && (pat.test(rel) || pat.test(e.name))) {
|
|
2771
2813
|
results.push(full);
|
|
@@ -2800,7 +2842,7 @@ async function parseFile(file, content, lang) {
|
|
|
2800
2842
|
}
|
|
2801
2843
|
}
|
|
2802
2844
|
async function runIndexer(_ctx, opts) {
|
|
2803
|
-
const { projectRoot, force = false, langs, ignore = [], indexDir } = opts;
|
|
2845
|
+
const { projectRoot, force = false, langs, ignore = [], indexDir, signal } = opts;
|
|
2804
2846
|
const store = new IndexStore(projectRoot, { indexDir });
|
|
2805
2847
|
const startMs = Date.now();
|
|
2806
2848
|
const errors = [];
|
|
@@ -2810,9 +2852,9 @@ async function runIndexer(_ctx, opts) {
|
|
|
2810
2852
|
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
2811
2853
|
let files;
|
|
2812
2854
|
if (opts.files && opts.files.length > 0) {
|
|
2813
|
-
files = opts.files.map((f) =>
|
|
2855
|
+
files = opts.files.map((f) => path2.resolve(projectRoot, f)).filter((f) => !isGitIgnored(path2.relative(projectRoot, f).replace(/\\/g, "/"), false));
|
|
2814
2856
|
} else {
|
|
2815
|
-
files = await findSourceFiles(projectRoot, ignore, isGitIgnored);
|
|
2857
|
+
files = await findSourceFiles(projectRoot, ignore, isGitIgnored, signal);
|
|
2816
2858
|
}
|
|
2817
2859
|
if (langs && langs.length > 0) {
|
|
2818
2860
|
const langSet = new Set(langs);
|
|
@@ -2831,11 +2873,14 @@ async function runIndexer(_ctx, opts) {
|
|
|
2831
2873
|
_setIndexProgress(fi + 1, files.length);
|
|
2832
2874
|
if (fi > 0 && fi % YIELD_EVERY_N === 0) {
|
|
2833
2875
|
await yieldEventLoop();
|
|
2876
|
+
throwIfAborted(signal);
|
|
2834
2877
|
}
|
|
2835
2878
|
let stat10;
|
|
2836
2879
|
try {
|
|
2837
|
-
|
|
2838
|
-
|
|
2880
|
+
const statOpts = signal ? { signal } : {};
|
|
2881
|
+
stat10 = await fs13.stat(file, statOpts);
|
|
2882
|
+
} catch (e) {
|
|
2883
|
+
if (isAbortError(e)) throw e;
|
|
2839
2884
|
store.deleteFile(file);
|
|
2840
2885
|
continue;
|
|
2841
2886
|
}
|
|
@@ -2853,8 +2898,9 @@ async function runIndexer(_ctx, opts) {
|
|
|
2853
2898
|
store.deleteSymbolsForFile(file);
|
|
2854
2899
|
let content;
|
|
2855
2900
|
try {
|
|
2856
|
-
content = await
|
|
2901
|
+
content = await fs13.readFile(file, { encoding: "utf8", signal });
|
|
2857
2902
|
} catch (e) {
|
|
2903
|
+
if (isAbortError(e)) throw e;
|
|
2858
2904
|
errors.push(`read error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
2859
2905
|
continue;
|
|
2860
2906
|
}
|
|
@@ -2903,7 +2949,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2903
2949
|
}
|
|
2904
2950
|
for (const [file_] of existingMeta) {
|
|
2905
2951
|
try {
|
|
2906
|
-
await
|
|
2952
|
+
await fs13.stat(file_);
|
|
2907
2953
|
} catch {
|
|
2908
2954
|
store.deleteFile(file_);
|
|
2909
2955
|
}
|
|
@@ -2944,12 +2990,13 @@ var codebaseIndexTool = {
|
|
|
2944
2990
|
}
|
|
2945
2991
|
}
|
|
2946
2992
|
},
|
|
2947
|
-
async execute(input, ctx) {
|
|
2993
|
+
async execute(input, ctx, execOpts) {
|
|
2948
2994
|
const result = await runIndexer(ctx, {
|
|
2949
2995
|
projectRoot: ctx.projectRoot,
|
|
2950
2996
|
force: input.force ?? false,
|
|
2951
2997
|
langs: input.langs,
|
|
2952
|
-
indexDir: codebaseIndexDirOverride(ctx)
|
|
2998
|
+
indexDir: codebaseIndexDirOverride(ctx),
|
|
2999
|
+
signal: execOpts?.signal
|
|
2953
3000
|
});
|
|
2954
3001
|
setIndexReady();
|
|
2955
3002
|
return result;
|
|
@@ -2964,7 +3011,7 @@ function tokenise(text) {
|
|
|
2964
3011
|
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
2965
3012
|
}
|
|
2966
3013
|
function splitName(name) {
|
|
2967
|
-
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_
|
|
3014
|
+
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
2968
3015
|
}
|
|
2969
3016
|
function buildIndexableText(name, signature, docComment) {
|
|
2970
3017
|
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
@@ -3294,11 +3341,11 @@ function findGitDir(cwd) {
|
|
|
3294
3341
|
let dir = cwd;
|
|
3295
3342
|
for (let i = 0; i < 20; i++) {
|
|
3296
3343
|
try {
|
|
3297
|
-
const stat10 = statSync(
|
|
3344
|
+
const stat10 = statSync(path2.join(dir, ".git"));
|
|
3298
3345
|
if (stat10.isDirectory()) return dir;
|
|
3299
3346
|
} catch {
|
|
3300
3347
|
}
|
|
3301
|
-
const parent =
|
|
3348
|
+
const parent = path2.dirname(dir);
|
|
3302
3349
|
if (parent === dir) break;
|
|
3303
3350
|
dir = parent;
|
|
3304
3351
|
}
|
|
@@ -3338,9 +3385,9 @@ async function fileDiff(input, ctx, _signal) {
|
|
|
3338
3385
|
const results = [];
|
|
3339
3386
|
for (const file of files) {
|
|
3340
3387
|
const absPath = safeResolve(file, ctx);
|
|
3341
|
-
const stat10 = await
|
|
3388
|
+
const stat10 = await fs13.stat(absPath).catch(() => null);
|
|
3342
3389
|
if (!stat10?.isFile()) continue;
|
|
3343
|
-
const content = await
|
|
3390
|
+
const content = await fs13.readFile(absPath, "utf8");
|
|
3344
3391
|
const lines = content.split(/\r?\n/);
|
|
3345
3392
|
results.push(formatWithLineNumbers(file, lines));
|
|
3346
3393
|
}
|
|
@@ -3402,7 +3449,7 @@ var documentTool = {
|
|
|
3402
3449
|
const fileList = input.files ? await resolveFiles(Array.isArray(input.files) ? input.files.join(",") : input.files, cwd) : input.path ? [safeResolve(input.path, ctx)] : [];
|
|
3403
3450
|
for (const absPath of fileList) {
|
|
3404
3451
|
try {
|
|
3405
|
-
const content = await
|
|
3452
|
+
const content = await fs13.readFile(absPath, "utf8");
|
|
3406
3453
|
filesProcessed++;
|
|
3407
3454
|
const processed = processFile(
|
|
3408
3455
|
content,
|
|
@@ -3438,7 +3485,7 @@ async function resolveFiles(filesInput, cwd) {
|
|
|
3438
3485
|
for (const f of files) {
|
|
3439
3486
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
3440
3487
|
try {
|
|
3441
|
-
const stat10 = await
|
|
3488
|
+
const stat10 = await fs13.stat(absPath);
|
|
3442
3489
|
if (stat10.isFile()) resolved.push(absPath);
|
|
3443
3490
|
} catch {
|
|
3444
3491
|
}
|
|
@@ -3530,7 +3577,7 @@ var editTool = {
|
|
|
3530
3577
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
3531
3578
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
3532
3579
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
3533
|
-
const stat10 = await
|
|
3580
|
+
const stat10 = await fs13.stat(absPath).catch((err) => {
|
|
3534
3581
|
if (err.code === "ENOENT") {
|
|
3535
3582
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
3536
3583
|
}
|
|
@@ -3540,8 +3587,8 @@ var editTool = {
|
|
|
3540
3587
|
if (!ctx.hasRead(absPath)) {
|
|
3541
3588
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
3542
3589
|
}
|
|
3543
|
-
const original = await
|
|
3544
|
-
const updated = await
|
|
3590
|
+
const original = await fs13.readFile(absPath, "utf8");
|
|
3591
|
+
const updated = await fs13.stat(absPath);
|
|
3545
3592
|
const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
|
|
3546
3593
|
const lastReadMtime = ctx.lastReadMtime(absPath);
|
|
3547
3594
|
if (lastReadMtime !== void 0 && updated.mtimeMs > lastReadMtime + mtimeTolerance) {
|
|
@@ -3581,7 +3628,7 @@ var editTool = {
|
|
|
3581
3628
|
const newFileLf = input.replace_all ? fileLf.split(oldLf).join(newLf) : fileLf.replace(oldLf, newLf);
|
|
3582
3629
|
const newFile = toStyle(newFileLf, style);
|
|
3583
3630
|
await atomicWrite(absPath, newFile, { mode: updated.mode & 511 });
|
|
3584
|
-
const written = await
|
|
3631
|
+
const written = await fs13.stat(absPath);
|
|
3585
3632
|
ctx.recordRead(absPath, written.mtimeMs);
|
|
3586
3633
|
ctx.session.recordFileChange({
|
|
3587
3634
|
path: absPath,
|
|
@@ -3806,9 +3853,9 @@ var execTool = {
|
|
|
3806
3853
|
allowed: false
|
|
3807
3854
|
};
|
|
3808
3855
|
}
|
|
3809
|
-
const requestedCwd = input.cwd ?
|
|
3810
|
-
const rel =
|
|
3811
|
-
if (rel.startsWith("..") ||
|
|
3856
|
+
const requestedCwd = input.cwd ? path2.resolve(ctx.projectRoot, input.cwd) : ctx.cwd;
|
|
3857
|
+
const rel = path2.relative(ctx.projectRoot, requestedCwd);
|
|
3858
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
3812
3859
|
return {
|
|
3813
3860
|
command: cmd,
|
|
3814
3861
|
args,
|
|
@@ -3830,11 +3877,14 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
3830
3877
|
let stderr = "";
|
|
3831
3878
|
let killed = false;
|
|
3832
3879
|
const startedAt = Date.now();
|
|
3833
|
-
const
|
|
3880
|
+
const resolved = resolveWin32Command(cmd);
|
|
3881
|
+
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
3882
|
+
const child = spawn(resolved, args, {
|
|
3834
3883
|
cwd,
|
|
3835
3884
|
signal,
|
|
3836
3885
|
env: buildChildEnv(sessionId),
|
|
3837
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3886
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3887
|
+
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
3838
3888
|
});
|
|
3839
3889
|
const registry = getProcessRegistry();
|
|
3840
3890
|
const pid = child.pid;
|
|
@@ -4617,7 +4667,7 @@ var globTool = {
|
|
|
4617
4667
|
}
|
|
4618
4668
|
let entries;
|
|
4619
4669
|
try {
|
|
4620
|
-
entries = await
|
|
4670
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
4621
4671
|
} catch {
|
|
4622
4672
|
return;
|
|
4623
4673
|
}
|
|
@@ -4626,14 +4676,14 @@ var globTool = {
|
|
|
4626
4676
|
if (DEFAULT_IGNORE2.includes(name)) continue;
|
|
4627
4677
|
if (ignored.includes(name)) continue;
|
|
4628
4678
|
const rel = relPrefix ? `${relPrefix}/${name}` : name;
|
|
4629
|
-
const full =
|
|
4679
|
+
const full = path2.join(dir, name);
|
|
4630
4680
|
if (e.isDirectory()) {
|
|
4631
4681
|
await walk(full, rel);
|
|
4632
4682
|
if (truncated) return;
|
|
4633
4683
|
} else if (e.isFile()) {
|
|
4634
4684
|
if (re.test(rel) || re.test(name)) {
|
|
4635
4685
|
try {
|
|
4636
|
-
const st = await
|
|
4686
|
+
const st = await fs13.stat(full);
|
|
4637
4687
|
results.push({ rel: full, mtime: st.mtimeMs });
|
|
4638
4688
|
if (results.length >= limit) {
|
|
4639
4689
|
truncated = true;
|
|
@@ -4652,7 +4702,7 @@ var globTool = {
|
|
|
4652
4702
|
};
|
|
4653
4703
|
async function readGitignore(dir) {
|
|
4654
4704
|
try {
|
|
4655
|
-
const raw = await
|
|
4705
|
+
const raw = await fs13.readFile(path2.join(dir, ".gitignore"), "utf8");
|
|
4656
4706
|
return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
4657
4707
|
} catch {
|
|
4658
4708
|
return [];
|
|
@@ -4670,7 +4720,7 @@ var DANGEROUS_PATTERNS = [
|
|
|
4670
4720
|
// Quantifier on alternation with length 2+
|
|
4671
4721
|
/\([^|)]+\|[^)]+\)[+*][+*]/,
|
|
4672
4722
|
// Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
|
|
4673
|
-
/[
|
|
4723
|
+
/[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
|
|
4674
4724
|
];
|
|
4675
4725
|
function compileUserRegex(pattern, flags) {
|
|
4676
4726
|
if (typeof pattern !== "string") {
|
|
@@ -4936,7 +4986,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4936
4986
|
if (stopped || signal.aborted) return;
|
|
4937
4987
|
let entries;
|
|
4938
4988
|
try {
|
|
4939
|
-
entries = await
|
|
4989
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
4940
4990
|
} catch {
|
|
4941
4991
|
return;
|
|
4942
4992
|
}
|
|
@@ -4944,16 +4994,16 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4944
4994
|
if (stopped) return;
|
|
4945
4995
|
if (DEFAULT_IGNORE3.includes(e.name)) continue;
|
|
4946
4996
|
if (e.isSymbolicLink()) continue;
|
|
4947
|
-
const full =
|
|
4997
|
+
const full = path2.join(dir, e.name);
|
|
4948
4998
|
if (e.isDirectory()) {
|
|
4949
4999
|
await walk(full);
|
|
4950
5000
|
} else if (e.isFile()) {
|
|
4951
5001
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
4952
5002
|
if (globRe) globRe.lastIndex = 0;
|
|
4953
5003
|
try {
|
|
4954
|
-
const stat10 = await
|
|
5004
|
+
const stat10 = await fs13.stat(full);
|
|
4955
5005
|
if (stat10.size > 1e6) continue;
|
|
4956
|
-
const head = await
|
|
5006
|
+
const head = await fs13.readFile(full);
|
|
4957
5007
|
if (isBinaryBuffer(head)) continue;
|
|
4958
5008
|
const text = head.toString("utf8");
|
|
4959
5009
|
const lines = text.split(/\r?\n/);
|
|
@@ -5134,7 +5184,7 @@ var jsonTool = {
|
|
|
5134
5184
|
let raw;
|
|
5135
5185
|
if (input.file) {
|
|
5136
5186
|
try {
|
|
5137
|
-
raw = await
|
|
5187
|
+
raw = await fs13.readFile(input.file, "utf8");
|
|
5138
5188
|
} catch {
|
|
5139
5189
|
return { data: null, formatted: "", type: "unknown", error: `Could not read file` };
|
|
5140
5190
|
}
|
|
@@ -5172,8 +5222,8 @@ var jsonTool = {
|
|
|
5172
5222
|
};
|
|
5173
5223
|
}
|
|
5174
5224
|
};
|
|
5175
|
-
function query(data,
|
|
5176
|
-
const parts =
|
|
5225
|
+
function query(data, path20) {
|
|
5226
|
+
const parts = path20.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
5177
5227
|
let current = data;
|
|
5178
5228
|
for (const part of parts) {
|
|
5179
5229
|
if (current === null || current === void 0) return void 0;
|
|
@@ -5444,7 +5494,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
5444
5494
|
}
|
|
5445
5495
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
5446
5496
|
var MAX_TAIL_LINES = 1e5;
|
|
5447
|
-
async function fileLogs(
|
|
5497
|
+
async function fileLogs(path20, lines, filterRe, stream) {
|
|
5448
5498
|
const { createInterface } = await import('node:readline');
|
|
5449
5499
|
const { createReadStream } = await import('node:fs');
|
|
5450
5500
|
const entries = [];
|
|
@@ -5453,7 +5503,7 @@ async function fileLogs(path19, lines, filterRe, stream) {
|
|
|
5453
5503
|
let writeIdx = 0;
|
|
5454
5504
|
let totalLines = 0;
|
|
5455
5505
|
const rl = createInterface({
|
|
5456
|
-
input: createReadStream(
|
|
5506
|
+
input: createReadStream(path20),
|
|
5457
5507
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
5458
5508
|
});
|
|
5459
5509
|
for await (const line of rl) {
|
|
@@ -5474,7 +5524,7 @@ async function fileLogs(path19, lines, filterRe, stream) {
|
|
|
5474
5524
|
if (parsed) entries.push(parsed);
|
|
5475
5525
|
}
|
|
5476
5526
|
return {
|
|
5477
|
-
source:
|
|
5527
|
+
source: path20,
|
|
5478
5528
|
entries,
|
|
5479
5529
|
total: entries.length,
|
|
5480
5530
|
truncated: totalLines > effLines,
|
|
@@ -5557,7 +5607,9 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
5557
5607
|
let stdout = "";
|
|
5558
5608
|
let stderr = "";
|
|
5559
5609
|
const MAX = 1e5;
|
|
5560
|
-
const
|
|
5610
|
+
const resolved = resolveWin32Command(manager);
|
|
5611
|
+
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
5612
|
+
const child = spawn(resolved, args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {} });
|
|
5561
5613
|
child.stdout?.on("data", (c) => {
|
|
5562
5614
|
if (stdout.length < MAX) stdout += c.toString();
|
|
5563
5615
|
});
|
|
@@ -5641,9 +5693,9 @@ var patchTool = {
|
|
|
5641
5693
|
for (const t of targets) {
|
|
5642
5694
|
const stripped = stripPathComponents(t, strip);
|
|
5643
5695
|
if (!stripped) continue;
|
|
5644
|
-
const candidate =
|
|
5645
|
-
const rel =
|
|
5646
|
-
if (rel.startsWith("..") ||
|
|
5696
|
+
const candidate = path2.resolve(dir, stripped);
|
|
5697
|
+
const rel = path2.relative(ctx.projectRoot, candidate);
|
|
5698
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
5647
5699
|
return {
|
|
5648
5700
|
applied: 0,
|
|
5649
5701
|
rejected: 1,
|
|
@@ -5653,12 +5705,12 @@ var patchTool = {
|
|
|
5653
5705
|
};
|
|
5654
5706
|
}
|
|
5655
5707
|
}
|
|
5656
|
-
const tmpDir = await
|
|
5708
|
+
const tmpDir = await fs13.mkdtemp(path2.join(os.tmpdir(), ".wstack_patch_"));
|
|
5657
5709
|
try {
|
|
5658
|
-
await
|
|
5710
|
+
await fs13.chmod(tmpDir, 448).catch(() => {
|
|
5659
5711
|
});
|
|
5660
|
-
const patchFile =
|
|
5661
|
-
await
|
|
5712
|
+
const patchFile = path2.join(tmpDir, "in.diff");
|
|
5713
|
+
await fs13.writeFile(patchFile, input.patch, { mode: 384 });
|
|
5662
5714
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
5663
5715
|
const result = await runPatch(args, dir, opts.signal);
|
|
5664
5716
|
if (result.exitCode !== 0 && !dryRun) {
|
|
@@ -5679,7 +5731,7 @@ var patchTool = {
|
|
|
5679
5731
|
message: result.stdout || "patch applied"
|
|
5680
5732
|
};
|
|
5681
5733
|
} finally {
|
|
5682
|
-
await
|
|
5734
|
+
await fs13.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
5683
5735
|
});
|
|
5684
5736
|
}
|
|
5685
5737
|
}
|
|
@@ -5922,7 +5974,7 @@ var readTool = {
|
|
|
5922
5974
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
5923
5975
|
let stat10;
|
|
5924
5976
|
try {
|
|
5925
|
-
stat10 = await
|
|
5977
|
+
stat10 = await fs13.stat(absPath);
|
|
5926
5978
|
} catch (err) {
|
|
5927
5979
|
const code = err.code;
|
|
5928
5980
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
@@ -5934,7 +5986,7 @@ var readTool = {
|
|
|
5934
5986
|
if (stat10.size > MAX_BYTES2) {
|
|
5935
5987
|
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES2})`);
|
|
5936
5988
|
}
|
|
5937
|
-
const buf = await
|
|
5989
|
+
const buf = await fs13.readFile(absPath);
|
|
5938
5990
|
if (isBinaryBuffer(buf)) {
|
|
5939
5991
|
throw new Error(`read: "${input.path}" appears to be binary`);
|
|
5940
5992
|
}
|
|
@@ -6002,11 +6054,11 @@ var replaceTool = {
|
|
|
6002
6054
|
const dryRun = input.dry_run ?? false;
|
|
6003
6055
|
const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
|
|
6004
6056
|
const fileList = await resolveFiles2(filesInput, ctx, globRe);
|
|
6005
|
-
const realRoot = await
|
|
6057
|
+
const realRoot = await fs13.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
6006
6058
|
const results = [];
|
|
6007
6059
|
let totalReplacements = 0;
|
|
6008
6060
|
for (const absPath of fileList) {
|
|
6009
|
-
const lstat2 = await
|
|
6061
|
+
const lstat2 = await fs13.lstat(absPath).catch((err) => {
|
|
6010
6062
|
if (err.code === "ENOENT") return null;
|
|
6011
6063
|
throw err;
|
|
6012
6064
|
});
|
|
@@ -6014,17 +6066,17 @@ var replaceTool = {
|
|
|
6014
6066
|
if (lstat2.isSymbolicLink()) continue;
|
|
6015
6067
|
let realPath;
|
|
6016
6068
|
try {
|
|
6017
|
-
realPath = await
|
|
6069
|
+
realPath = await fs13.realpath(absPath);
|
|
6018
6070
|
} catch {
|
|
6019
6071
|
continue;
|
|
6020
6072
|
}
|
|
6021
|
-
const rel =
|
|
6022
|
-
if (rel.startsWith("..") ||
|
|
6023
|
-
const stat10 = await
|
|
6073
|
+
const rel = path2.relative(realRoot, realPath);
|
|
6074
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) continue;
|
|
6075
|
+
const stat10 = await fs13.stat(realPath).catch(() => null);
|
|
6024
6076
|
if (!stat10 || !stat10.isFile()) continue;
|
|
6025
6077
|
let content;
|
|
6026
6078
|
try {
|
|
6027
|
-
const buf = await
|
|
6079
|
+
const buf = await fs13.readFile(realPath);
|
|
6028
6080
|
if (isBinaryBuffer(buf)) continue;
|
|
6029
6081
|
content = buf.toString("utf8");
|
|
6030
6082
|
} catch {
|
|
@@ -6076,7 +6128,7 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
|
|
|
6076
6128
|
const resolved = [];
|
|
6077
6129
|
for (const p of parts) {
|
|
6078
6130
|
const absPath = safeResolve(p, ctx);
|
|
6079
|
-
const stat10 = await
|
|
6131
|
+
const stat10 = await fs13.stat(absPath).catch(() => null);
|
|
6080
6132
|
if (stat10?.isFile()) {
|
|
6081
6133
|
resolved.push(absPath);
|
|
6082
6134
|
}
|
|
@@ -6131,15 +6183,15 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
6131
6183
|
const walk = async (dir) => {
|
|
6132
6184
|
let entries;
|
|
6133
6185
|
try {
|
|
6134
|
-
entries = await
|
|
6186
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
6135
6187
|
} catch {
|
|
6136
6188
|
return;
|
|
6137
6189
|
}
|
|
6138
6190
|
for (const e of entries) {
|
|
6139
6191
|
if (DEFAULT_IGNORE4.includes(e.name)) continue;
|
|
6140
|
-
const full =
|
|
6192
|
+
const full = path2.join(dir, e.name);
|
|
6141
6193
|
try {
|
|
6142
|
-
const stat10 = await
|
|
6194
|
+
const stat10 = await fs13.lstat(full);
|
|
6143
6195
|
if (stat10.isSymbolicLink()) continue;
|
|
6144
6196
|
} catch {
|
|
6145
6197
|
continue;
|
|
@@ -6307,16 +6359,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
6307
6359
|
let filesCreated = 0;
|
|
6308
6360
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
6309
6361
|
const resolvedPath = substituteVars(filePath, name, vars);
|
|
6310
|
-
const joinedPath =
|
|
6311
|
-
const root =
|
|
6312
|
-
const target =
|
|
6313
|
-
const rel =
|
|
6314
|
-
if (rel.startsWith("..") ||
|
|
6362
|
+
const joinedPath = path2.join(cwd, resolvedPath);
|
|
6363
|
+
const root = path2.resolve(ctx.projectRoot);
|
|
6364
|
+
const target = path2.resolve(joinedPath);
|
|
6365
|
+
const rel = path2.relative(root, target);
|
|
6366
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
6315
6367
|
throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
|
|
6316
6368
|
}
|
|
6317
6369
|
const fullPath = target;
|
|
6318
6370
|
if (!dryRun) {
|
|
6319
|
-
await
|
|
6371
|
+
await fs13.mkdir(path2.dirname(fullPath), { recursive: true });
|
|
6320
6372
|
await atomicWrite(fullPath, substituteVars(content, name, vars));
|
|
6321
6373
|
}
|
|
6322
6374
|
files.push(resolvedPath);
|
|
@@ -6742,7 +6794,7 @@ var testTool = {
|
|
|
6742
6794
|
type: "final",
|
|
6743
6795
|
output: {
|
|
6744
6796
|
runner: "none",
|
|
6745
|
-
exit_code:
|
|
6797
|
+
exit_code: 0,
|
|
6746
6798
|
tests_run: 0,
|
|
6747
6799
|
passed: 0,
|
|
6748
6800
|
failed: 0,
|
|
@@ -6772,14 +6824,14 @@ async function detectRunner(cwd) {
|
|
|
6772
6824
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
6773
6825
|
for (const f of candidates) {
|
|
6774
6826
|
try {
|
|
6775
|
-
await stat10(
|
|
6827
|
+
await stat10(path2.join(cwd, f));
|
|
6776
6828
|
if (f.includes("vitest")) return "vitest";
|
|
6777
6829
|
if (f.includes("jest")) return "jest";
|
|
6778
6830
|
if (f.includes("mocha")) return "mocha";
|
|
6779
6831
|
} catch {
|
|
6780
6832
|
}
|
|
6781
6833
|
}
|
|
6782
|
-
return
|
|
6834
|
+
return null;
|
|
6783
6835
|
}
|
|
6784
6836
|
function buildArgs2(runner, input) {
|
|
6785
6837
|
const args = [];
|
|
@@ -7318,7 +7370,7 @@ var treeTool = {
|
|
|
7318
7370
|
}
|
|
7319
7371
|
};
|
|
7320
7372
|
async function walkDir(dir, depth, opts) {
|
|
7321
|
-
const entries = await
|
|
7373
|
+
const entries = await fs13.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
7322
7374
|
const filtered = entries.filter((e) => {
|
|
7323
7375
|
if (!opts.showHidden && e.name.startsWith(".")) return false;
|
|
7324
7376
|
if (opts.exclude.has(e.name)) return false;
|
|
@@ -7348,7 +7400,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
7348
7400
|
opts.lines.push(opts.prefix + branch + displayName);
|
|
7349
7401
|
if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
|
|
7350
7402
|
const childPrefix = opts.prefix + connector;
|
|
7351
|
-
await walkDir(
|
|
7403
|
+
await walkDir(path2.join(dir, entry.name), depth + 1, {
|
|
7352
7404
|
...opts,
|
|
7353
7405
|
prefix: childPrefix,
|
|
7354
7406
|
isLast
|
|
@@ -7431,8 +7483,8 @@ async function findTsConfig(cwd) {
|
|
|
7431
7483
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
7432
7484
|
for (const f of candidates) {
|
|
7433
7485
|
try {
|
|
7434
|
-
const s = await stat10(
|
|
7435
|
-
if (s.isFile()) return
|
|
7486
|
+
const s = await stat10(path2.join(cwd, f));
|
|
7487
|
+
if (s.isFile()) return path2.join(cwd, f);
|
|
7436
7488
|
} catch {
|
|
7437
7489
|
}
|
|
7438
7490
|
}
|
|
@@ -7468,14 +7520,14 @@ var writeTool = {
|
|
|
7468
7520
|
let existed = false;
|
|
7469
7521
|
let prev = "";
|
|
7470
7522
|
try {
|
|
7471
|
-
const stat11 = await
|
|
7523
|
+
const stat11 = await fs13.stat(absPath);
|
|
7472
7524
|
existed = stat11.isFile();
|
|
7473
7525
|
if (existed) {
|
|
7474
7526
|
if (!ctx.hasRead(absPath)) {
|
|
7475
|
-
prev = await
|
|
7527
|
+
prev = await fs13.readFile(absPath, "utf8");
|
|
7476
7528
|
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
7477
7529
|
} else {
|
|
7478
|
-
prev = await
|
|
7530
|
+
prev = await fs13.readFile(absPath, "utf8");
|
|
7479
7531
|
}
|
|
7480
7532
|
}
|
|
7481
7533
|
} catch (err) {
|
|
@@ -7486,7 +7538,7 @@ var writeTool = {
|
|
|
7486
7538
|
await atomicWrite(absPath, input.content);
|
|
7487
7539
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
7488
7540
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
7489
|
-
const stat10 = await
|
|
7541
|
+
const stat10 = await fs13.stat(absPath);
|
|
7490
7542
|
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
7491
7543
|
ctx.session.recordFileChange({
|
|
7492
7544
|
path: absPath,
|