@wrongstack/tools 0.300.0 → 0.301.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/batch-tool-use.js +3 -1
- package/dist/browser/index.js +1 -1
- package/dist/builtin.d.ts +3 -2
- package/dist/builtin.js +310 -83
- package/dist/codebase-index/bm25.d.ts +7 -1
- package/dist/codebase-index/index.js +23 -13
- package/dist/codebase-index/project-server.js +23 -13
- package/dist/codebase-index/worker.js +20 -12
- package/dist/git.js +2 -5
- package/dist/glob.js +2 -2
- package/dist/grep.js +118 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +422 -90
- package/dist/json.js +81 -0
- package/dist/logs.js +81 -0
- package/dist/next-steps-tool.d.ts +26 -0
- package/dist/pack.js +310 -83
- package/dist/patch.js +206 -45
- package/dist/read.js +23 -13
- package/dist/replace.js +81 -0
- package/dist/skill.js +51 -2
- package/dist/tool-help.js +2 -2
- package/dist/tool-search.js +1 -1
- package/dist/tool-tier.d.ts +1 -1
- package/dist/tool-tier.js +311 -100
- package/dist/tool-use.js +1 -1
- package/dist/tree.js +13 -3
- package/package.json +3 -3
package/dist/pack.js
CHANGED
|
@@ -5921,16 +5921,23 @@ function looksBinary(content) {
|
|
|
5921
5921
|
}
|
|
5922
5922
|
return bad / sample.length > 0.1;
|
|
5923
5923
|
}
|
|
5924
|
-
function
|
|
5925
|
-
|
|
5926
|
-
let
|
|
5927
|
-
|
|
5928
|
-
if (content.charCodeAt(i) === 10) {
|
|
5929
|
-
line++;
|
|
5930
|
-
lastNl = i;
|
|
5931
|
-
}
|
|
5924
|
+
function newlineOffsets2(content) {
|
|
5925
|
+
const offsets = [];
|
|
5926
|
+
for (let i = 0; i < content.length; i++) {
|
|
5927
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
5932
5928
|
}
|
|
5933
|
-
return
|
|
5929
|
+
return offsets;
|
|
5930
|
+
}
|
|
5931
|
+
function lineColAt(offsets, index) {
|
|
5932
|
+
let low = 0;
|
|
5933
|
+
let high = offsets.length;
|
|
5934
|
+
while (low < high) {
|
|
5935
|
+
const mid = low + high >>> 1;
|
|
5936
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
5937
|
+
else high = mid;
|
|
5938
|
+
}
|
|
5939
|
+
const lastNl = low > 0 ? offsets[low - 1] : -1;
|
|
5940
|
+
return { line: low + 1, col: index - lastNl };
|
|
5934
5941
|
}
|
|
5935
5942
|
function parseGeneric2(opts) {
|
|
5936
5943
|
const { file, lang } = opts;
|
|
@@ -5943,6 +5950,7 @@ function parseGeneric2(opts) {
|
|
|
5943
5950
|
const patterns = patternsFor(lang);
|
|
5944
5951
|
const symbols = [];
|
|
5945
5952
|
const seen = /* @__PURE__ */ new Set();
|
|
5953
|
+
const nlOffsets = newlineOffsets2(content);
|
|
5946
5954
|
for (const pattern of patterns) {
|
|
5947
5955
|
const re = new RegExp(pattern.re.source, pattern.re.flags.includes("g") ? pattern.re.flags : `${pattern.re.flags}g`);
|
|
5948
5956
|
re.lastIndex = 0;
|
|
@@ -5956,7 +5964,7 @@ function parseGeneric2(opts) {
|
|
|
5956
5964
|
if (!/^[A-Za-z_#.@/\w][\w.\-:/#!?]*$/.test(name) && lang !== "md" && lang !== "toml") {
|
|
5957
5965
|
continue;
|
|
5958
5966
|
}
|
|
5959
|
-
const { line, col } = lineColAt(
|
|
5967
|
+
const { line, col } = lineColAt(nlOffsets, match.index ?? 0);
|
|
5960
5968
|
const key = `${name}\0${line}\0${pattern.kind}`;
|
|
5961
5969
|
if (seen.has(key)) continue;
|
|
5962
5970
|
seen.add(key);
|
|
@@ -8751,7 +8759,9 @@ async function executeSingle(call, ctx, governedExecute) {
|
|
|
8751
8759
|
executionMs: Date.now() - start
|
|
8752
8760
|
};
|
|
8753
8761
|
}
|
|
8754
|
-
const tool = ctx.tools.find(
|
|
8762
|
+
const tool = (ctx.catalogTools ?? ctx.tools).find(
|
|
8763
|
+
(candidate) => candidate.name === call.tool
|
|
8764
|
+
);
|
|
8755
8765
|
if (!tool) {
|
|
8756
8766
|
return {
|
|
8757
8767
|
tool: call.tool,
|
|
@@ -8978,7 +8988,7 @@ function parsePrivateOriginAllowlist(raw) {
|
|
|
8978
8988
|
if (!raw?.trim()) return [];
|
|
8979
8989
|
const origins = /* @__PURE__ */ new Set();
|
|
8980
8990
|
for (const entry of raw.split(",")) {
|
|
8981
|
-
const candidate = entry.trim();
|
|
8991
|
+
const candidate = entry.trim().replace(/^["']+|["']+$/gu, "");
|
|
8982
8992
|
if (!candidate) continue;
|
|
8983
8993
|
const url = parseBrowserUrl(candidate, true);
|
|
8984
8994
|
if (url.pathname !== "/" || url.search || url.hash) {
|
|
@@ -10211,9 +10221,9 @@ import * as path16 from "node:path";
|
|
|
10211
10221
|
// src/codebase-index/bm25.ts
|
|
10212
10222
|
var K1 = 1.5;
|
|
10213
10223
|
var B = 0.75;
|
|
10224
|
+
var TOKENISE_RE = new RegExp("[^\\p{L}\\p{N}$']", "gu");
|
|
10214
10225
|
function tokenise(text) {
|
|
10215
|
-
|
|
10216
|
-
return sanitised.toLowerCase().split(" ").filter(Boolean);
|
|
10226
|
+
return text.replace(TOKENISE_RE, " ").toLowerCase().trim().split(/\s+/).filter(Boolean);
|
|
10217
10227
|
}
|
|
10218
10228
|
function splitName(name) {
|
|
10219
10229
|
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([\p{L}])(\d)/gu, "$1 $2").replace(/(\d)([\p{L}])/gu, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
@@ -12699,7 +12709,9 @@ var PROJECT_INDEX_SERVER_METADATA_FILE = "server.json";
|
|
|
12699
12709
|
var PROJECT_INDEX_SERVER_SOCKET_DIR = `wsci-v${PROJECT_INDEX_SERVER_PROTOCOL_VERSION}`;
|
|
12700
12710
|
var buildIdCache;
|
|
12701
12711
|
function projectIndexServerBuildId(entrypoint) {
|
|
12702
|
-
const
|
|
12712
|
+
const href = entrypoint instanceof URL ? entrypoint.href : entrypoint;
|
|
12713
|
+
const cleanHref = href.split(/[?#]/, 1)[0] ?? href;
|
|
12714
|
+
const file = cleanHref.startsWith("file:") ? fileURLToPath(cleanHref) : path17.resolve(cleanHref);
|
|
12703
12715
|
try {
|
|
12704
12716
|
const stat18 = fs12.statSync(file);
|
|
12705
12717
|
if (buildIdCache?.file === file && buildIdCache.mtimeMs === stat18.mtimeMs && buildIdCache.size === stat18.size) {
|
|
@@ -19637,11 +19649,8 @@ function buildArgs(input) {
|
|
|
19637
19649
|
...input.branch.startsWith("-") || input.branch.includes(" --") ? [] : [input.branch]
|
|
19638
19650
|
] : ["branch"];
|
|
19639
19651
|
case "checkout":
|
|
19640
|
-
return [
|
|
19641
|
-
|
|
19642
|
-
...input.branch ? ["--", input.branch] : [],
|
|
19643
|
-
...files.length ? ["--", ...files] : []
|
|
19644
|
-
];
|
|
19652
|
+
if (files.length) return ["checkout", "--", ...files];
|
|
19653
|
+
return input.branch ? ["checkout", input.branch, "--"] : ["checkout"];
|
|
19645
19654
|
case "stash":
|
|
19646
19655
|
return input.message ? ["stash", "push", "-m", input.message] : ["stash", "push"];
|
|
19647
19656
|
case "push":
|
|
@@ -19746,7 +19755,7 @@ async function mapWithConcurrency2(items, limit, fn) {
|
|
|
19746
19755
|
|
|
19747
19756
|
// src/glob.ts
|
|
19748
19757
|
init_util();
|
|
19749
|
-
var DEFAULT_IGNORE2 = DEFAULT_WALK_IGNORE_DIRS2;
|
|
19758
|
+
var DEFAULT_IGNORE2 = new Set(DEFAULT_WALK_IGNORE_DIRS2);
|
|
19750
19759
|
var WALK_CONCURRENCY = 16;
|
|
19751
19760
|
var globTool = {
|
|
19752
19761
|
name: "glob",
|
|
@@ -19825,7 +19834,7 @@ var globTool = {
|
|
|
19825
19834
|
const matchedFiles = [];
|
|
19826
19835
|
for (const e of entries) {
|
|
19827
19836
|
const name = e.name;
|
|
19828
|
-
if (DEFAULT_IGNORE2.
|
|
19837
|
+
if (DEFAULT_IGNORE2.has(name)) continue;
|
|
19829
19838
|
const rel = relPrefix ? `${relPrefix}/${name}` : name;
|
|
19830
19839
|
const full = path30.join(dir, name);
|
|
19831
19840
|
if (e.isDirectory()) {
|
|
@@ -19899,6 +19908,81 @@ var DANGEROUS_PATTERNS = [
|
|
|
19899
19908
|
// Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
|
|
19900
19909
|
/[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
|
|
19901
19910
|
];
|
|
19911
|
+
function hasAmbiguousQuantifiedAlternation(pattern) {
|
|
19912
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
19913
|
+
if (pattern[i] !== "(") continue;
|
|
19914
|
+
if (i > 0 && pattern[i - 1] === "\\") continue;
|
|
19915
|
+
let depth = 0;
|
|
19916
|
+
let inClass = false;
|
|
19917
|
+
let j = i;
|
|
19918
|
+
for (; j < pattern.length; j++) {
|
|
19919
|
+
const ch = pattern[j];
|
|
19920
|
+
if (ch === "\\") {
|
|
19921
|
+
j++;
|
|
19922
|
+
continue;
|
|
19923
|
+
}
|
|
19924
|
+
if (inClass) {
|
|
19925
|
+
if (ch === "]") inClass = false;
|
|
19926
|
+
continue;
|
|
19927
|
+
}
|
|
19928
|
+
if (ch === "[") {
|
|
19929
|
+
inClass = true;
|
|
19930
|
+
continue;
|
|
19931
|
+
}
|
|
19932
|
+
if (ch === "(") depth++;
|
|
19933
|
+
else if (ch === ")") {
|
|
19934
|
+
depth--;
|
|
19935
|
+
if (depth === 0) break;
|
|
19936
|
+
}
|
|
19937
|
+
}
|
|
19938
|
+
if (j >= pattern.length) return false;
|
|
19939
|
+
const next = pattern[j + 1];
|
|
19940
|
+
if (next !== "+" && next !== "*" && next !== "{") continue;
|
|
19941
|
+
let inner = pattern.slice(i + 1, j);
|
|
19942
|
+
inner = inner.replace(/^\?(?::|<?[=!])/u, "");
|
|
19943
|
+
const branches = [];
|
|
19944
|
+
let current = "";
|
|
19945
|
+
let d = 0;
|
|
19946
|
+
let cls = false;
|
|
19947
|
+
for (let k = 0; k < inner.length; k++) {
|
|
19948
|
+
const ch = inner[k];
|
|
19949
|
+
if (ch === "\\") {
|
|
19950
|
+
current += ch + (inner[k + 1] ?? "");
|
|
19951
|
+
k++;
|
|
19952
|
+
continue;
|
|
19953
|
+
}
|
|
19954
|
+
if (cls) {
|
|
19955
|
+
if (ch === "]") cls = false;
|
|
19956
|
+
current += ch;
|
|
19957
|
+
continue;
|
|
19958
|
+
}
|
|
19959
|
+
if (ch === "[") {
|
|
19960
|
+
cls = true;
|
|
19961
|
+
current += ch;
|
|
19962
|
+
continue;
|
|
19963
|
+
}
|
|
19964
|
+
if (ch === "(") d++;
|
|
19965
|
+
if (ch === ")") d--;
|
|
19966
|
+
if (ch === "|" && d === 0) {
|
|
19967
|
+
branches.push(current);
|
|
19968
|
+
current = "";
|
|
19969
|
+
continue;
|
|
19970
|
+
}
|
|
19971
|
+
current += ch;
|
|
19972
|
+
}
|
|
19973
|
+
branches.push(current);
|
|
19974
|
+
if (branches.length < 2) continue;
|
|
19975
|
+
for (let a = 0; a < branches.length; a++) {
|
|
19976
|
+
for (let b = a + 1; b < branches.length; b++) {
|
|
19977
|
+
const x = branches[a];
|
|
19978
|
+
const y = branches[b];
|
|
19979
|
+
if (x === "" || y === "") return true;
|
|
19980
|
+
if (x === y || x.startsWith(y) || y.startsWith(x)) return true;
|
|
19981
|
+
}
|
|
19982
|
+
}
|
|
19983
|
+
}
|
|
19984
|
+
return false;
|
|
19985
|
+
}
|
|
19902
19986
|
function compileUserRegex(pattern, flags) {
|
|
19903
19987
|
if (typeof pattern !== "string") {
|
|
19904
19988
|
return { ok: false, reason: "pattern must be a string" };
|
|
@@ -19917,6 +20001,12 @@ function compileUserRegex(pattern, flags) {
|
|
|
19917
20001
|
};
|
|
19918
20002
|
}
|
|
19919
20003
|
}
|
|
20004
|
+
if (hasAmbiguousQuantifiedAlternation(pattern)) {
|
|
20005
|
+
return {
|
|
20006
|
+
ok: false,
|
|
20007
|
+
reason: "pattern quantifies an alternation with overlapping branches \u2014 rewrite so no two branches can match the same text"
|
|
20008
|
+
};
|
|
20009
|
+
}
|
|
19920
20010
|
try {
|
|
19921
20011
|
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
19922
20012
|
} catch (err) {
|
|
@@ -19933,7 +20023,7 @@ function capSubject(line) {
|
|
|
19933
20023
|
|
|
19934
20024
|
// src/grep.ts
|
|
19935
20025
|
init_util();
|
|
19936
|
-
var DEFAULT_IGNORE3 = DEFAULT_WALK_IGNORE_DIRS3;
|
|
20026
|
+
var DEFAULT_IGNORE3 = new Set(DEFAULT_WALK_IGNORE_DIRS3);
|
|
19937
20027
|
var NATIVE_SCAN_CONCURRENCY = 32;
|
|
19938
20028
|
var NATIVE_READ_CHUNK_BYTES = 64 * 1024;
|
|
19939
20029
|
var NATIVE_MAX_FILE_BYTES = 1e6;
|
|
@@ -20006,7 +20096,7 @@ var grepTool = {
|
|
|
20006
20096
|
field: "pattern"
|
|
20007
20097
|
});
|
|
20008
20098
|
}
|
|
20009
|
-
const base = input.path ?
|
|
20099
|
+
const base = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
|
|
20010
20100
|
const mode = input.output_mode ?? "content";
|
|
20011
20101
|
const limit = Math.max(1, Math.min(input.limit ?? 200, 2e3));
|
|
20012
20102
|
const validation = compileUserRegex(input.pattern, input.case_insensitive ? "i" : "");
|
|
@@ -20331,7 +20421,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
20331
20421
|
const subdirs = [];
|
|
20332
20422
|
for (const e of entries) {
|
|
20333
20423
|
if (stopped) return;
|
|
20334
|
-
if (DEFAULT_IGNORE3.
|
|
20424
|
+
if (DEFAULT_IGNORE3.has(e.name)) continue;
|
|
20335
20425
|
if (e.isSymbolicLink()) continue;
|
|
20336
20426
|
const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
|
|
20337
20427
|
const full = path31.join(dir, e.name);
|
|
@@ -23392,12 +23482,12 @@ import { spawn as spawn13 } from "node:child_process";
|
|
|
23392
23482
|
import * as fs27 from "node:fs/promises";
|
|
23393
23483
|
import * as os9 from "node:os";
|
|
23394
23484
|
import * as path32 from "node:path";
|
|
23395
|
-
import { buildChildEnv as buildChildEnv8 } from "@wrongstack/core/utils";
|
|
23485
|
+
import { buildChildEnv as buildChildEnv8, toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
23396
23486
|
var patchTool = {
|
|
23397
23487
|
name: "patch",
|
|
23398
23488
|
category: "Filesystem",
|
|
23399
23489
|
description: "Apply a unified diff (patch) to the project. This is the correct tool when you have a diff that needs to be applied precisely, including handling of rejects.",
|
|
23400
|
-
usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n-
|
|
23490
|
+
usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n- Applied with `--merge`: a conflicting hunk writes git-style conflict\n markers (<<<<<<< / ======= / >>>>>>>) INTO the file and reports failure.\n It does NOT create .rej/.orig files. `files` lists what changed on disk\n even when the patch failed, so read those back before retrying.\nOften cleaner than many small `edit` operations for larger changes.",
|
|
23401
23491
|
selection: {
|
|
23402
23492
|
doNotUseWhen: "you do not already have a unified diff or only need one precise replacement.",
|
|
23403
23493
|
useInstead: ["edit"]
|
|
@@ -23423,31 +23513,50 @@ var patchTool = {
|
|
|
23423
23513
|
},
|
|
23424
23514
|
async execute(input, ctx, opts) {
|
|
23425
23515
|
if (!input?.patch) throw new Error("patch: patch content is required");
|
|
23426
|
-
const dir = input.directory ? safeResolve(input.directory, ctx) : ctx.cwd;
|
|
23427
23516
|
const strip = Math.max(1, input.strip ?? 1);
|
|
23428
23517
|
const dryRun = input.dry_run ?? false;
|
|
23518
|
+
const refuse = (message) => ({
|
|
23519
|
+
applied: 0,
|
|
23520
|
+
rejected: 1,
|
|
23521
|
+
files: [],
|
|
23522
|
+
dry_run: dryRun,
|
|
23523
|
+
message
|
|
23524
|
+
});
|
|
23525
|
+
let dir;
|
|
23526
|
+
try {
|
|
23527
|
+
dir = input.directory ? await safeResolveReal(input.directory, ctx) : ctx.cwd;
|
|
23528
|
+
} catch (err) {
|
|
23529
|
+
return refuse(`patch refused: ${toErrorMessage4(err)}`);
|
|
23530
|
+
}
|
|
23531
|
+
const realRoot = await fs27.realpath(ctx.projectRoot).catch(() => path32.resolve(ctx.projectRoot));
|
|
23429
23532
|
const targets = extractDiffTargets(input.patch);
|
|
23430
23533
|
const resolvedTargets = [];
|
|
23431
23534
|
for (const t of targets) {
|
|
23432
|
-
const stripped = stripPathComponents(t, strip);
|
|
23535
|
+
const stripped = stripPathComponents(t.raw, strip);
|
|
23433
23536
|
if (!stripped) continue;
|
|
23537
|
+
if (path32.isAbsolute(stripped)) {
|
|
23538
|
+
return refuse(`patch refused: target "${t.raw}" strips to absolute path`);
|
|
23539
|
+
}
|
|
23434
23540
|
const candidate = path32.resolve(dir, stripped);
|
|
23435
|
-
|
|
23541
|
+
let real;
|
|
23542
|
+
try {
|
|
23543
|
+
real = await resolveRealInsideRoot(candidate, ctx);
|
|
23544
|
+
} catch (err) {
|
|
23545
|
+
return refuse(`patch refused: target "${t.raw}" ${toErrorMessage4(err)}`);
|
|
23546
|
+
}
|
|
23547
|
+
const rel = path32.relative(realRoot, real);
|
|
23436
23548
|
if (rel.startsWith("..") || path32.isAbsolute(rel)) {
|
|
23437
|
-
return {
|
|
23438
|
-
applied: 0,
|
|
23439
|
-
rejected: 1,
|
|
23440
|
-
files: [],
|
|
23441
|
-
dry_run: dryRun,
|
|
23442
|
-
message: `patch refused: target "${t}" resolves outside project root`
|
|
23443
|
-
};
|
|
23549
|
+
return refuse(`patch refused: target "${t.raw}" resolves outside project root`);
|
|
23444
23550
|
}
|
|
23445
|
-
resolvedTargets.push(
|
|
23551
|
+
resolvedTargets.push({ raw: t.raw, deleted: t.deleted, abs: real });
|
|
23446
23552
|
}
|
|
23447
23553
|
const beforeContents = /* @__PURE__ */ new Map();
|
|
23554
|
+
const beforeExisted = /* @__PURE__ */ new Set();
|
|
23448
23555
|
if (!dryRun) {
|
|
23449
23556
|
for (const target of resolvedTargets) {
|
|
23450
|
-
|
|
23557
|
+
const existed = (await fs27.stat(target.abs).catch(() => null))?.isFile() ?? false;
|
|
23558
|
+
if (existed) beforeExisted.add(target.abs);
|
|
23559
|
+
beforeContents.set(target.abs, await readTextForTracking(target.abs));
|
|
23451
23560
|
}
|
|
23452
23561
|
}
|
|
23453
23562
|
const tmpDir = await fs27.mkdtemp(path32.join(os9.tmpdir(), ".wstack_patch_"));
|
|
@@ -23457,32 +23566,70 @@ var patchTool = {
|
|
|
23457
23566
|
const patchFile = path32.join(tmpDir, "in.diff");
|
|
23458
23567
|
await fs27.writeFile(patchFile, input.patch, { mode: 384 });
|
|
23459
23568
|
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
23460
|
-
const result = await runPatch(args, dir, opts.signal
|
|
23461
|
-
|
|
23462
|
-
|
|
23463
|
-
|
|
23464
|
-
|
|
23465
|
-
|
|
23466
|
-
dry_run: dryRun,
|
|
23467
|
-
message: `patch failed: ${result.stderr || result.stdout}`
|
|
23468
|
-
};
|
|
23469
|
-
}
|
|
23470
|
-
const patched = extractPatchedFiles(result.stdout);
|
|
23569
|
+
const result = await runPatch(args, dir, opts.signal, {
|
|
23570
|
+
patchFile,
|
|
23571
|
+
strip,
|
|
23572
|
+
dryRun
|
|
23573
|
+
});
|
|
23574
|
+
const touched = [];
|
|
23471
23575
|
if (!dryRun) {
|
|
23472
23576
|
for (const target of resolvedTargets) {
|
|
23473
|
-
const
|
|
23474
|
-
const
|
|
23577
|
+
const abs = target.abs;
|
|
23578
|
+
const before = beforeContents.get(abs) ?? null;
|
|
23579
|
+
const stat18 = await fs27.stat(abs).catch(() => null);
|
|
23580
|
+
if (!stat18?.isFile()) {
|
|
23581
|
+
if (beforeExisted.has(abs)) {
|
|
23582
|
+
touched.push(abs);
|
|
23583
|
+
ctx.session?.recordFileChange?.({
|
|
23584
|
+
path: abs,
|
|
23585
|
+
action: "deleted",
|
|
23586
|
+
before,
|
|
23587
|
+
after: null
|
|
23588
|
+
});
|
|
23589
|
+
}
|
|
23590
|
+
continue;
|
|
23591
|
+
}
|
|
23592
|
+
const after = await readTextForTracking(abs);
|
|
23475
23593
|
if (after === null || after === before) continue;
|
|
23476
|
-
|
|
23477
|
-
|
|
23594
|
+
touched.push(abs);
|
|
23595
|
+
ctx.recordRead?.(abs, stat18.mtimeMs, "write", sha256hex(after));
|
|
23478
23596
|
ctx.session?.recordFileChange?.({
|
|
23479
|
-
path:
|
|
23597
|
+
path: abs,
|
|
23480
23598
|
action: before === null ? "created" : "modified",
|
|
23481
23599
|
before,
|
|
23482
23600
|
after
|
|
23483
23601
|
});
|
|
23484
23602
|
}
|
|
23485
23603
|
}
|
|
23604
|
+
if (result.exitCode !== 0) {
|
|
23605
|
+
if (!dryRun) {
|
|
23606
|
+
const partial = touched.length > 0 ? ` ${touched.length} file(s) were still modified on disk and have been recorded for rewind: ${touched.map((p) => path32.relative(realRoot, p) || p).join(", ")}.` : "";
|
|
23607
|
+
return {
|
|
23608
|
+
applied: touched.length,
|
|
23609
|
+
rejected: 1,
|
|
23610
|
+
// Normalize to relative-to-realRoot for API consistency with the
|
|
23611
|
+
// success path (which returns GNU patch's dir-relative names).
|
|
23612
|
+
// `touched` entries are realpaths from resolveRealInsideRoot, and
|
|
23613
|
+
// realRoot is also a realpath, so path.relative is like-for-like.
|
|
23614
|
+
files: touched.map((p) => path32.relative(realRoot, p) || p),
|
|
23615
|
+
dry_run: dryRun,
|
|
23616
|
+
message: `patch failed: ${result.stderr || result.stdout}${partial}`
|
|
23617
|
+
};
|
|
23618
|
+
}
|
|
23619
|
+
const wouldPatch = extractPatchedFiles(result.stdout);
|
|
23620
|
+
return {
|
|
23621
|
+
applied: wouldPatch.length,
|
|
23622
|
+
rejected: 1,
|
|
23623
|
+
files: wouldPatch,
|
|
23624
|
+
dry_run: dryRun,
|
|
23625
|
+
message: `patch preview: would conflict \u2014 ${result.stderr || result.stdout}`
|
|
23626
|
+
};
|
|
23627
|
+
}
|
|
23628
|
+
const patched = result.engine === "git" ? [
|
|
23629
|
+
...new Set(
|
|
23630
|
+
resolvedTargets.map((target) => path32.relative(dir, target.abs) || target.abs)
|
|
23631
|
+
)
|
|
23632
|
+
] : extractPatchedFiles(result.stdout);
|
|
23486
23633
|
return {
|
|
23487
23634
|
applied: patched.length,
|
|
23488
23635
|
rejected: 0,
|
|
@@ -23510,27 +23657,86 @@ async function readTextForTracking(absPath) {
|
|
|
23510
23657
|
}
|
|
23511
23658
|
function extractDiffTargets(patch) {
|
|
23512
23659
|
const out = [];
|
|
23513
|
-
const
|
|
23514
|
-
|
|
23515
|
-
|
|
23516
|
-
|
|
23517
|
-
|
|
23518
|
-
|
|
23519
|
-
|
|
23660
|
+
const clean = (raw) => {
|
|
23661
|
+
if (!raw) return "";
|
|
23662
|
+
return (raw.length > 4096 ? raw.slice(0, 4096) : raw).trim();
|
|
23663
|
+
};
|
|
23664
|
+
let lastOld;
|
|
23665
|
+
let inHunk = false;
|
|
23666
|
+
let oldLinesLeft = 0;
|
|
23667
|
+
let newLinesLeft = 0;
|
|
23668
|
+
for (const line of patch.split(/\r?\n/)) {
|
|
23669
|
+
const hunkMatch = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line);
|
|
23670
|
+
if (hunkMatch) {
|
|
23671
|
+
inHunk = true;
|
|
23672
|
+
oldLinesLeft = hunkMatch[1] ? Number(hunkMatch[1]) : 1;
|
|
23673
|
+
newLinesLeft = hunkMatch[2] ? Number(hunkMatch[2]) : 1;
|
|
23674
|
+
lastOld = void 0;
|
|
23675
|
+
continue;
|
|
23676
|
+
}
|
|
23677
|
+
if (inHunk) {
|
|
23678
|
+
const ch = line[0];
|
|
23679
|
+
if (ch === "-") oldLinesLeft--;
|
|
23680
|
+
else if (ch === "+") newLinesLeft--;
|
|
23681
|
+
else if (ch === " " || ch === void 0) {
|
|
23682
|
+
oldLinesLeft--;
|
|
23683
|
+
newLinesLeft--;
|
|
23684
|
+
}
|
|
23685
|
+
if (oldLinesLeft <= 0 && newLinesLeft <= 0) inHunk = false;
|
|
23686
|
+
continue;
|
|
23687
|
+
}
|
|
23688
|
+
const oldMatch = /^---\s+([^\t\r\n]+)/.exec(line);
|
|
23689
|
+
if (oldMatch) {
|
|
23690
|
+
lastOld = clean(oldMatch[1]);
|
|
23691
|
+
continue;
|
|
23692
|
+
}
|
|
23693
|
+
const newMatch = /^\+\+\+\s+([^\t\r\n]+)/.exec(line);
|
|
23694
|
+
if (!newMatch) continue;
|
|
23695
|
+
const newTarget = clean(newMatch[1]);
|
|
23696
|
+
if (newTarget && newTarget !== "/dev/null") {
|
|
23697
|
+
out.push({ raw: newTarget, deleted: false });
|
|
23698
|
+
} else if (lastOld && lastOld !== "/dev/null") {
|
|
23699
|
+
out.push({ raw: lastOld, deleted: true });
|
|
23700
|
+
}
|
|
23701
|
+
lastOld = void 0;
|
|
23520
23702
|
}
|
|
23521
23703
|
return out;
|
|
23522
23704
|
}
|
|
23523
23705
|
function stripPathComponents(p, strip) {
|
|
23524
|
-
const
|
|
23525
|
-
|
|
23526
|
-
|
|
23706
|
+
const s = p.replace(/\\/g, "/");
|
|
23707
|
+
let idx = 0;
|
|
23708
|
+
for (let i = 0; i < strip; i++) {
|
|
23709
|
+
while (idx < s.length && s[idx] !== "/") idx++;
|
|
23710
|
+
let hadSlash = false;
|
|
23711
|
+
while (idx < s.length && s[idx] === "/") {
|
|
23712
|
+
idx++;
|
|
23713
|
+
hadSlash = true;
|
|
23714
|
+
}
|
|
23715
|
+
if (!hadSlash) return void 0;
|
|
23716
|
+
}
|
|
23717
|
+
return s.slice(idx) || void 0;
|
|
23718
|
+
}
|
|
23719
|
+
function runPatch(args, cwd, signal, fallback) {
|
|
23720
|
+
return runPatchProcess("patch", args, cwd, signal).then(async (result) => {
|
|
23721
|
+
if (!result.unavailable) return { ...result, engine: "patch" };
|
|
23722
|
+
const gitArgs = [
|
|
23723
|
+
"apply",
|
|
23724
|
+
"--unsafe-paths",
|
|
23725
|
+
`-p${fallback.strip}`,
|
|
23726
|
+
"--verbose",
|
|
23727
|
+
...fallback.dryRun ? ["--check"] : [],
|
|
23728
|
+
fallback.patchFile
|
|
23729
|
+
];
|
|
23730
|
+
const gitResult = await runPatchProcess("git", gitArgs, cwd, signal);
|
|
23731
|
+
return { ...gitResult, engine: "git" };
|
|
23732
|
+
});
|
|
23527
23733
|
}
|
|
23528
|
-
function
|
|
23734
|
+
function runPatchProcess(command, args, cwd, signal) {
|
|
23529
23735
|
return new Promise((resolve16) => {
|
|
23530
23736
|
let stdout = "";
|
|
23531
23737
|
let stderr = "";
|
|
23532
23738
|
const env = { ...buildChildEnv8(), LANG: "C", LC_ALL: "C" };
|
|
23533
|
-
const child = spawn13(
|
|
23739
|
+
const child = spawn13(command, args, {
|
|
23534
23740
|
cwd,
|
|
23535
23741
|
signal,
|
|
23536
23742
|
env,
|
|
@@ -23543,13 +23749,24 @@ function runPatch(args, cwd, signal) {
|
|
|
23543
23749
|
child.stderr?.on("data", (c) => {
|
|
23544
23750
|
stderr += c.toString();
|
|
23545
23751
|
});
|
|
23546
|
-
child.on(
|
|
23547
|
-
|
|
23752
|
+
child.on(
|
|
23753
|
+
"close",
|
|
23754
|
+
(code) => resolve16({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
|
|
23755
|
+
);
|
|
23756
|
+
child.on(
|
|
23757
|
+
"error",
|
|
23758
|
+
(e) => resolve16({
|
|
23759
|
+
exitCode: 1,
|
|
23760
|
+
stdout: "",
|
|
23761
|
+
stderr: e.message,
|
|
23762
|
+
unavailable: e.code === "ENOENT"
|
|
23763
|
+
})
|
|
23764
|
+
);
|
|
23548
23765
|
});
|
|
23549
23766
|
}
|
|
23550
23767
|
function extractPatchedFiles(output) {
|
|
23551
23768
|
const files = [];
|
|
23552
|
-
const re = /patching file (.+)/gi;
|
|
23769
|
+
const re = /(?:patching|checking) file (.+)/gi;
|
|
23553
23770
|
for (const m of output.matchAll(re)) {
|
|
23554
23771
|
if (m[1]) files.push(m[1]);
|
|
23555
23772
|
}
|
|
@@ -23858,7 +24075,7 @@ function mkResult(plan, ok, message, todos) {
|
|
|
23858
24075
|
init_util();
|
|
23859
24076
|
import * as fs28 from "node:fs/promises";
|
|
23860
24077
|
import { FsError, ToolValidationError as ToolValidationError5 } from "@wrongstack/core/types";
|
|
23861
|
-
import { toErrorMessage as
|
|
24078
|
+
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
23862
24079
|
var ADVANCED_MODE_META_KEY = "tools.read.advancedMode";
|
|
23863
24080
|
var MAX_BYTES2 = 5 * 1024 * 1024;
|
|
23864
24081
|
var readTool = {
|
|
@@ -23926,7 +24143,7 @@ var readTool = {
|
|
|
23926
24143
|
});
|
|
23927
24144
|
}
|
|
23928
24145
|
throw new FsError({
|
|
23929
|
-
message: `read: failed to stat "${input.path}": ${
|
|
24146
|
+
message: `read: failed to stat "${input.path}": ${toErrorMessage5(err)}`,
|
|
23930
24147
|
code: "FS_READ_FAILED",
|
|
23931
24148
|
path: absPath,
|
|
23932
24149
|
context: { errno: code },
|
|
@@ -24585,7 +24802,7 @@ function substituteVars(content, name, vars) {
|
|
|
24585
24802
|
// src/search.ts
|
|
24586
24803
|
import { FetchError as FetchError3, ToolValidationError as ToolValidationError7 } from "@wrongstack/core/types";
|
|
24587
24804
|
import { expectDefined as expectDefined9 } from "@wrongstack/core/utils";
|
|
24588
|
-
import { toErrorMessage as
|
|
24805
|
+
import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
24589
24806
|
var DEFAULT_NUM = 10;
|
|
24590
24807
|
var MAX_RESULTS = 50;
|
|
24591
24808
|
var TIMEOUT_MS3 = 15e3;
|
|
@@ -24789,7 +25006,7 @@ async function duckduckgoSearch(query, num, signal) {
|
|
|
24789
25006
|
return parseDuckDuckGo(html, num);
|
|
24790
25007
|
} catch (err) {
|
|
24791
25008
|
console.log(
|
|
24792
|
-
JSON.stringify({ level: "debug", event: "search_failed", query, error:
|
|
25009
|
+
JSON.stringify({ level: "debug", event: "search_failed", query, error: toErrorMessage6(err) })
|
|
24793
25010
|
);
|
|
24794
25011
|
return [{ title: "Search unavailable", url: "https://duckduckgo.com/unavailable", snippet: "Could not reach DuckDuckGo", score: 0 }];
|
|
24795
25012
|
}
|
|
@@ -24973,7 +25190,7 @@ function decodeHtmlEntities(text) {
|
|
|
24973
25190
|
|
|
24974
25191
|
// src/set-working-dir.ts
|
|
24975
25192
|
import * as fs31 from "node:fs/promises";
|
|
24976
|
-
import { toErrorMessage as
|
|
25193
|
+
import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
|
|
24977
25194
|
var setWorkingDirTool = {
|
|
24978
25195
|
name: "set_working_dir",
|
|
24979
25196
|
category: "Context",
|
|
@@ -25007,7 +25224,7 @@ var setWorkingDirTool = {
|
|
|
25007
25224
|
} catch (err) {
|
|
25008
25225
|
return {
|
|
25009
25226
|
current: ctx.workingDir,
|
|
25010
|
-
error:
|
|
25227
|
+
error: toErrorMessage7(err)
|
|
25011
25228
|
};
|
|
25012
25229
|
}
|
|
25013
25230
|
try {
|
|
@@ -25742,7 +25959,7 @@ var toolHelpTool = {
|
|
|
25742
25959
|
const format = input.format ?? "short";
|
|
25743
25960
|
const includeExamples = input.include_examples ?? false;
|
|
25744
25961
|
if (input.tool) {
|
|
25745
|
-
const tool = ctx.tools.find((t) => t.name === input.tool);
|
|
25962
|
+
const tool = (ctx.catalogTools ?? ctx.tools).find((t) => t.name === input.tool);
|
|
25746
25963
|
if (!tool) {
|
|
25747
25964
|
return {
|
|
25748
25965
|
tool: input.tool,
|
|
@@ -25767,7 +25984,7 @@ var toolHelpTool = {
|
|
|
25767
25984
|
total: 1
|
|
25768
25985
|
};
|
|
25769
25986
|
}
|
|
25770
|
-
const allTools = ctx.tools.map((t) => ({
|
|
25987
|
+
const allTools = (ctx.catalogTools ?? ctx.tools).map((t) => ({
|
|
25771
25988
|
name: t.name,
|
|
25772
25989
|
description: t.description,
|
|
25773
25990
|
usageHint: t.usageHint ?? "",
|
|
@@ -25875,7 +26092,7 @@ var toolSearchTool = {
|
|
|
25875
26092
|
},
|
|
25876
26093
|
async execute(input, ctx) {
|
|
25877
26094
|
const limit = Math.min(input.limit ?? 20, 100);
|
|
25878
|
-
const tools = ctx.tools;
|
|
26095
|
+
const tools = ctx.catalogTools ?? ctx.tools;
|
|
25879
26096
|
const query = input.query?.toLowerCase() ?? "";
|
|
25880
26097
|
const filtered = tools.filter((t) => {
|
|
25881
26098
|
if (query && !t.name.toLowerCase().includes(query) && !t.description.toLowerCase().includes(query)) {
|
|
@@ -25955,7 +26172,7 @@ var toolUseTool = {
|
|
|
25955
26172
|
executionMs: 0
|
|
25956
26173
|
};
|
|
25957
26174
|
}
|
|
25958
|
-
const tool = ctx.tools.find((t) => t.name === input.tool);
|
|
26175
|
+
const tool = (ctx.catalogTools ?? ctx.tools).find((t) => t.name === input.tool);
|
|
25959
26176
|
if (!tool) {
|
|
25960
26177
|
return {
|
|
25961
26178
|
tool: input.tool,
|
|
@@ -26013,7 +26230,13 @@ init_util();
|
|
|
26013
26230
|
import * as fs32 from "node:fs/promises";
|
|
26014
26231
|
import * as path36 from "node:path";
|
|
26015
26232
|
import { DEFAULT_WALK_IGNORE_DIRS as DEFAULT_WALK_IGNORE_DIRS4, expectDefined as expectDefined10 } from "@wrongstack/core/utils";
|
|
26016
|
-
var DEFAULT_IGNORE5 =
|
|
26233
|
+
var DEFAULT_IGNORE5 = /* @__PURE__ */ new Set([
|
|
26234
|
+
...DEFAULT_WALK_IGNORE_DIRS4,
|
|
26235
|
+
".wrongstack",
|
|
26236
|
+
".ssh",
|
|
26237
|
+
".gnupg",
|
|
26238
|
+
".aws"
|
|
26239
|
+
]);
|
|
26017
26240
|
var DEFAULT_MAX_ENTRIES2 = 5e3;
|
|
26018
26241
|
var MAX_TREE_OUTPUT_BYTES = 256 * 1024;
|
|
26019
26242
|
var treeTool = {
|
|
@@ -26174,8 +26397,12 @@ async function walkDir(dir, depth, opts) {
|
|
|
26174
26397
|
return true;
|
|
26175
26398
|
});
|
|
26176
26399
|
if (depth > 0) {
|
|
26177
|
-
|
|
26178
|
-
|
|
26400
|
+
let dirCount = 0;
|
|
26401
|
+
let fileCount = 0;
|
|
26402
|
+
for (const e of filtered) {
|
|
26403
|
+
if (e.isDirectory()) dirCount++;
|
|
26404
|
+
else if (e.isFile()) fileCount++;
|
|
26405
|
+
}
|
|
26179
26406
|
opts.totalDirs.value += dirCount;
|
|
26180
26407
|
opts.totalFiles.value += fileCount;
|
|
26181
26408
|
opts.onProgress?.();
|