@wrongstack/tools 0.119.1 → 0.148.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 +36 -10
- package/dist/audit.js.map +1 -1
- package/dist/bash.js +30 -28
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +178 -154
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.js +1 -1
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/document.js +1 -1
- package/dist/document.js.map +1 -1
- package/dist/edit.js.map +1 -1
- package/dist/exec.js +28 -4
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js +1 -13
- package/dist/fetch.js.map +1 -1
- package/dist/format.js +39 -13
- package/dist/format.js.map +1 -1
- package/dist/git.js +3 -3
- package/dist/git.js.map +1 -1
- package/dist/glob.js.map +1 -1
- package/dist/grep.js +1 -1
- package/dist/grep.js.map +1 -1
- package/dist/index.js +87 -63
- package/dist/index.js.map +1 -1
- package/dist/install.js +39 -13
- package/dist/install.js.map +1 -1
- package/dist/lint.js +39 -13
- 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 +178 -154
- package/dist/pack.js.map +1 -1
- package/dist/patch.js.map +1 -1
- package/dist/read.js +1 -1
- package/dist/read.js.map +1 -1
- package/dist/replace.js +6 -2
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js.map +1 -1
- package/dist/search.js.map +1 -1
- package/dist/test.js +42 -16
- package/dist/test.js.map +1 -1
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.js +41 -15
- package/dist/typecheck.js.map +1 -1
- package/dist/write.js.map +1 -1
- package/package.json +4 -4
package/dist/builtin.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
|
+
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
1
2
|
import * as Core from '@wrongstack/core';
|
|
2
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';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
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;
|
|
@@ -65,7 +90,7 @@ async function* spawnStream(opts) {
|
|
|
65
90
|
waiter = resolve7;
|
|
66
91
|
});
|
|
67
92
|
}
|
|
68
|
-
const chunk =
|
|
93
|
+
const chunk = queue.shift();
|
|
69
94
|
if (chunk.kind === "close") {
|
|
70
95
|
if (!spawnFailed) exitCode = chunk.code ?? 0;
|
|
71
96
|
break;
|
|
@@ -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
|
);
|
|
@@ -181,9 +206,9 @@ function collapseConsecutiveDuplicates(text, minRun = REPEAT_RUN_THRESHOLD) {
|
|
|
181
206
|
while (j < lines.length && lines[j] === lines[i]) j++;
|
|
182
207
|
const run = j - i;
|
|
183
208
|
if (run >= minRun) {
|
|
184
|
-
out.push(
|
|
209
|
+
out.push(lines[i], `\u2026 \u27E8repeated ${run}\xD7\u27E9`);
|
|
185
210
|
} else {
|
|
186
|
-
for (let k = i; k < j; k++) out.push(
|
|
211
|
+
for (let k = i; k < j; k++) out.push(lines[k]);
|
|
187
212
|
}
|
|
188
213
|
i = j;
|
|
189
214
|
}
|
|
@@ -838,44 +863,46 @@ var bashTool = {
|
|
|
838
863
|
let pending = "";
|
|
839
864
|
let timedOut = false;
|
|
840
865
|
const timers = [];
|
|
841
|
-
|
|
842
|
-
timedOut = true;
|
|
866
|
+
function killWithTimeout(child2, timeoutMs2) {
|
|
843
867
|
if (isWin) {
|
|
844
868
|
try {
|
|
845
|
-
|
|
869
|
+
child2.kill();
|
|
846
870
|
} catch {
|
|
847
871
|
}
|
|
848
|
-
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
if (typeof child2.pid === "number") {
|
|
876
|
+
try {
|
|
877
|
+
process.kill(-child2.pid, "SIGTERM");
|
|
878
|
+
} catch {
|
|
879
|
+
child2.kill("SIGTERM");
|
|
880
|
+
}
|
|
881
|
+
} else {
|
|
882
|
+
child2.kill("SIGTERM");
|
|
883
|
+
}
|
|
884
|
+
} catch {
|
|
885
|
+
}
|
|
886
|
+
const killTimer = setTimeout(() => {
|
|
849
887
|
try {
|
|
850
|
-
if (typeof
|
|
888
|
+
if (typeof child2.pid === "number") {
|
|
851
889
|
try {
|
|
852
|
-
process.kill(-
|
|
890
|
+
process.kill(-child2.pid, "SIGKILL");
|
|
853
891
|
} catch {
|
|
854
|
-
|
|
892
|
+
child2.kill("SIGKILL");
|
|
855
893
|
}
|
|
856
894
|
} else {
|
|
857
|
-
|
|
895
|
+
child2.kill("SIGKILL");
|
|
858
896
|
}
|
|
859
|
-
const killTimer = setTimeout(() => {
|
|
860
|
-
try {
|
|
861
|
-
if (typeof child.pid === "number") {
|
|
862
|
-
try {
|
|
863
|
-
process.kill(-child.pid, "SIGKILL");
|
|
864
|
-
} catch {
|
|
865
|
-
child.kill("SIGKILL");
|
|
866
|
-
}
|
|
867
|
-
} else {
|
|
868
|
-
child.kill("SIGKILL");
|
|
869
|
-
}
|
|
870
|
-
} catch {
|
|
871
|
-
} finally {
|
|
872
|
-
killTimer.unref?.();
|
|
873
|
-
}
|
|
874
|
-
}, 2e3);
|
|
875
|
-
timers.push(killTimer);
|
|
876
897
|
} catch {
|
|
877
898
|
}
|
|
878
|
-
}
|
|
899
|
+
}, timeoutMs2);
|
|
900
|
+
timers.push(killTimer);
|
|
901
|
+
killTimer.unref?.();
|
|
902
|
+
}
|
|
903
|
+
const timer = setTimeout(() => {
|
|
904
|
+
timedOut = true;
|
|
905
|
+
killWithTimeout(child, 2e3);
|
|
879
906
|
}, timeoutMs);
|
|
880
907
|
timers.push(timer);
|
|
881
908
|
timer.unref?.();
|
|
@@ -1140,7 +1167,7 @@ var IndexStore = class {
|
|
|
1140
1167
|
this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
|
|
1141
1168
|
fs.mkdirSync(this.indexDir, { recursive: true });
|
|
1142
1169
|
const Database = loadDatabaseSync();
|
|
1143
|
-
this.db = new Database(
|
|
1170
|
+
this.db = new Database(path2.join(this.indexDir, DB_FILE));
|
|
1144
1171
|
this.initSchema();
|
|
1145
1172
|
}
|
|
1146
1173
|
initSchema() {
|
|
@@ -1438,7 +1465,7 @@ var IndexStore = class {
|
|
|
1438
1465
|
}));
|
|
1439
1466
|
}
|
|
1440
1467
|
sizeBytes() {
|
|
1441
|
-
const dbPath =
|
|
1468
|
+
const dbPath = path2.join(this.indexDir, DB_FILE);
|
|
1442
1469
|
try {
|
|
1443
1470
|
return fs.statSync(dbPath).size;
|
|
1444
1471
|
} catch {
|
|
@@ -1877,10 +1904,10 @@ func formatType(t ast.Expr) string {
|
|
|
1877
1904
|
}
|
|
1878
1905
|
`;
|
|
1879
1906
|
function syncGoParse(filePath, content, lang) {
|
|
1880
|
-
const tmpDir =
|
|
1907
|
+
const tmpDir = path2.join(os.tmpdir(), "ws-go-parse");
|
|
1881
1908
|
try {
|
|
1882
1909
|
mkdirSync(tmpDir, { recursive: true });
|
|
1883
|
-
const scriptPath =
|
|
1910
|
+
const scriptPath = path2.join(tmpDir, "parse.go");
|
|
1884
1911
|
writeFileSync(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
1885
1912
|
const stdout = execFileSync("go", ["run", scriptPath], {
|
|
1886
1913
|
input: content,
|
|
@@ -2124,9 +2151,9 @@ print(json.dumps([s.to_dict() for s in syms]))
|
|
|
2124
2151
|
`;
|
|
2125
2152
|
function syncPyParse(filePath, lang) {
|
|
2126
2153
|
try {
|
|
2127
|
-
const tmpDir =
|
|
2154
|
+
const tmpDir = path2.join(os.tmpdir(), "ws-py-parse");
|
|
2128
2155
|
mkdirSync(tmpDir, { recursive: true });
|
|
2129
|
-
const scriptPath =
|
|
2156
|
+
const scriptPath = path2.join(tmpDir, "parse.py");
|
|
2130
2157
|
writeFileSync(scriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
2131
2158
|
const stdout = execFileSync("python", [scriptPath, filePath], {
|
|
2132
2159
|
timeout: 15e3,
|
|
@@ -2167,7 +2194,7 @@ function parseSymbols4(opts) {
|
|
|
2167
2194
|
function checkNativeParser() {
|
|
2168
2195
|
try {
|
|
2169
2196
|
execFileSync("rustc", ["--version"], { stdio: "pipe" });
|
|
2170
|
-
const toolsDir =
|
|
2197
|
+
const toolsDir = path2.join(process.cwd(), "tools");
|
|
2171
2198
|
try {
|
|
2172
2199
|
execFileSync(
|
|
2173
2200
|
"cargo",
|
|
@@ -2177,7 +2204,7 @@ function checkNativeParser() {
|
|
|
2177
2204
|
"--format-version",
|
|
2178
2205
|
"1",
|
|
2179
2206
|
"--manifest-path",
|
|
2180
|
-
|
|
2207
|
+
path2.join(toolsDir, "Cargo.toml")
|
|
2181
2208
|
],
|
|
2182
2209
|
{ stdio: "pipe" }
|
|
2183
2210
|
);
|
|
@@ -2191,13 +2218,13 @@ function checkNativeParser() {
|
|
|
2191
2218
|
}
|
|
2192
2219
|
function tryNativeParse(file, content) {
|
|
2193
2220
|
try {
|
|
2194
|
-
const toolsDir =
|
|
2195
|
-
const crateDir =
|
|
2196
|
-
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");
|
|
2197
2224
|
writeFileSync(tmpFile, content, "utf8");
|
|
2198
2225
|
const result = spawnSync(
|
|
2199
2226
|
"cargo",
|
|
2200
|
-
["run", "--manifest-path",
|
|
2227
|
+
["run", "--manifest-path", path2.join(toolsDir, "Cargo.toml")],
|
|
2201
2228
|
{
|
|
2202
2229
|
cwd: process.cwd(),
|
|
2203
2230
|
encoding: "utf8",
|
|
@@ -2295,7 +2322,7 @@ function parseSymbols5(opts) {
|
|
|
2295
2322
|
function regexParse2(opts) {
|
|
2296
2323
|
const { file, content, lang } = opts;
|
|
2297
2324
|
const symbols = [];
|
|
2298
|
-
const basename2 =
|
|
2325
|
+
const basename2 = path2.basename(file).toLowerCase();
|
|
2299
2326
|
const isPackageJson = basename2 === "package.json";
|
|
2300
2327
|
const isTsconfig = basename2 === "tsconfig.json" || basename2 === "tsconfig.build.json";
|
|
2301
2328
|
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
@@ -2321,11 +2348,11 @@ function regexParse2(opts) {
|
|
|
2321
2348
|
const line = lineFromOffset(offset);
|
|
2322
2349
|
symbols.push(
|
|
2323
2350
|
makeSymbol({
|
|
2324
|
-
name:
|
|
2351
|
+
name: path2.basename(file),
|
|
2325
2352
|
kind: "object",
|
|
2326
2353
|
line,
|
|
2327
2354
|
col: 0,
|
|
2328
|
-
signature: `"${
|
|
2355
|
+
signature: `"${path2.basename(file)}" = { ... }`,
|
|
2329
2356
|
file,
|
|
2330
2357
|
lang
|
|
2331
2358
|
})
|
|
@@ -2679,7 +2706,7 @@ function compileGitignore(lines) {
|
|
|
2679
2706
|
async function loadGitignoreMatcher(projectRoot) {
|
|
2680
2707
|
let lines = [];
|
|
2681
2708
|
try {
|
|
2682
|
-
const raw = await
|
|
2709
|
+
const raw = await fs13.readFile(path2.join(projectRoot, ".gitignore"), "utf8");
|
|
2683
2710
|
lines = raw.split("\n");
|
|
2684
2711
|
} catch {
|
|
2685
2712
|
}
|
|
@@ -2750,20 +2777,20 @@ async function findSourceFiles(projectRoot, ignore, isGitIgnored) {
|
|
|
2750
2777
|
const walk = async (dir) => {
|
|
2751
2778
|
let entries;
|
|
2752
2779
|
try {
|
|
2753
|
-
entries = await
|
|
2780
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
2754
2781
|
} catch {
|
|
2755
2782
|
return;
|
|
2756
2783
|
}
|
|
2757
2784
|
for (const e of entries) {
|
|
2758
2785
|
if (ignoreSet.has(e.name)) continue;
|
|
2759
|
-
const full =
|
|
2760
|
-
const rel =
|
|
2786
|
+
const full = path2.join(dir, e.name);
|
|
2787
|
+
const rel = path2.relative(projectRoot, full).replace(/\\/g, "/");
|
|
2761
2788
|
if (e.isDirectory()) {
|
|
2762
2789
|
if (isGitIgnored(rel, true)) continue;
|
|
2763
2790
|
await walk(full);
|
|
2764
2791
|
} else if (e.isFile()) {
|
|
2765
2792
|
if (isGitIgnored(rel, false)) continue;
|
|
2766
|
-
const ext =
|
|
2793
|
+
const ext = path2.extname(e.name);
|
|
2767
2794
|
for (const { ext: extName, pat } of globs) {
|
|
2768
2795
|
if (ext === extName && (pat.test(rel) || pat.test(e.name))) {
|
|
2769
2796
|
results.push(full);
|
|
@@ -2808,7 +2835,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2808
2835
|
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
2809
2836
|
let files;
|
|
2810
2837
|
if (opts.files && opts.files.length > 0) {
|
|
2811
|
-
files = opts.files.map((f) =>
|
|
2838
|
+
files = opts.files.map((f) => path2.resolve(projectRoot, f)).filter((f) => !isGitIgnored(path2.relative(projectRoot, f).replace(/\\/g, "/"), false));
|
|
2812
2839
|
} else {
|
|
2813
2840
|
files = await findSourceFiles(projectRoot, ignore, isGitIgnored);
|
|
2814
2841
|
}
|
|
@@ -2832,7 +2859,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2832
2859
|
}
|
|
2833
2860
|
let stat10;
|
|
2834
2861
|
try {
|
|
2835
|
-
stat10 = await
|
|
2862
|
+
stat10 = await fs13.stat(file);
|
|
2836
2863
|
} catch {
|
|
2837
2864
|
store.deleteFile(file);
|
|
2838
2865
|
continue;
|
|
@@ -2851,7 +2878,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2851
2878
|
store.deleteSymbolsForFile(file);
|
|
2852
2879
|
let content;
|
|
2853
2880
|
try {
|
|
2854
|
-
content = await
|
|
2881
|
+
content = await fs13.readFile(file, "utf8");
|
|
2855
2882
|
} catch (e) {
|
|
2856
2883
|
errors.push(`read error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
2857
2884
|
continue;
|
|
@@ -2901,7 +2928,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2901
2928
|
}
|
|
2902
2929
|
for (const [file_] of existingMeta) {
|
|
2903
2930
|
try {
|
|
2904
|
-
await
|
|
2931
|
+
await fs13.stat(file_);
|
|
2905
2932
|
} catch {
|
|
2906
2933
|
store.deleteFile(file_);
|
|
2907
2934
|
}
|
|
@@ -2962,7 +2989,7 @@ function tokenise(text) {
|
|
|
2962
2989
|
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
2963
2990
|
}
|
|
2964
2991
|
function splitName(name) {
|
|
2965
|
-
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_
|
|
2992
|
+
return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
2966
2993
|
}
|
|
2967
2994
|
function buildIndexableText(name, signature, docComment) {
|
|
2968
2995
|
return [splitName(name), name, signature, docComment].filter(Boolean).join(" ");
|
|
@@ -3292,11 +3319,11 @@ function findGitDir(cwd) {
|
|
|
3292
3319
|
let dir = cwd;
|
|
3293
3320
|
for (let i = 0; i < 20; i++) {
|
|
3294
3321
|
try {
|
|
3295
|
-
const stat10 = statSync(
|
|
3322
|
+
const stat10 = statSync(path2.join(dir, ".git"));
|
|
3296
3323
|
if (stat10.isDirectory()) return dir;
|
|
3297
3324
|
} catch {
|
|
3298
3325
|
}
|
|
3299
|
-
const parent =
|
|
3326
|
+
const parent = path2.dirname(dir);
|
|
3300
3327
|
if (parent === dir) break;
|
|
3301
3328
|
dir = parent;
|
|
3302
3329
|
}
|
|
@@ -3336,9 +3363,9 @@ async function fileDiff(input, ctx, _signal) {
|
|
|
3336
3363
|
const results = [];
|
|
3337
3364
|
for (const file of files) {
|
|
3338
3365
|
const absPath = safeResolve(file, ctx);
|
|
3339
|
-
const stat10 = await
|
|
3366
|
+
const stat10 = await fs13.stat(absPath).catch(() => null);
|
|
3340
3367
|
if (!stat10?.isFile()) continue;
|
|
3341
|
-
const content = await
|
|
3368
|
+
const content = await fs13.readFile(absPath, "utf8");
|
|
3342
3369
|
const lines = content.split(/\r?\n/);
|
|
3343
3370
|
results.push(formatWithLineNumbers(file, lines));
|
|
3344
3371
|
}
|
|
@@ -3400,7 +3427,7 @@ var documentTool = {
|
|
|
3400
3427
|
const fileList = input.files ? await resolveFiles(Array.isArray(input.files) ? input.files.join(",") : input.files, cwd) : input.path ? [safeResolve(input.path, ctx)] : [];
|
|
3401
3428
|
for (const absPath of fileList) {
|
|
3402
3429
|
try {
|
|
3403
|
-
const content = await
|
|
3430
|
+
const content = await fs13.readFile(absPath, "utf8");
|
|
3404
3431
|
filesProcessed++;
|
|
3405
3432
|
const processed = processFile(
|
|
3406
3433
|
content,
|
|
@@ -3436,7 +3463,7 @@ async function resolveFiles(filesInput, cwd) {
|
|
|
3436
3463
|
for (const f of files) {
|
|
3437
3464
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
3438
3465
|
try {
|
|
3439
|
-
const stat10 = await
|
|
3466
|
+
const stat10 = await fs13.stat(absPath);
|
|
3440
3467
|
if (stat10.isFile()) resolved.push(absPath);
|
|
3441
3468
|
} catch {
|
|
3442
3469
|
}
|
|
@@ -3528,7 +3555,7 @@ var editTool = {
|
|
|
3528
3555
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
3529
3556
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
3530
3557
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
3531
|
-
const stat10 = await
|
|
3558
|
+
const stat10 = await fs13.stat(absPath).catch((err) => {
|
|
3532
3559
|
if (err.code === "ENOENT") {
|
|
3533
3560
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
3534
3561
|
}
|
|
@@ -3538,8 +3565,8 @@ var editTool = {
|
|
|
3538
3565
|
if (!ctx.hasRead(absPath)) {
|
|
3539
3566
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
3540
3567
|
}
|
|
3541
|
-
const original = await
|
|
3542
|
-
const updated = await
|
|
3568
|
+
const original = await fs13.readFile(absPath, "utf8");
|
|
3569
|
+
const updated = await fs13.stat(absPath);
|
|
3543
3570
|
const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
|
|
3544
3571
|
const lastReadMtime = ctx.lastReadMtime(absPath);
|
|
3545
3572
|
if (lastReadMtime !== void 0 && updated.mtimeMs > lastReadMtime + mtimeTolerance) {
|
|
@@ -3579,7 +3606,7 @@ var editTool = {
|
|
|
3579
3606
|
const newFileLf = input.replace_all ? fileLf.split(oldLf).join(newLf) : fileLf.replace(oldLf, newLf);
|
|
3580
3607
|
const newFile = toStyle(newFileLf, style);
|
|
3581
3608
|
await atomicWrite(absPath, newFile, { mode: updated.mode & 511 });
|
|
3582
|
-
const written = await
|
|
3609
|
+
const written = await fs13.stat(absPath);
|
|
3583
3610
|
ctx.recordRead(absPath, written.mtimeMs);
|
|
3584
3611
|
ctx.session.recordFileChange({
|
|
3585
3612
|
path: absPath,
|
|
@@ -3804,9 +3831,9 @@ var execTool = {
|
|
|
3804
3831
|
allowed: false
|
|
3805
3832
|
};
|
|
3806
3833
|
}
|
|
3807
|
-
const requestedCwd = input.cwd ?
|
|
3808
|
-
const rel =
|
|
3809
|
-
if (rel.startsWith("..") ||
|
|
3834
|
+
const requestedCwd = input.cwd ? path2.resolve(ctx.projectRoot, input.cwd) : ctx.cwd;
|
|
3835
|
+
const rel = path2.relative(ctx.projectRoot, requestedCwd);
|
|
3836
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
3810
3837
|
return {
|
|
3811
3838
|
command: cmd,
|
|
3812
3839
|
args,
|
|
@@ -3828,11 +3855,14 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
3828
3855
|
let stderr = "";
|
|
3829
3856
|
let killed = false;
|
|
3830
3857
|
const startedAt = Date.now();
|
|
3831
|
-
const
|
|
3858
|
+
const resolved = resolveWin32Command(cmd);
|
|
3859
|
+
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
3860
|
+
const child = spawn(resolved, args, {
|
|
3832
3861
|
cwd,
|
|
3833
3862
|
signal,
|
|
3834
3863
|
env: buildChildEnv(sessionId),
|
|
3835
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
3864
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3865
|
+
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
3836
3866
|
});
|
|
3837
3867
|
const registry = getProcessRegistry();
|
|
3838
3868
|
const pid = child.pid;
|
|
@@ -3891,19 +3921,7 @@ if (ALLOW_PRIVATE && !process.env["CI"]) {
|
|
|
3891
3921
|
"[WrongStack] WARNING: WRONGSTACK_FETCH_ALLOW_PRIVATE=1 is active \u2014\n fetch tool can now access private IPs (10.x, 192.168.x, 169.254.x),\n cloud metadata endpoints, and plaintext HTTP. Use only on isolated networks."
|
|
3892
3922
|
);
|
|
3893
3923
|
}
|
|
3894
|
-
|
|
3895
|
-
const anyFn = AbortSignal.any;
|
|
3896
|
-
if (typeof anyFn === "function") return anyFn(signals);
|
|
3897
|
-
const ctrl = new AbortController();
|
|
3898
|
-
for (const sig of signals) {
|
|
3899
|
-
if (sig.aborted) {
|
|
3900
|
-
ctrl.abort(sig.reason);
|
|
3901
|
-
return ctrl.signal;
|
|
3902
|
-
}
|
|
3903
|
-
sig.addEventListener("abort", () => ctrl.abort(sig.reason), { once: true });
|
|
3904
|
-
}
|
|
3905
|
-
return ctrl.signal;
|
|
3906
|
-
}
|
|
3924
|
+
var combineSignals = (signals) => AbortSignal.any(signals);
|
|
3907
3925
|
function guardedLookup(hostname, options, callback) {
|
|
3908
3926
|
dns.lookup(hostname, { all: true }).then((records) => {
|
|
3909
3927
|
const family = options?.family;
|
|
@@ -4627,7 +4645,7 @@ var globTool = {
|
|
|
4627
4645
|
}
|
|
4628
4646
|
let entries;
|
|
4629
4647
|
try {
|
|
4630
|
-
entries = await
|
|
4648
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
4631
4649
|
} catch {
|
|
4632
4650
|
return;
|
|
4633
4651
|
}
|
|
@@ -4636,14 +4654,14 @@ var globTool = {
|
|
|
4636
4654
|
if (DEFAULT_IGNORE2.includes(name)) continue;
|
|
4637
4655
|
if (ignored.includes(name)) continue;
|
|
4638
4656
|
const rel = relPrefix ? `${relPrefix}/${name}` : name;
|
|
4639
|
-
const full =
|
|
4657
|
+
const full = path2.join(dir, name);
|
|
4640
4658
|
if (e.isDirectory()) {
|
|
4641
4659
|
await walk(full, rel);
|
|
4642
4660
|
if (truncated) return;
|
|
4643
4661
|
} else if (e.isFile()) {
|
|
4644
4662
|
if (re.test(rel) || re.test(name)) {
|
|
4645
4663
|
try {
|
|
4646
|
-
const st = await
|
|
4664
|
+
const st = await fs13.stat(full);
|
|
4647
4665
|
results.push({ rel: full, mtime: st.mtimeMs });
|
|
4648
4666
|
if (results.length >= limit) {
|
|
4649
4667
|
truncated = true;
|
|
@@ -4662,7 +4680,7 @@ var globTool = {
|
|
|
4662
4680
|
};
|
|
4663
4681
|
async function readGitignore(dir) {
|
|
4664
4682
|
try {
|
|
4665
|
-
const raw = await
|
|
4683
|
+
const raw = await fs13.readFile(path2.join(dir, ".gitignore"), "utf8");
|
|
4666
4684
|
return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
4667
4685
|
} catch {
|
|
4668
4686
|
return [];
|
|
@@ -4680,7 +4698,7 @@ var DANGEROUS_PATTERNS = [
|
|
|
4680
4698
|
// Quantifier on alternation with length 2+
|
|
4681
4699
|
/\([^|)]+\|[^)]+\)[+*][+*]/,
|
|
4682
4700
|
// Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
|
|
4683
|
-
/[
|
|
4701
|
+
/[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
|
|
4684
4702
|
];
|
|
4685
4703
|
function compileUserRegex(pattern, flags) {
|
|
4686
4704
|
if (typeof pattern !== "string") {
|
|
@@ -4946,7 +4964,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4946
4964
|
if (stopped || signal.aborted) return;
|
|
4947
4965
|
let entries;
|
|
4948
4966
|
try {
|
|
4949
|
-
entries = await
|
|
4967
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
4950
4968
|
} catch {
|
|
4951
4969
|
return;
|
|
4952
4970
|
}
|
|
@@ -4954,16 +4972,16 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4954
4972
|
if (stopped) return;
|
|
4955
4973
|
if (DEFAULT_IGNORE3.includes(e.name)) continue;
|
|
4956
4974
|
if (e.isSymbolicLink()) continue;
|
|
4957
|
-
const full =
|
|
4975
|
+
const full = path2.join(dir, e.name);
|
|
4958
4976
|
if (e.isDirectory()) {
|
|
4959
4977
|
await walk(full);
|
|
4960
4978
|
} else if (e.isFile()) {
|
|
4961
4979
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
4962
4980
|
if (globRe) globRe.lastIndex = 0;
|
|
4963
4981
|
try {
|
|
4964
|
-
const stat10 = await
|
|
4982
|
+
const stat10 = await fs13.stat(full);
|
|
4965
4983
|
if (stat10.size > 1e6) continue;
|
|
4966
|
-
const head = await
|
|
4984
|
+
const head = await fs13.readFile(full);
|
|
4967
4985
|
if (isBinaryBuffer(head)) continue;
|
|
4968
4986
|
const text = head.toString("utf8");
|
|
4969
4987
|
const lines = text.split(/\r?\n/);
|
|
@@ -5144,7 +5162,7 @@ var jsonTool = {
|
|
|
5144
5162
|
let raw;
|
|
5145
5163
|
if (input.file) {
|
|
5146
5164
|
try {
|
|
5147
|
-
raw = await
|
|
5165
|
+
raw = await fs13.readFile(input.file, "utf8");
|
|
5148
5166
|
} catch {
|
|
5149
5167
|
return { data: null, formatted: "", type: "unknown", error: `Could not read file` };
|
|
5150
5168
|
}
|
|
@@ -5182,8 +5200,8 @@ var jsonTool = {
|
|
|
5182
5200
|
};
|
|
5183
5201
|
}
|
|
5184
5202
|
};
|
|
5185
|
-
function query(data,
|
|
5186
|
-
const parts =
|
|
5203
|
+
function query(data, path20) {
|
|
5204
|
+
const parts = path20.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
5187
5205
|
let current = data;
|
|
5188
5206
|
for (const part of parts) {
|
|
5189
5207
|
if (current === null || current === void 0) return void 0;
|
|
@@ -5454,7 +5472,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
5454
5472
|
}
|
|
5455
5473
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
5456
5474
|
var MAX_TAIL_LINES = 1e5;
|
|
5457
|
-
async function fileLogs(
|
|
5475
|
+
async function fileLogs(path20, lines, filterRe, stream) {
|
|
5458
5476
|
const { createInterface } = await import('node:readline');
|
|
5459
5477
|
const { createReadStream } = await import('node:fs');
|
|
5460
5478
|
const entries = [];
|
|
@@ -5463,7 +5481,7 @@ async function fileLogs(path19, lines, filterRe, stream) {
|
|
|
5463
5481
|
let writeIdx = 0;
|
|
5464
5482
|
let totalLines = 0;
|
|
5465
5483
|
const rl = createInterface({
|
|
5466
|
-
input: createReadStream(
|
|
5484
|
+
input: createReadStream(path20),
|
|
5467
5485
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
5468
5486
|
});
|
|
5469
5487
|
for await (const line of rl) {
|
|
@@ -5484,7 +5502,7 @@ async function fileLogs(path19, lines, filterRe, stream) {
|
|
|
5484
5502
|
if (parsed) entries.push(parsed);
|
|
5485
5503
|
}
|
|
5486
5504
|
return {
|
|
5487
|
-
source:
|
|
5505
|
+
source: path20,
|
|
5488
5506
|
entries,
|
|
5489
5507
|
total: entries.length,
|
|
5490
5508
|
truncated: totalLines > effLines,
|
|
@@ -5567,7 +5585,9 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
5567
5585
|
let stdout = "";
|
|
5568
5586
|
let stderr = "";
|
|
5569
5587
|
const MAX = 1e5;
|
|
5570
|
-
const
|
|
5588
|
+
const resolved = resolveWin32Command(manager);
|
|
5589
|
+
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
5590
|
+
const child = spawn(resolved, args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {} });
|
|
5571
5591
|
child.stdout?.on("data", (c) => {
|
|
5572
5592
|
if (stdout.length < MAX) stdout += c.toString();
|
|
5573
5593
|
});
|
|
@@ -5651,9 +5671,9 @@ var patchTool = {
|
|
|
5651
5671
|
for (const t of targets) {
|
|
5652
5672
|
const stripped = stripPathComponents(t, strip);
|
|
5653
5673
|
if (!stripped) continue;
|
|
5654
|
-
const candidate =
|
|
5655
|
-
const rel =
|
|
5656
|
-
if (rel.startsWith("..") ||
|
|
5674
|
+
const candidate = path2.resolve(dir, stripped);
|
|
5675
|
+
const rel = path2.relative(ctx.projectRoot, candidate);
|
|
5676
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
5657
5677
|
return {
|
|
5658
5678
|
applied: 0,
|
|
5659
5679
|
rejected: 1,
|
|
@@ -5663,12 +5683,12 @@ var patchTool = {
|
|
|
5663
5683
|
};
|
|
5664
5684
|
}
|
|
5665
5685
|
}
|
|
5666
|
-
const tmpDir = await
|
|
5686
|
+
const tmpDir = await fs13.mkdtemp(path2.join(os.tmpdir(), ".wstack_patch_"));
|
|
5667
5687
|
try {
|
|
5668
|
-
await
|
|
5688
|
+
await fs13.chmod(tmpDir, 448).catch(() => {
|
|
5669
5689
|
});
|
|
5670
|
-
const patchFile =
|
|
5671
|
-
await
|
|
5690
|
+
const patchFile = path2.join(tmpDir, "in.diff");
|
|
5691
|
+
await fs13.writeFile(patchFile, input.patch, { mode: 384 });
|
|
5672
5692
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
5673
5693
|
const result = await runPatch(args, dir, opts.signal);
|
|
5674
5694
|
if (result.exitCode !== 0 && !dryRun) {
|
|
@@ -5689,7 +5709,7 @@ var patchTool = {
|
|
|
5689
5709
|
message: result.stdout || "patch applied"
|
|
5690
5710
|
};
|
|
5691
5711
|
} finally {
|
|
5692
|
-
await
|
|
5712
|
+
await fs13.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
5693
5713
|
});
|
|
5694
5714
|
}
|
|
5695
5715
|
}
|
|
@@ -5932,7 +5952,7 @@ var readTool = {
|
|
|
5932
5952
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
5933
5953
|
let stat10;
|
|
5934
5954
|
try {
|
|
5935
|
-
stat10 = await
|
|
5955
|
+
stat10 = await fs13.stat(absPath);
|
|
5936
5956
|
} catch (err) {
|
|
5937
5957
|
const code = err.code;
|
|
5938
5958
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
@@ -5944,7 +5964,7 @@ var readTool = {
|
|
|
5944
5964
|
if (stat10.size > MAX_BYTES2) {
|
|
5945
5965
|
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES2})`);
|
|
5946
5966
|
}
|
|
5947
|
-
const buf = await
|
|
5967
|
+
const buf = await fs13.readFile(absPath);
|
|
5948
5968
|
if (isBinaryBuffer(buf)) {
|
|
5949
5969
|
throw new Error(`read: "${input.path}" appears to be binary`);
|
|
5950
5970
|
}
|
|
@@ -6012,11 +6032,11 @@ var replaceTool = {
|
|
|
6012
6032
|
const dryRun = input.dry_run ?? false;
|
|
6013
6033
|
const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
|
|
6014
6034
|
const fileList = await resolveFiles2(filesInput, ctx, globRe);
|
|
6015
|
-
const realRoot = await
|
|
6035
|
+
const realRoot = await fs13.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
6016
6036
|
const results = [];
|
|
6017
6037
|
let totalReplacements = 0;
|
|
6018
6038
|
for (const absPath of fileList) {
|
|
6019
|
-
const lstat2 = await
|
|
6039
|
+
const lstat2 = await fs13.lstat(absPath).catch((err) => {
|
|
6020
6040
|
if (err.code === "ENOENT") return null;
|
|
6021
6041
|
throw err;
|
|
6022
6042
|
});
|
|
@@ -6024,17 +6044,17 @@ var replaceTool = {
|
|
|
6024
6044
|
if (lstat2.isSymbolicLink()) continue;
|
|
6025
6045
|
let realPath;
|
|
6026
6046
|
try {
|
|
6027
|
-
realPath = await
|
|
6047
|
+
realPath = await fs13.realpath(absPath);
|
|
6028
6048
|
} catch {
|
|
6029
6049
|
continue;
|
|
6030
6050
|
}
|
|
6031
|
-
const rel =
|
|
6032
|
-
if (rel.startsWith("..") ||
|
|
6033
|
-
const stat10 = await
|
|
6051
|
+
const rel = path2.relative(realRoot, realPath);
|
|
6052
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) continue;
|
|
6053
|
+
const stat10 = await fs13.stat(realPath).catch(() => null);
|
|
6034
6054
|
if (!stat10 || !stat10.isFile()) continue;
|
|
6035
6055
|
let content;
|
|
6036
6056
|
try {
|
|
6037
|
-
const buf = await
|
|
6057
|
+
const buf = await fs13.readFile(realPath);
|
|
6038
6058
|
if (isBinaryBuffer(buf)) continue;
|
|
6039
6059
|
content = buf.toString("utf8");
|
|
6040
6060
|
} catch {
|
|
@@ -6086,7 +6106,7 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
|
|
|
6086
6106
|
const resolved = [];
|
|
6087
6107
|
for (const p of parts) {
|
|
6088
6108
|
const absPath = safeResolve(p, ctx);
|
|
6089
|
-
const stat10 = await
|
|
6109
|
+
const stat10 = await fs13.stat(absPath).catch(() => null);
|
|
6090
6110
|
if (stat10?.isFile()) {
|
|
6091
6111
|
resolved.push(absPath);
|
|
6092
6112
|
}
|
|
@@ -6117,7 +6137,11 @@ function checkRg() {
|
|
|
6117
6137
|
}
|
|
6118
6138
|
function spawnRgFind(pattern, base) {
|
|
6119
6139
|
const args = ["--files", "--glob", pattern, base];
|
|
6120
|
-
const child = spawn("rg", args, {
|
|
6140
|
+
const child = spawn("rg", args, {
|
|
6141
|
+
signal: AbortSignal.timeout(3e4),
|
|
6142
|
+
env: buildChildEnv(),
|
|
6143
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6144
|
+
});
|
|
6121
6145
|
let buf = "";
|
|
6122
6146
|
child.stdout?.on("data", (chunk) => {
|
|
6123
6147
|
buf += chunk.toString();
|
|
@@ -6137,15 +6161,15 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
6137
6161
|
const walk = async (dir) => {
|
|
6138
6162
|
let entries;
|
|
6139
6163
|
try {
|
|
6140
|
-
entries = await
|
|
6164
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
6141
6165
|
} catch {
|
|
6142
6166
|
return;
|
|
6143
6167
|
}
|
|
6144
6168
|
for (const e of entries) {
|
|
6145
6169
|
if (DEFAULT_IGNORE4.includes(e.name)) continue;
|
|
6146
|
-
const full =
|
|
6170
|
+
const full = path2.join(dir, e.name);
|
|
6147
6171
|
try {
|
|
6148
|
-
const stat10 = await
|
|
6172
|
+
const stat10 = await fs13.lstat(full);
|
|
6149
6173
|
if (stat10.isSymbolicLink()) continue;
|
|
6150
6174
|
} catch {
|
|
6151
6175
|
continue;
|
|
@@ -6313,16 +6337,16 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
6313
6337
|
let filesCreated = 0;
|
|
6314
6338
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
6315
6339
|
const resolvedPath = substituteVars(filePath, name, vars);
|
|
6316
|
-
const joinedPath =
|
|
6317
|
-
const root =
|
|
6318
|
-
const target =
|
|
6319
|
-
const rel =
|
|
6320
|
-
if (rel.startsWith("..") ||
|
|
6340
|
+
const joinedPath = path2.join(cwd, resolvedPath);
|
|
6341
|
+
const root = path2.resolve(ctx.projectRoot);
|
|
6342
|
+
const target = path2.resolve(joinedPath);
|
|
6343
|
+
const rel = path2.relative(root, target);
|
|
6344
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
6321
6345
|
throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
|
|
6322
6346
|
}
|
|
6323
6347
|
const fullPath = target;
|
|
6324
6348
|
if (!dryRun) {
|
|
6325
|
-
await
|
|
6349
|
+
await fs13.mkdir(path2.dirname(fullPath), { recursive: true });
|
|
6326
6350
|
await atomicWrite(fullPath, substituteVars(content, name, vars));
|
|
6327
6351
|
}
|
|
6328
6352
|
files.push(resolvedPath);
|
|
@@ -6748,7 +6772,7 @@ var testTool = {
|
|
|
6748
6772
|
type: "final",
|
|
6749
6773
|
output: {
|
|
6750
6774
|
runner: "none",
|
|
6751
|
-
exit_code:
|
|
6775
|
+
exit_code: 0,
|
|
6752
6776
|
tests_run: 0,
|
|
6753
6777
|
passed: 0,
|
|
6754
6778
|
failed: 0,
|
|
@@ -6778,14 +6802,14 @@ async function detectRunner(cwd) {
|
|
|
6778
6802
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
6779
6803
|
for (const f of candidates) {
|
|
6780
6804
|
try {
|
|
6781
|
-
await stat10(
|
|
6805
|
+
await stat10(path2.join(cwd, f));
|
|
6782
6806
|
if (f.includes("vitest")) return "vitest";
|
|
6783
6807
|
if (f.includes("jest")) return "jest";
|
|
6784
6808
|
if (f.includes("mocha")) return "mocha";
|
|
6785
6809
|
} catch {
|
|
6786
6810
|
}
|
|
6787
6811
|
}
|
|
6788
|
-
return
|
|
6812
|
+
return null;
|
|
6789
6813
|
}
|
|
6790
6814
|
function buildArgs2(runner, input) {
|
|
6791
6815
|
const args = [];
|
|
@@ -7324,7 +7348,7 @@ var treeTool = {
|
|
|
7324
7348
|
}
|
|
7325
7349
|
};
|
|
7326
7350
|
async function walkDir(dir, depth, opts) {
|
|
7327
|
-
const entries = await
|
|
7351
|
+
const entries = await fs13.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
7328
7352
|
const filtered = entries.filter((e) => {
|
|
7329
7353
|
if (!opts.showHidden && e.name.startsWith(".")) return false;
|
|
7330
7354
|
if (opts.exclude.has(e.name)) return false;
|
|
@@ -7354,7 +7378,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
7354
7378
|
opts.lines.push(opts.prefix + branch + displayName);
|
|
7355
7379
|
if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
|
|
7356
7380
|
const childPrefix = opts.prefix + connector;
|
|
7357
|
-
await walkDir(
|
|
7381
|
+
await walkDir(path2.join(dir, entry.name), depth + 1, {
|
|
7358
7382
|
...opts,
|
|
7359
7383
|
prefix: childPrefix,
|
|
7360
7384
|
isLast
|
|
@@ -7437,8 +7461,8 @@ async function findTsConfig(cwd) {
|
|
|
7437
7461
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
7438
7462
|
for (const f of candidates) {
|
|
7439
7463
|
try {
|
|
7440
|
-
const s = await stat10(
|
|
7441
|
-
if (s.isFile()) return
|
|
7464
|
+
const s = await stat10(path2.join(cwd, f));
|
|
7465
|
+
if (s.isFile()) return path2.join(cwd, f);
|
|
7442
7466
|
} catch {
|
|
7443
7467
|
}
|
|
7444
7468
|
}
|
|
@@ -7474,14 +7498,14 @@ var writeTool = {
|
|
|
7474
7498
|
let existed = false;
|
|
7475
7499
|
let prev = "";
|
|
7476
7500
|
try {
|
|
7477
|
-
const stat11 = await
|
|
7501
|
+
const stat11 = await fs13.stat(absPath);
|
|
7478
7502
|
existed = stat11.isFile();
|
|
7479
7503
|
if (existed) {
|
|
7480
7504
|
if (!ctx.hasRead(absPath)) {
|
|
7481
|
-
prev = await
|
|
7505
|
+
prev = await fs13.readFile(absPath, "utf8");
|
|
7482
7506
|
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
7483
7507
|
} else {
|
|
7484
|
-
prev = await
|
|
7508
|
+
prev = await fs13.readFile(absPath, "utf8");
|
|
7485
7509
|
}
|
|
7486
7510
|
}
|
|
7487
7511
|
} catch (err) {
|
|
@@ -7492,7 +7516,7 @@ var writeTool = {
|
|
|
7492
7516
|
await atomicWrite(absPath, input.content);
|
|
7493
7517
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
7494
7518
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
7495
|
-
const stat10 = await
|
|
7519
|
+
const stat10 = await fs13.stat(absPath);
|
|
7496
7520
|
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
7497
7521
|
ctx.session.recordFileChange({
|
|
7498
7522
|
path: absPath,
|