@wrongstack/tools 0.66.13 → 0.73.1
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/{codebase-stats-tool-C8ApERbn.d.ts → background-indexer-sbsseCCC.d.ts} +49 -1
- package/dist/builtin.js +122 -54
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.d.ts +35 -4
- package/dist/codebase-index/index.js +140 -12
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +145 -13
- package/dist/index.js.map +1 -1
- package/dist/pack.js +122 -54
- package/dist/pack.js.map +1 -1
- package/dist/tool-search.js +5 -1
- package/dist/tool-search.js.map +1 -1
- package/package.json +2 -2
package/dist/pack.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import * as Core from '@wrongstack/core';
|
|
3
3
|
import { buildChildEnv, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, compileGlob, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, resolveWstackPaths } from '@wrongstack/core';
|
|
4
|
-
import * as
|
|
4
|
+
import * as fs12 from 'node:fs/promises';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import { resolve, sep, dirname } from 'node:path';
|
|
7
7
|
import * as os from 'node:os';
|
|
@@ -127,12 +127,12 @@ function safeResolve(input, ctx) {
|
|
|
127
127
|
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
128
128
|
}
|
|
129
129
|
async function assertRealInsideRoot(absPath, ctx) {
|
|
130
|
-
const realRoot = await
|
|
130
|
+
const realRoot = await fs12.realpath(ctx.projectRoot).catch(() => path.resolve(ctx.projectRoot));
|
|
131
131
|
let probe = absPath;
|
|
132
132
|
for (; ; ) {
|
|
133
133
|
let real;
|
|
134
134
|
try {
|
|
135
|
-
real = await
|
|
135
|
+
real = await fs12.realpath(probe);
|
|
136
136
|
} catch (err) {
|
|
137
137
|
if (err.code === "ENOENT") {
|
|
138
138
|
const parent = path.dirname(probe);
|
|
@@ -1296,6 +1296,17 @@ var IndexStore = class {
|
|
|
1296
1296
|
({ id, text }) => ({ id, text })
|
|
1297
1297
|
);
|
|
1298
1298
|
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Largest symbol id currently in the table (0 when empty). New ids must be
|
|
1301
|
+
* allocated from this, NOT from `COUNT(*)`: incremental reindexes delete a
|
|
1302
|
+
* changed file's rows, so the row count drops below the max id and a
|
|
1303
|
+
* count-based id would collide with a surviving row (UNIQUE constraint on
|
|
1304
|
+
* `symbols.id`). Ids may have gaps — that is fine.
|
|
1305
|
+
*/
|
|
1306
|
+
getMaxSymbolId() {
|
|
1307
|
+
const rows = this.db.prepare("SELECT MAX(id) AS m FROM symbols").all();
|
|
1308
|
+
return rows[0]?.m ?? 0;
|
|
1309
|
+
}
|
|
1299
1310
|
// ─── Stats ───────────────────────────────────────────────────────────────────
|
|
1300
1311
|
getStats() {
|
|
1301
1312
|
const sizeBytes = this.sizeBytes();
|
|
@@ -2614,6 +2625,56 @@ function makeSymbol2(opts) {
|
|
|
2614
2625
|
text: `${opts.name} ${opts.signature}`.trim()
|
|
2615
2626
|
};
|
|
2616
2627
|
}
|
|
2628
|
+
function globBody(glob) {
|
|
2629
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
2630
|
+
}
|
|
2631
|
+
function compileGitignore(lines) {
|
|
2632
|
+
const rules = [];
|
|
2633
|
+
for (const raw of lines) {
|
|
2634
|
+
let line = raw.replace(/\r$/, "");
|
|
2635
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
2636
|
+
line = line.trim();
|
|
2637
|
+
let negated = false;
|
|
2638
|
+
if (line.startsWith("!")) {
|
|
2639
|
+
negated = true;
|
|
2640
|
+
line = line.slice(1);
|
|
2641
|
+
}
|
|
2642
|
+
let dirOnly = false;
|
|
2643
|
+
if (line.endsWith("/")) {
|
|
2644
|
+
dirOnly = true;
|
|
2645
|
+
line = line.slice(0, -1);
|
|
2646
|
+
}
|
|
2647
|
+
if (!line) continue;
|
|
2648
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
2649
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
2650
|
+
const body = globBody(line);
|
|
2651
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
2652
|
+
rules.push({
|
|
2653
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
2654
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
2655
|
+
negated,
|
|
2656
|
+
dirOnly
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
return (relPath, isDir) => {
|
|
2660
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2661
|
+
let ignored = false;
|
|
2662
|
+
for (const r of rules) {
|
|
2663
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
2664
|
+
if (re.test(p)) ignored = !r.negated;
|
|
2665
|
+
}
|
|
2666
|
+
return ignored;
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
2670
|
+
let lines = [];
|
|
2671
|
+
try {
|
|
2672
|
+
const raw = await fs12.readFile(path.join(projectRoot, ".gitignore"), "utf8");
|
|
2673
|
+
lines = raw.split("\n");
|
|
2674
|
+
} catch {
|
|
2675
|
+
}
|
|
2676
|
+
return compileGitignore(lines);
|
|
2677
|
+
}
|
|
2617
2678
|
|
|
2618
2679
|
// src/codebase-index/indexer.ts
|
|
2619
2680
|
var DEFAULT_IGNORE = [
|
|
@@ -2627,7 +2688,7 @@ var DEFAULT_IGNORE = [
|
|
|
2627
2688
|
"__snapshots__",
|
|
2628
2689
|
".nyc_output"
|
|
2629
2690
|
];
|
|
2630
|
-
async function findSourceFiles(projectRoot, ignore) {
|
|
2691
|
+
async function findSourceFiles(projectRoot, ignore, isGitIgnored) {
|
|
2631
2692
|
const results = [];
|
|
2632
2693
|
const ignoreSet = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore]);
|
|
2633
2694
|
const globs = [
|
|
@@ -2645,17 +2706,19 @@ async function findSourceFiles(projectRoot, ignore) {
|
|
|
2645
2706
|
const walk = async (dir) => {
|
|
2646
2707
|
let entries;
|
|
2647
2708
|
try {
|
|
2648
|
-
entries = await
|
|
2709
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
2649
2710
|
} catch {
|
|
2650
2711
|
return;
|
|
2651
2712
|
}
|
|
2652
2713
|
for (const e of entries) {
|
|
2653
2714
|
if (ignoreSet.has(e.name)) continue;
|
|
2654
2715
|
const full = path.join(dir, e.name);
|
|
2716
|
+
const rel = path.relative(projectRoot, full).replace(/\\/g, "/");
|
|
2655
2717
|
if (e.isDirectory()) {
|
|
2718
|
+
if (isGitIgnored(rel, true)) continue;
|
|
2656
2719
|
await walk(full);
|
|
2657
2720
|
} else if (e.isFile()) {
|
|
2658
|
-
|
|
2721
|
+
if (isGitIgnored(rel, false)) continue;
|
|
2659
2722
|
const ext = path.extname(e.name);
|
|
2660
2723
|
for (const { ext: extName, pat } of globs) {
|
|
2661
2724
|
if (ext === extName && (pat.test(rel) || pat.test(e.name))) {
|
|
@@ -2698,11 +2761,12 @@ async function runIndexer(_ctx, opts) {
|
|
|
2698
2761
|
const langStats = {};
|
|
2699
2762
|
let filesIndexed = 0;
|
|
2700
2763
|
let symbolsIndexed = 0;
|
|
2764
|
+
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
2701
2765
|
let files;
|
|
2702
2766
|
if (opts.files && opts.files.length > 0) {
|
|
2703
|
-
files = opts.files.map((f) => path.resolve(projectRoot, f));
|
|
2767
|
+
files = opts.files.map((f) => path.resolve(projectRoot, f)).filter((f) => !isGitIgnored(path.relative(projectRoot, f).replace(/\\/g, "/"), false));
|
|
2704
2768
|
} else {
|
|
2705
|
-
files = await findSourceFiles(projectRoot, ignore);
|
|
2769
|
+
files = await findSourceFiles(projectRoot, ignore, isGitIgnored);
|
|
2706
2770
|
}
|
|
2707
2771
|
if (langs && langs.length > 0) {
|
|
2708
2772
|
const langSet = new Set(langs);
|
|
@@ -2719,7 +2783,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2719
2783
|
for (const file of files) {
|
|
2720
2784
|
let stat10;
|
|
2721
2785
|
try {
|
|
2722
|
-
stat10 = await
|
|
2786
|
+
stat10 = await fs12.stat(file);
|
|
2723
2787
|
} catch {
|
|
2724
2788
|
store.deleteFile(file);
|
|
2725
2789
|
continue;
|
|
@@ -2734,11 +2798,11 @@ async function runIndexer(_ctx, opts) {
|
|
|
2734
2798
|
filesIndexed++;
|
|
2735
2799
|
continue;
|
|
2736
2800
|
}
|
|
2737
|
-
store.deleteSymbolsForFile(file);
|
|
2738
2801
|
store.deleteRefsForFile(file);
|
|
2802
|
+
store.deleteSymbolsForFile(file);
|
|
2739
2803
|
let content;
|
|
2740
2804
|
try {
|
|
2741
|
-
content = await
|
|
2805
|
+
content = await fs12.readFile(file, "utf8");
|
|
2742
2806
|
} catch (e) {
|
|
2743
2807
|
errors.push(`read error: ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
2744
2808
|
continue;
|
|
@@ -2761,7 +2825,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2761
2825
|
filesIndexed++;
|
|
2762
2826
|
continue;
|
|
2763
2827
|
}
|
|
2764
|
-
const nextId = store.
|
|
2828
|
+
const nextId = store.getMaxSymbolId() + 1;
|
|
2765
2829
|
const symbolsWithIds = parsed.symbols.map((s, i) => ({ ...s, id: nextId + i }));
|
|
2766
2830
|
store.insertSymbols(symbolsWithIds, nextId);
|
|
2767
2831
|
const count = symbolsWithIds.length;
|
|
@@ -2788,7 +2852,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
2788
2852
|
}
|
|
2789
2853
|
for (const [file_] of existingMeta) {
|
|
2790
2854
|
try {
|
|
2791
|
-
await
|
|
2855
|
+
await fs12.stat(file_);
|
|
2792
2856
|
} catch {
|
|
2793
2857
|
store.deleteFile(file_);
|
|
2794
2858
|
}
|
|
@@ -3171,9 +3235,9 @@ async function fileDiff(input, ctx, _signal) {
|
|
|
3171
3235
|
const results = [];
|
|
3172
3236
|
for (const file of files) {
|
|
3173
3237
|
const absPath = safeResolve(file, ctx);
|
|
3174
|
-
const stat10 = await
|
|
3238
|
+
const stat10 = await fs12.stat(absPath).catch(() => null);
|
|
3175
3239
|
if (!stat10?.isFile()) continue;
|
|
3176
|
-
const content = await
|
|
3240
|
+
const content = await fs12.readFile(absPath, "utf8");
|
|
3177
3241
|
const lines = content.split(/\r?\n/);
|
|
3178
3242
|
results.push(formatWithLineNumbers(file, lines));
|
|
3179
3243
|
}
|
|
@@ -3235,7 +3299,7 @@ var documentTool = {
|
|
|
3235
3299
|
const fileList = input.files ? await resolveFiles(Array.isArray(input.files) ? input.files.join(",") : input.files, cwd) : input.path ? [safeResolve(input.path, ctx)] : [];
|
|
3236
3300
|
for (const absPath of fileList) {
|
|
3237
3301
|
try {
|
|
3238
|
-
const content = await
|
|
3302
|
+
const content = await fs12.readFile(absPath, "utf8");
|
|
3239
3303
|
filesProcessed++;
|
|
3240
3304
|
const processed = processFile(
|
|
3241
3305
|
content,
|
|
@@ -3271,7 +3335,7 @@ async function resolveFiles(filesInput, cwd) {
|
|
|
3271
3335
|
for (const f of files) {
|
|
3272
3336
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
3273
3337
|
try {
|
|
3274
|
-
const stat10 = await
|
|
3338
|
+
const stat10 = await fs12.stat(absPath);
|
|
3275
3339
|
if (stat10.isFile()) resolved.push(absPath);
|
|
3276
3340
|
} catch {
|
|
3277
3341
|
}
|
|
@@ -3363,7 +3427,7 @@ var editTool = {
|
|
|
3363
3427
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
3364
3428
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
3365
3429
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
3366
|
-
const stat10 = await
|
|
3430
|
+
const stat10 = await fs12.stat(absPath).catch((err) => {
|
|
3367
3431
|
if (err.code === "ENOENT") {
|
|
3368
3432
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
3369
3433
|
}
|
|
@@ -3373,8 +3437,8 @@ var editTool = {
|
|
|
3373
3437
|
if (!ctx.hasRead(absPath)) {
|
|
3374
3438
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
3375
3439
|
}
|
|
3376
|
-
const original = await
|
|
3377
|
-
const updated = await
|
|
3440
|
+
const original = await fs12.readFile(absPath, "utf8");
|
|
3441
|
+
const updated = await fs12.stat(absPath);
|
|
3378
3442
|
const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
|
|
3379
3443
|
const lastReadMtime = ctx.lastReadMtime(absPath);
|
|
3380
3444
|
if (lastReadMtime !== void 0 && updated.mtimeMs > lastReadMtime + mtimeTolerance) {
|
|
@@ -3414,7 +3478,7 @@ var editTool = {
|
|
|
3414
3478
|
const newFileLf = input.replace_all ? fileLf.split(oldLf).join(newLf) : fileLf.replace(oldLf, newLf);
|
|
3415
3479
|
const newFile = toStyle(newFileLf, style);
|
|
3416
3480
|
await atomicWrite(absPath, newFile, { mode: updated.mode & 511 });
|
|
3417
|
-
const written = await
|
|
3481
|
+
const written = await fs12.stat(absPath);
|
|
3418
3482
|
ctx.recordRead(absPath, written.mtimeMs);
|
|
3419
3483
|
ctx.session.recordFileChange({
|
|
3420
3484
|
path: absPath,
|
|
@@ -4446,7 +4510,7 @@ var globTool = {
|
|
|
4446
4510
|
}
|
|
4447
4511
|
let entries;
|
|
4448
4512
|
try {
|
|
4449
|
-
entries = await
|
|
4513
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
4450
4514
|
} catch {
|
|
4451
4515
|
return;
|
|
4452
4516
|
}
|
|
@@ -4462,7 +4526,7 @@ var globTool = {
|
|
|
4462
4526
|
} else if (e.isFile()) {
|
|
4463
4527
|
if (re.test(rel) || re.test(name)) {
|
|
4464
4528
|
try {
|
|
4465
|
-
const st = await
|
|
4529
|
+
const st = await fs12.stat(full);
|
|
4466
4530
|
results.push({ rel: full, mtime: st.mtimeMs });
|
|
4467
4531
|
if (results.length >= limit) {
|
|
4468
4532
|
truncated = true;
|
|
@@ -4481,7 +4545,7 @@ var globTool = {
|
|
|
4481
4545
|
};
|
|
4482
4546
|
async function readGitignore(dir) {
|
|
4483
4547
|
try {
|
|
4484
|
-
const raw = await
|
|
4548
|
+
const raw = await fs12.readFile(path.join(dir, ".gitignore"), "utf8");
|
|
4485
4549
|
return raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
4486
4550
|
} catch {
|
|
4487
4551
|
return [];
|
|
@@ -4763,7 +4827,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4763
4827
|
if (stopped || signal.aborted) return;
|
|
4764
4828
|
let entries;
|
|
4765
4829
|
try {
|
|
4766
|
-
entries = await
|
|
4830
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
4767
4831
|
} catch {
|
|
4768
4832
|
return;
|
|
4769
4833
|
}
|
|
@@ -4778,9 +4842,9 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
4778
4842
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
4779
4843
|
if (globRe) globRe.lastIndex = 0;
|
|
4780
4844
|
try {
|
|
4781
|
-
const stat10 = await
|
|
4845
|
+
const stat10 = await fs12.stat(full);
|
|
4782
4846
|
if (stat10.size > 1e6) continue;
|
|
4783
|
-
const head = await
|
|
4847
|
+
const head = await fs12.readFile(full);
|
|
4784
4848
|
if (isBinaryBuffer(head)) continue;
|
|
4785
4849
|
const text = head.toString("utf8");
|
|
4786
4850
|
const lines = text.split(/\r?\n/);
|
|
@@ -4959,7 +5023,7 @@ var jsonTool = {
|
|
|
4959
5023
|
let raw;
|
|
4960
5024
|
if (input.file) {
|
|
4961
5025
|
try {
|
|
4962
|
-
raw = await
|
|
5026
|
+
raw = await fs12.readFile(input.file, "utf8");
|
|
4963
5027
|
} catch {
|
|
4964
5028
|
return { data: null, formatted: "", type: "unknown", error: `Could not read file` };
|
|
4965
5029
|
}
|
|
@@ -4997,8 +5061,8 @@ var jsonTool = {
|
|
|
4997
5061
|
};
|
|
4998
5062
|
}
|
|
4999
5063
|
};
|
|
5000
|
-
function query(data,
|
|
5001
|
-
const parts =
|
|
5064
|
+
function query(data, path19) {
|
|
5065
|
+
const parts = path19.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
5002
5066
|
let current = data;
|
|
5003
5067
|
for (const part of parts) {
|
|
5004
5068
|
if (current === null || current === void 0) return void 0;
|
|
@@ -5267,7 +5331,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
5267
5331
|
}
|
|
5268
5332
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
5269
5333
|
var MAX_TAIL_LINES = 1e5;
|
|
5270
|
-
async function fileLogs(
|
|
5334
|
+
async function fileLogs(path19, lines, filterRe, stream) {
|
|
5271
5335
|
const { createInterface } = await import('node:readline');
|
|
5272
5336
|
const { createReadStream } = await import('node:fs');
|
|
5273
5337
|
const entries = [];
|
|
@@ -5276,7 +5340,7 @@ async function fileLogs(path18, lines, filterRe, stream) {
|
|
|
5276
5340
|
let writeIdx = 0;
|
|
5277
5341
|
let totalLines = 0;
|
|
5278
5342
|
const rl = createInterface({
|
|
5279
|
-
input: createReadStream(
|
|
5343
|
+
input: createReadStream(path19),
|
|
5280
5344
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
5281
5345
|
});
|
|
5282
5346
|
for await (const line of rl) {
|
|
@@ -5297,7 +5361,7 @@ async function fileLogs(path18, lines, filterRe, stream) {
|
|
|
5297
5361
|
if (parsed) entries.push(parsed);
|
|
5298
5362
|
}
|
|
5299
5363
|
return {
|
|
5300
|
-
source:
|
|
5364
|
+
source: path19,
|
|
5301
5365
|
entries,
|
|
5302
5366
|
total: entries.length,
|
|
5303
5367
|
truncated: totalLines > effLines,
|
|
@@ -5476,12 +5540,12 @@ var patchTool = {
|
|
|
5476
5540
|
};
|
|
5477
5541
|
}
|
|
5478
5542
|
}
|
|
5479
|
-
const tmpDir = await
|
|
5543
|
+
const tmpDir = await fs12.mkdtemp(path.join(os.tmpdir(), ".wstack_patch_"));
|
|
5480
5544
|
try {
|
|
5481
|
-
await
|
|
5545
|
+
await fs12.chmod(tmpDir, 448).catch(() => {
|
|
5482
5546
|
});
|
|
5483
5547
|
const patchFile = path.join(tmpDir, "in.diff");
|
|
5484
|
-
await
|
|
5548
|
+
await fs12.writeFile(patchFile, input.patch, { mode: 384 });
|
|
5485
5549
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
5486
5550
|
const result = await runPatch(args, dir, opts.signal);
|
|
5487
5551
|
if (result.exitCode !== 0 && !dryRun) {
|
|
@@ -5502,7 +5566,7 @@ var patchTool = {
|
|
|
5502
5566
|
message: result.stdout || "patch applied"
|
|
5503
5567
|
};
|
|
5504
5568
|
} finally {
|
|
5505
|
-
await
|
|
5569
|
+
await fs12.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
5506
5570
|
});
|
|
5507
5571
|
}
|
|
5508
5572
|
}
|
|
@@ -5744,7 +5808,7 @@ var readTool = {
|
|
|
5744
5808
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
5745
5809
|
let stat10;
|
|
5746
5810
|
try {
|
|
5747
|
-
stat10 = await
|
|
5811
|
+
stat10 = await fs12.stat(absPath);
|
|
5748
5812
|
} catch (err) {
|
|
5749
5813
|
const code = err.code;
|
|
5750
5814
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
@@ -5756,7 +5820,7 @@ var readTool = {
|
|
|
5756
5820
|
if (stat10.size > MAX_BYTES2) {
|
|
5757
5821
|
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES2})`);
|
|
5758
5822
|
}
|
|
5759
|
-
const buf = await
|
|
5823
|
+
const buf = await fs12.readFile(absPath);
|
|
5760
5824
|
if (isBinaryBuffer(buf)) {
|
|
5761
5825
|
throw new Error(`read: "${input.path}" appears to be binary`);
|
|
5762
5826
|
}
|
|
@@ -5824,11 +5888,11 @@ var replaceTool = {
|
|
|
5824
5888
|
const dryRun = input.dry_run ?? false;
|
|
5825
5889
|
const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
|
|
5826
5890
|
const fileList = await resolveFiles2(filesInput, ctx, globRe);
|
|
5827
|
-
const realRoot = await
|
|
5891
|
+
const realRoot = await fs12.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
5828
5892
|
const results = [];
|
|
5829
5893
|
let totalReplacements = 0;
|
|
5830
5894
|
for (const absPath of fileList) {
|
|
5831
|
-
const lstat2 = await
|
|
5895
|
+
const lstat2 = await fs12.lstat(absPath).catch((err) => {
|
|
5832
5896
|
if (err.code === "ENOENT") return null;
|
|
5833
5897
|
throw err;
|
|
5834
5898
|
});
|
|
@@ -5836,17 +5900,17 @@ var replaceTool = {
|
|
|
5836
5900
|
if (lstat2.isSymbolicLink()) continue;
|
|
5837
5901
|
let realPath;
|
|
5838
5902
|
try {
|
|
5839
|
-
realPath = await
|
|
5903
|
+
realPath = await fs12.realpath(absPath);
|
|
5840
5904
|
} catch {
|
|
5841
5905
|
continue;
|
|
5842
5906
|
}
|
|
5843
5907
|
const rel = path.relative(realRoot, realPath);
|
|
5844
5908
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
5845
|
-
const stat10 = await
|
|
5909
|
+
const stat10 = await fs12.stat(realPath).catch(() => null);
|
|
5846
5910
|
if (!stat10 || !stat10.isFile()) continue;
|
|
5847
5911
|
let content;
|
|
5848
5912
|
try {
|
|
5849
|
-
const buf = await
|
|
5913
|
+
const buf = await fs12.readFile(realPath);
|
|
5850
5914
|
if (isBinaryBuffer(buf)) continue;
|
|
5851
5915
|
content = buf.toString("utf8");
|
|
5852
5916
|
} catch {
|
|
@@ -5898,7 +5962,7 @@ async function resolveFiles2(filesInput, ctx, extraGlob) {
|
|
|
5898
5962
|
const resolved = [];
|
|
5899
5963
|
for (const p of parts) {
|
|
5900
5964
|
const absPath = safeResolve(p, ctx);
|
|
5901
|
-
const stat10 = await
|
|
5965
|
+
const stat10 = await fs12.stat(absPath).catch(() => null);
|
|
5902
5966
|
if (stat10?.isFile()) {
|
|
5903
5967
|
resolved.push(absPath);
|
|
5904
5968
|
}
|
|
@@ -5949,7 +6013,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
5949
6013
|
const walk = async (dir) => {
|
|
5950
6014
|
let entries;
|
|
5951
6015
|
try {
|
|
5952
|
-
entries = await
|
|
6016
|
+
entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
5953
6017
|
} catch {
|
|
5954
6018
|
return;
|
|
5955
6019
|
}
|
|
@@ -5957,7 +6021,7 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
5957
6021
|
if (DEFAULT_IGNORE4.includes(e.name)) continue;
|
|
5958
6022
|
const full = path.join(dir, e.name);
|
|
5959
6023
|
try {
|
|
5960
|
-
const stat10 = await
|
|
6024
|
+
const stat10 = await fs12.lstat(full);
|
|
5961
6025
|
if (stat10.isSymbolicLink()) continue;
|
|
5962
6026
|
} catch {
|
|
5963
6027
|
continue;
|
|
@@ -6134,7 +6198,7 @@ async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
|
6134
6198
|
}
|
|
6135
6199
|
const fullPath = target;
|
|
6136
6200
|
if (!dryRun) {
|
|
6137
|
-
await
|
|
6201
|
+
await fs12.mkdir(path.dirname(fullPath), { recursive: true });
|
|
6138
6202
|
await atomicWrite(fullPath, substituteVars(content, name, vars));
|
|
6139
6203
|
}
|
|
6140
6204
|
files.push(resolvedPath);
|
|
@@ -6773,10 +6837,14 @@ var toolSearchTool = {
|
|
|
6773
6837
|
permission: t.permission,
|
|
6774
6838
|
mutating: t.mutating
|
|
6775
6839
|
}));
|
|
6840
|
+
const totalAvailable = tools.length;
|
|
6841
|
+
const hint = results.length === 0 && query2 ? `No tools matched "${input.query}". Use tool-help (without arguments) to see all ${totalAvailable} available tools.` : void 0;
|
|
6776
6842
|
return {
|
|
6777
6843
|
tools: results,
|
|
6778
6844
|
total: filtered.length,
|
|
6779
|
-
truncated: filtered.length > limit
|
|
6845
|
+
truncated: filtered.length > limit,
|
|
6846
|
+
...hint ? { hint } : {},
|
|
6847
|
+
_available: totalAvailable
|
|
6780
6848
|
};
|
|
6781
6849
|
}
|
|
6782
6850
|
};
|
|
@@ -6986,7 +7054,7 @@ var treeTool = {
|
|
|
6986
7054
|
}
|
|
6987
7055
|
};
|
|
6988
7056
|
async function walkDir(dir, depth, opts) {
|
|
6989
|
-
const entries = await
|
|
7057
|
+
const entries = await fs12.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
6990
7058
|
const filtered = entries.filter((e) => {
|
|
6991
7059
|
if (!opts.showHidden && e.name.startsWith(".")) return false;
|
|
6992
7060
|
if (opts.exclude.has(e.name)) return false;
|
|
@@ -7134,14 +7202,14 @@ var writeTool = {
|
|
|
7134
7202
|
let existed = false;
|
|
7135
7203
|
let prev = "";
|
|
7136
7204
|
try {
|
|
7137
|
-
const stat11 = await
|
|
7205
|
+
const stat11 = await fs12.stat(absPath);
|
|
7138
7206
|
existed = stat11.isFile();
|
|
7139
7207
|
if (existed) {
|
|
7140
7208
|
if (!ctx.hasRead(absPath)) {
|
|
7141
|
-
prev = await
|
|
7209
|
+
prev = await fs12.readFile(absPath, "utf8");
|
|
7142
7210
|
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
7143
7211
|
} else {
|
|
7144
|
-
prev = await
|
|
7212
|
+
prev = await fs12.readFile(absPath, "utf8");
|
|
7145
7213
|
}
|
|
7146
7214
|
}
|
|
7147
7215
|
} catch (err) {
|
|
@@ -7152,7 +7220,7 @@ var writeTool = {
|
|
|
7152
7220
|
await atomicWrite(absPath, input.content);
|
|
7153
7221
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
7154
7222
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
7155
|
-
const stat10 = await
|
|
7223
|
+
const stat10 = await fs12.stat(absPath);
|
|
7156
7224
|
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
7157
7225
|
ctx.session.recordFileChange({
|
|
7158
7226
|
path: absPath,
|