@wrongstack/tools 0.313.0 → 0.316.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/_edit-match.d.ts +3 -2
- package/dist/_output-spool.d.ts +4 -3
- package/dist/_shell-pick.d.ts +2 -1
- package/dist/_spawn-stream.d.ts +2 -1
- package/dist/_syntax-check.d.ts +2 -1
- package/dist/audit.js +10 -4055
- package/dist/bash-kill-guard.d.ts +3 -6
- package/dist/bash.js +9 -2308
- package/dist/batch-tool-use.js +2 -187
- package/dist/browser/index.js +25 -1339
- package/dist/browser/network-guard-proxy.d.ts +2 -1
- package/dist/builtin.js +69 -33949
- package/dist/chunk-22B7GE5T.js +260 -0
- package/dist/chunk-2VSZC6QM.js +113 -0
- package/dist/chunk-3PVYQFRQ.js +472 -0
- package/dist/chunk-42BHI7TN.js +337 -0
- package/dist/chunk-4BHUSMNL.js +104 -0
- package/dist/chunk-4I4LWFOO.js +431 -0
- package/dist/chunk-4UESMQDP.js +122 -0
- package/dist/chunk-553IQUMO.js +478 -0
- package/dist/chunk-5H4GSJAI.js +223 -0
- package/dist/chunk-6RNIFLG4.js +305 -0
- package/dist/chunk-7EY5DCRO.js +434 -0
- package/dist/chunk-A6BRWIXN.js +317 -0
- package/dist/chunk-AHHDWNXC.js +200 -0
- package/dist/chunk-BSLVR56S.js +199 -0
- package/dist/chunk-BTSFTGIL.js +7 -0
- package/dist/chunk-D33S4RQG.js +222 -0
- package/dist/chunk-DA4KNE2H.js +4412 -0
- package/dist/chunk-DR7JOQTC.js +520 -0
- package/dist/chunk-EEOFQDT6.js +898 -0
- package/dist/chunk-EJWSCQ6D.js +137 -0
- package/dist/chunk-EMRGN4KM.js +170 -0
- package/dist/chunk-FQZOHLG3.js +89 -0
- package/dist/chunk-GOIZNNOA.js +221 -0
- package/dist/chunk-HFXSNKCV.js +196 -0
- package/dist/chunk-HYX2TZ7O.js +1272 -0
- package/dist/chunk-I64BS3VC.js +15 -0
- package/dist/chunk-IVWV2UM6.js +955 -0
- package/dist/chunk-IXMNTNW5.js +238 -0
- package/dist/chunk-IYK7XGDZ.js +892 -0
- package/dist/chunk-J5SKD6LN.js +235 -0
- package/dist/chunk-JNNVRHMV.js +2111 -0
- package/dist/chunk-JOHXDF3D.js +295 -0
- package/dist/chunk-JRJJX3L5.js +424 -0
- package/dist/chunk-K3WNSHUF.js +1371 -0
- package/dist/chunk-L25CVUVR.js +2830 -0
- package/dist/chunk-LIJQMYYX.js +59 -0
- package/dist/chunk-LWKUTMD5.js +197 -0
- package/dist/chunk-MQX2KXVW.js +54 -0
- package/dist/chunk-MZZN7VCD.js +359 -0
- package/dist/chunk-NBDZMMG2.js +169 -0
- package/dist/chunk-NKJW5GFO.js +366 -0
- package/dist/chunk-NKNOBUBU.js +522 -0
- package/dist/chunk-O4MBV4AX.js +134 -0
- package/dist/chunk-OUBDL3WH.js +3515 -0
- package/dist/chunk-PQKK5JH7.js +101 -0
- package/dist/chunk-PY2IOS7C.js +1000 -0
- package/dist/chunk-Q5JPPCZC.js +12 -0
- package/dist/chunk-QBV725YG.js +128 -0
- package/dist/chunk-QZQSPUBF.js +315 -0
- package/dist/chunk-RGISBZBD.js +231 -0
- package/dist/chunk-RJZQWEMS.js +84 -0
- package/dist/chunk-RXISBFR5.js +315 -0
- package/dist/chunk-S5CCLZL3.js +99 -0
- package/dist/chunk-SDLVH5RZ.js +178 -0
- package/dist/chunk-T2F4S7U4.js +1723 -0
- package/dist/chunk-TEBQ4VPZ.js +163 -0
- package/dist/chunk-U4D5S3VQ.js +21 -0
- package/dist/chunk-U4VAATKP.js +193 -0
- package/dist/chunk-UCYQTZ7F.js +113 -0
- package/dist/chunk-UKU2ZX4C.js +203 -0
- package/dist/chunk-UNSKG6DC.js +159 -0
- package/dist/chunk-V43EQ6VR.js +68 -0
- package/dist/chunk-VB3MJGCV.js +610 -0
- package/dist/chunk-VFKPWSOX.js +154 -0
- package/dist/chunk-VJNHFZ7J.js +190 -0
- package/dist/chunk-VXECAJJ7.js +1091 -0
- package/dist/chunk-WHIIIREL.js +223 -0
- package/dist/chunk-Z6DAKQ3U.js +1 -0
- package/dist/chunk-ZJQ4IGXK.js +474 -0
- package/dist/chunk-ZLTFLC25.js +385 -0
- package/dist/circuit-breaker.js +3 -191
- package/dist/codebase-index/ast-invariant-engine.d.ts +5 -4
- package/dist/codebase-index/binary-frame.d.ts +0 -2
- package/dist/codebase-index/bm25.d.ts +1 -1
- package/dist/codebase-index/circuit-breaker.d.ts +2 -1
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-repo-map-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-skeleton-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +3 -2
- package/dist/codebase-index/content-hash.d.ts +0 -2
- package/dist/codebase-index/import-extractor.d.ts +0 -13
- package/dist/codebase-index/index-service.d.ts +1 -1
- package/dist/codebase-index/index.js +84 -13688
- package/dist/codebase-index/languages.d.ts +2 -1
- package/dist/codebase-index/module-roots.d.ts +3 -2
- package/dist/codebase-index/parser-batch.d.ts +0 -5
- package/dist/codebase-index/parser-dispatch.d.ts +2 -1
- package/dist/codebase-index/parser-output.d.ts +4 -3
- package/dist/codebase-index/parser-worker-pool.d.ts +0 -2
- package/dist/codebase-index/parser-worker-script.d.ts +1 -16
- package/dist/codebase-index/parser-worker-script.js +8 -3995
- package/dist/codebase-index/project-server-client-state.d.ts +2 -2
- package/dist/codebase-index/project-server-endpoint.d.ts +0 -15
- package/dist/codebase-index/project-server.js +60 -8548
- package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +0 -2
- package/dist/codebase-index/ts-parser.d.ts +1 -1
- package/dist/codebase-index/vector-search.d.ts +2 -1
- package/dist/codebase-index/wal-maintenance.d.ts +3 -2
- package/dist/codebase-index/worker.js +20 -8416
- package/dist/codebase-index/writer-admin.d.ts +0 -1
- package/dist/codebase-index/writer-search.d.ts +0 -9
- package/dist/design.js +2 -299
- package/dist/diff.js +4 -256
- package/dist/document.js +4 -222
- package/dist/e2e.js +5 -476
- package/dist/edit.js +14 -10200
- package/dist/exec.js +16 -3131
- package/dist/fetch.js +8 -398
- package/dist/format.js +10 -3750
- package/dist/generic-parser-VUXDIAOZ.js +13 -0
- package/dist/git.js +4 -380
- package/dist/glob.js +6 -280
- package/dist/go-parser-JXYKJHRC.js +469 -0
- package/dist/grep.js +7 -604
- package/dist/index.js +318 -35387
- package/dist/install.js +10 -4091
- package/dist/json-parser-IBHZ4BZC.js +213 -0
- package/dist/json.js +4 -665
- package/dist/kanban-contract-actions.d.ts +1 -1
- package/dist/kanban-detail-actions.d.ts +2 -1
- package/dist/kanban-presence.d.ts +2 -1
- package/dist/kanban-serializer.d.ts +0 -8
- package/dist/kanban-split-task-handler.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +2 -1
- package/dist/kanban.js +3 -2096
- package/dist/languages/diagnostics.d.ts +2 -1
- package/dist/languages/index.js +23 -4664
- package/dist/languages/legacy-bridge.d.ts +3 -2
- package/dist/languages/types.d.ts +2 -1
- package/dist/legacy-bridge-7M6QLUQN.js +18 -0
- package/dist/lint.js +10 -3706
- package/dist/logs.js +4 -287
- package/dist/memory.js +6 -215
- package/dist/mode.js +3 -99
- package/dist/outdated.js +4 -5170
- package/dist/pack.js +65 -33897
- package/dist/parser-dispatch-QH4XPGG6.js +14 -0
- package/dist/patch.js +13 -9935
- package/dist/plan.js +5 -3286
- package/dist/process-registry.js +8 -651
- package/dist/ps-slash.js +12 -1439
- package/dist/pwsh.d.ts +0 -1
- package/dist/py-parser-IS4HKYMF.js +16 -0
- package/dist/read.js +13 -9816
- package/dist/replace.js +15 -10014
- package/dist/rs-parser-XHUUO2KZ.js +81 -0
- package/dist/scaffold.js +4 -253
- package/dist/search.js +5 -585
- package/dist/session-kanban-sync.d.ts +0 -6
- package/dist/session-kanban.js +30 -972
- package/dist/skill.js +2 -215
- package/dist/spawn-background.d.ts +1 -1
- package/dist/task.js +5 -3410
- package/dist/test.js +10 -3773
- package/dist/todo.js +4 -2891
- package/dist/tool-help.js +3 -123
- package/dist/tool-search.js +3 -79
- package/dist/tool-tier.js +68 -33984
- package/dist/tool-use.js +2 -93
- package/dist/tree-sitter-parser-AIERW3YI.js +15 -0
- package/dist/tree.js +4 -288
- package/dist/ts-parser-RCTYKSVI.js +232 -0
- package/dist/typecheck.js +10 -3753
- package/dist/win32.js +6 -60
- package/dist/write.js +14 -9882
- package/dist/yaml-parser-4SAXV3PL.js +157 -0
- package/package.json +5 -5
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/codebase-index/gitignore.ts
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { compileGlob } from "@wrongstack/core/utils";
|
|
5
|
+
function globBody(glob) {
|
|
6
|
+
return compileGlob(glob).source.replace(/^\^/, "").replace(/\$$/, "");
|
|
7
|
+
}
|
|
8
|
+
function compileGitignore(lines) {
|
|
9
|
+
const rules = [];
|
|
10
|
+
for (const raw of lines) {
|
|
11
|
+
let line = raw.replace(/\r$/, "");
|
|
12
|
+
if (!line.trim() || line.trimStart().startsWith("#")) continue;
|
|
13
|
+
line = line.trim();
|
|
14
|
+
let negated = false;
|
|
15
|
+
if (line.startsWith("!")) {
|
|
16
|
+
negated = true;
|
|
17
|
+
line = line.slice(1);
|
|
18
|
+
}
|
|
19
|
+
let dirOnly = false;
|
|
20
|
+
if (line.endsWith("/")) {
|
|
21
|
+
dirOnly = true;
|
|
22
|
+
line = line.slice(0, -1);
|
|
23
|
+
}
|
|
24
|
+
if (!line) continue;
|
|
25
|
+
const anchored = line.startsWith("/") || line.includes("/");
|
|
26
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
27
|
+
const body = globBody(line);
|
|
28
|
+
const prefix = anchored ? "^" : "(?:^|.*/)";
|
|
29
|
+
rules.push({
|
|
30
|
+
eqOrUnder: new RegExp(`${prefix}${body}(?:/.*)?$`),
|
|
31
|
+
under: new RegExp(`${prefix}${body}/.*$`),
|
|
32
|
+
negated,
|
|
33
|
+
dirOnly
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return (relPath, isDir) => {
|
|
37
|
+
const p = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
38
|
+
let ignored = false;
|
|
39
|
+
for (const r of rules) {
|
|
40
|
+
const re = r.dirOnly && !isDir ? r.under : r.eqOrUnder;
|
|
41
|
+
if (re.test(p)) ignored = !r.negated;
|
|
42
|
+
}
|
|
43
|
+
return ignored;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function loadGitignoreMatcher(projectRoot) {
|
|
47
|
+
let lines = [];
|
|
48
|
+
try {
|
|
49
|
+
const raw = await fs.readFile(path.join(projectRoot, ".gitignore"), "utf8");
|
|
50
|
+
lines = raw.split("\n");
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
return compileGitignore(lines);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
loadGitignoreMatcher
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=chunk-LIJQMYYX.js.map
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildWin32CmdShimInvocation,
|
|
3
|
+
resolveWin32Command
|
|
4
|
+
} from "./chunk-V43EQ6VR.js";
|
|
5
|
+
import {
|
|
6
|
+
COMMAND_OUTPUT_MAX_BYTES,
|
|
7
|
+
detectPackageManager,
|
|
8
|
+
normalizeCommandOutput,
|
|
9
|
+
safeResolveReal
|
|
10
|
+
} from "./chunk-D33S4RQG.js";
|
|
11
|
+
|
|
12
|
+
// src/outdated.ts
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
15
|
+
var outdatedTool = {
|
|
16
|
+
name: "outdated",
|
|
17
|
+
category: "Package Management",
|
|
18
|
+
description: "Check for outdated dependencies in the project. Reports current, wanted (semver range), and latest versions available.",
|
|
19
|
+
usageHint: "MAINTENANCE & SECURITY TOOL:\n\n- Run periodically or before dependency-related work.\n- Helps surface packages that may need updates for security or features.\n- Hits the package registry over HTTP, so it is NOT purely local \u2014 flagged as mutating for the confirmation gate.\nUse the output to decide on upgrades. Prefer this over manual shell commands for dependency hygiene.",
|
|
20
|
+
permission: "confirm",
|
|
21
|
+
icon: "package",
|
|
22
|
+
// Network side-effecting (registry HTTP). Pairs with `mutating: true`
|
|
23
|
+
// so the H7 invariant test (`no auto-permission tool declares
|
|
24
|
+
// mutating: true`) passes — a tool claiming `'auto'` must be purely
|
|
25
|
+
// read-only, but `outdated` makes outbound HTTP calls to the
|
|
26
|
+
// registry. The 'confirm' permission routes the call through the
|
|
27
|
+
// tool.confirm_needed flow on every invocation. M-1 originally
|
|
28
|
+
// fixed four sibling tools (mcp_control, shellcheck, shellcheck (scan mode),
|
|
29
|
+
// search) but missed this one; applying the same contract here.
|
|
30
|
+
// WS-046: gives permission decisions something to key on.
|
|
31
|
+
// The working directory scanned.
|
|
32
|
+
subjectKey: "cwd",
|
|
33
|
+
mutating: true,
|
|
34
|
+
// Capability is outbound network — the tool only hits the package
|
|
35
|
+
// registry over HTTP, never touches the filesystem or runs shell.
|
|
36
|
+
// Use the canonical `net.outbound` capability (not the non-existent
|
|
37
|
+
// `network` string) so the subagent allowlist recognises it and
|
|
38
|
+
// permits read-only registry lookups under a director.
|
|
39
|
+
// The H7 invariant test requires this array to be non-empty for
|
|
40
|
+
// any mutating:true tool (meta-tools whitelisted). See
|
|
41
|
+
// tests/permission-mutating-invariant.test.ts:92.
|
|
42
|
+
capabilities: ["net.outbound"],
|
|
43
|
+
timeoutMs: 6e4,
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {
|
|
47
|
+
cwd: { type: "string", description: "Working directory (default: cwd)" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async execute(input, ctx, opts) {
|
|
51
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
52
|
+
const manager = await detectPackageManager(cwd, ctx.projectRoot);
|
|
53
|
+
if (manager === "npm") {
|
|
54
|
+
try {
|
|
55
|
+
const { detectNonJsEcosystem } = await import("./legacy-bridge-7M6QLUQN.js");
|
|
56
|
+
const nonJs = await detectNonJsEcosystem(cwd, ctx.projectRoot);
|
|
57
|
+
if (nonJs) {
|
|
58
|
+
const { planLanguageOperation, executePackagePlan } = await import("./languages/index.js");
|
|
59
|
+
const planResult = await planLanguageOperation({
|
|
60
|
+
projectRoot: ctx.projectRoot,
|
|
61
|
+
cwd,
|
|
62
|
+
operation: "package-outdated",
|
|
63
|
+
language: nonJs,
|
|
64
|
+
signal: opts.signal
|
|
65
|
+
});
|
|
66
|
+
if (planResult.status === "planned") {
|
|
67
|
+
const runner = executePackagePlan({
|
|
68
|
+
projectRoot: ctx.projectRoot,
|
|
69
|
+
workspace: planResult.workspace,
|
|
70
|
+
plan: planResult.plan,
|
|
71
|
+
packages: [],
|
|
72
|
+
signal: opts.signal
|
|
73
|
+
});
|
|
74
|
+
let outcome = null;
|
|
75
|
+
for (; ; ) {
|
|
76
|
+
const next = await runner.next();
|
|
77
|
+
if (next.done) {
|
|
78
|
+
outcome = next.value;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (outcome) {
|
|
83
|
+
const packages = outcome.outdated.map((o) => ({
|
|
84
|
+
name: o.name,
|
|
85
|
+
current: o.previous ?? "unknown",
|
|
86
|
+
latest: o.resolved ?? "unknown",
|
|
87
|
+
wanted: o.requested ?? o.resolved ?? "unknown",
|
|
88
|
+
type: "unknown",
|
|
89
|
+
location: o.name
|
|
90
|
+
}));
|
|
91
|
+
return {
|
|
92
|
+
exit_code: outcome.run?.exitCode ?? 0,
|
|
93
|
+
packages,
|
|
94
|
+
total: packages.length,
|
|
95
|
+
output: outcome.run?.output ?? (packages.length === 0 ? "All packages up to date" : ""),
|
|
96
|
+
truncated: outcome.run?.truncated ?? false
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const args = ["outdated", "--json"];
|
|
105
|
+
return runOutdated(manager, args, cwd, opts.signal);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
function runOutdated(manager, args, cwd, signal) {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
let stdout = "";
|
|
111
|
+
let stderr = "";
|
|
112
|
+
const MAX = 1e5;
|
|
113
|
+
const resolved = resolveWin32Command(manager);
|
|
114
|
+
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
115
|
+
const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
|
|
116
|
+
const spawnCmd = shim?.command ?? resolved;
|
|
117
|
+
const spawnArgs = shim?.args ?? args;
|
|
118
|
+
const child = spawn(spawnCmd, spawnArgs, {
|
|
119
|
+
cwd,
|
|
120
|
+
signal,
|
|
121
|
+
env: buildChildEnv(),
|
|
122
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
123
|
+
windowsHide: true,
|
|
124
|
+
...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
|
|
125
|
+
});
|
|
126
|
+
child.stdout?.on("data", (c) => {
|
|
127
|
+
if (stdout.length < MAX) stdout += c.toString();
|
|
128
|
+
});
|
|
129
|
+
child.stderr?.on("data", (c) => {
|
|
130
|
+
if (stderr.length < MAX) stderr += c.toString();
|
|
131
|
+
});
|
|
132
|
+
child.on("close", (code) => {
|
|
133
|
+
const result = parseOutdatedOutput(stdout, code ?? 0);
|
|
134
|
+
resolve(result);
|
|
135
|
+
});
|
|
136
|
+
child.on("error", (e) => {
|
|
137
|
+
resolve({
|
|
138
|
+
exit_code: 1,
|
|
139
|
+
packages: [],
|
|
140
|
+
total: 0,
|
|
141
|
+
output: e.message,
|
|
142
|
+
truncated: false
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function parseOutdatedOutput(json, exitCode) {
|
|
148
|
+
const packages = [];
|
|
149
|
+
if (!json) {
|
|
150
|
+
return {
|
|
151
|
+
exit_code: exitCode,
|
|
152
|
+
packages: [],
|
|
153
|
+
total: 0,
|
|
154
|
+
output: exitCode === 0 ? "All packages up to date" : "Could not check outdated packages",
|
|
155
|
+
truncated: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const truncated = json.length >= 1e5 || Buffer.byteLength(json, "utf8") > COMMAND_OUTPUT_MAX_BYTES;
|
|
159
|
+
let parsedOk = false;
|
|
160
|
+
try {
|
|
161
|
+
const data = JSON.parse(json);
|
|
162
|
+
parsedOk = true;
|
|
163
|
+
for (const name of Object.keys(data)) {
|
|
164
|
+
const info = data[name] ?? {};
|
|
165
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
166
|
+
packages.push({
|
|
167
|
+
name,
|
|
168
|
+
current: str(info["current"]) ?? "unknown",
|
|
169
|
+
latest: str(info["latest"]) ?? "unknown",
|
|
170
|
+
wanted: str(info["wanted"]) ?? "unknown",
|
|
171
|
+
// npm calls it `type`; pnpm calls it `dependencyType`.
|
|
172
|
+
type: str(info["type"]) ?? str(info["dependencyType"]) ?? "unknown",
|
|
173
|
+
location: str(info["location"]) ?? name
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
const outdatedFound = parsedOk && exitCode === 1;
|
|
179
|
+
let output = normalizeCommandOutput(json);
|
|
180
|
+
if (outdatedFound) {
|
|
181
|
+
output = `${output}
|
|
182
|
+
|
|
183
|
+
Note: exit code 1 from \`outdated\` means outdated packages were found (expected); treated as success.`;
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
exit_code: outdatedFound ? 0 : exitCode,
|
|
187
|
+
packages,
|
|
188
|
+
total: packages.length,
|
|
189
|
+
output,
|
|
190
|
+
truncated
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
outdatedTool
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=chunk-LWKUTMD5.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
builtinToolsPack
|
|
3
|
+
} from "./chunk-I64BS3VC.js";
|
|
4
|
+
import {
|
|
5
|
+
TIER1_TOOLS,
|
|
6
|
+
TIER2_TOOLS
|
|
7
|
+
} from "./chunk-VXECAJJ7.js";
|
|
8
|
+
|
|
9
|
+
// src/tool-tier.ts
|
|
10
|
+
function toolNameSet(tools) {
|
|
11
|
+
return new Set(tools.flatMap((tool) => tool ? [tool.name] : []));
|
|
12
|
+
}
|
|
13
|
+
var BUILTIN_TIER_COUNTS = {
|
|
14
|
+
off: builtinToolsPack.tools?.length ?? 0,
|
|
15
|
+
minimal: TIER1_TOOLS.length,
|
|
16
|
+
light: TIER1_TOOLS.length,
|
|
17
|
+
medium: TIER1_TOOLS.length + TIER2_TOOLS.length,
|
|
18
|
+
aggressive: TIER1_TOOLS.length
|
|
19
|
+
};
|
|
20
|
+
function selectBuiltinToolsForTier(tier, allTools) {
|
|
21
|
+
switch (tier) {
|
|
22
|
+
case "off":
|
|
23
|
+
return [...allTools];
|
|
24
|
+
case "minimal":
|
|
25
|
+
case "light": {
|
|
26
|
+
const tier1Names = toolNameSet(TIER1_TOOLS);
|
|
27
|
+
return allTools.filter((tool) => tier1Names.has(tool.name));
|
|
28
|
+
}
|
|
29
|
+
case "medium": {
|
|
30
|
+
const tier1Names = toolNameSet(TIER1_TOOLS);
|
|
31
|
+
const tier2Names = toolNameSet(TIER2_TOOLS);
|
|
32
|
+
return allTools.filter((tool) => tier1Names.has(tool.name) || tier2Names.has(tool.name));
|
|
33
|
+
}
|
|
34
|
+
case "aggressive": {
|
|
35
|
+
const tier1Names = toolNameSet(TIER1_TOOLS);
|
|
36
|
+
return allTools.filter((tool) => tier1Names.has(tool.name));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function registerBuiltinToolTier(options) {
|
|
41
|
+
const tools = selectBuiltinToolsForTier(
|
|
42
|
+
options.tier,
|
|
43
|
+
options.tools ?? builtinToolsPack.tools ?? []
|
|
44
|
+
);
|
|
45
|
+
options.registry.registerAllOrThrow(tools, options.owner ?? builtinToolsPack.name);
|
|
46
|
+
return tools;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
BUILTIN_TIER_COUNTS,
|
|
51
|
+
selectBuiltinToolsForTier,
|
|
52
|
+
registerBuiltinToolTier
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=chunk-MQX2KXVW.js.map
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compileUserRegex
|
|
3
|
+
} from "./chunk-Q5JPPCZC.js";
|
|
4
|
+
import {
|
|
5
|
+
enqueueReindex
|
|
6
|
+
} from "./chunk-HYX2TZ7O.js";
|
|
7
|
+
import {
|
|
8
|
+
isBinaryBuffer,
|
|
9
|
+
safeResolveReal,
|
|
10
|
+
sha256hex,
|
|
11
|
+
truncateDiffPayload
|
|
12
|
+
} from "./chunk-D33S4RQG.js";
|
|
13
|
+
|
|
14
|
+
// src/replace.ts
|
|
15
|
+
import { spawn } from "node:child_process";
|
|
16
|
+
import * as fs from "node:fs/promises";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
19
|
+
import {
|
|
20
|
+
atomicWrite,
|
|
21
|
+
buildChildEnv,
|
|
22
|
+
compileGlob,
|
|
23
|
+
detectNewlineStyle,
|
|
24
|
+
expectDefined,
|
|
25
|
+
normalizeToLf,
|
|
26
|
+
toStyle,
|
|
27
|
+
unifiedDiff
|
|
28
|
+
} from "@wrongstack/core/utils";
|
|
29
|
+
var MAX_DIFF_BYTES = 262144;
|
|
30
|
+
var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
|
|
31
|
+
var replaceTool = {
|
|
32
|
+
name: "replace",
|
|
33
|
+
category: "Transform",
|
|
34
|
+
description: "Perform a search-and-replace across multiple files using a regex pattern. This is a powerful bulk transformation tool. Dry-run is ON by default \u2014 set `dry_run: false` to apply changes.",
|
|
35
|
+
usageHint: "DANGEROUS IF USED CARELESSLY \u2014 review the diff output carefully.\n\nRecommended workflow:\n1. Run without `dry_run: false` first to see exactly what would change (dry-run is the default).\n2. Review the diff output, then re-run with `dry_run: false` to apply.\n3. Use a specific enough `pattern` (and `glob` / `files`) to avoid accidental broad changes.\n4. `replace_all` controls whether only the first match per file or all matches are replaced.\n5. `replacement` supports regex substitutions: `$1`\u2013`$9` insert capture groups, `$&` inserts the whole match, and `$$` inserts a literal dollar sign.\nThis tool is excellent for large-scale refactors (renaming, import updates, etc.) but must be used with caution.",
|
|
36
|
+
permission: "confirm",
|
|
37
|
+
// WS-046: gives permission decisions something to key on.
|
|
38
|
+
// The file scope being rewritten, not the pattern: a trust rule should say
|
|
39
|
+
// where a bulk replace may run.
|
|
40
|
+
subjectKey: "files",
|
|
41
|
+
mutating: true,
|
|
42
|
+
capabilities: ["fs.write"],
|
|
43
|
+
icon: "edit",
|
|
44
|
+
timeoutMs: 3e4,
|
|
45
|
+
maxOutputBytes: 262144,
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
pattern: { type: "string", description: "Regex pattern to match" },
|
|
50
|
+
replacement: {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "Replacement string. Supports `$1`\u2013`$9` (capture groups), `$&` (whole match), and `$$` (literal dollar sign) \u2014 same semantics as JavaScript String.replace."
|
|
53
|
+
},
|
|
54
|
+
files: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "File(s) to target: single path, comma-separated list, or glob pattern"
|
|
57
|
+
},
|
|
58
|
+
glob: { type: "string", description: 'Additional glob filter (e.g. "*.ts")' },
|
|
59
|
+
replace_all: {
|
|
60
|
+
type: "boolean",
|
|
61
|
+
description: "Replace all occurrences in each file (default: true)"
|
|
62
|
+
},
|
|
63
|
+
dry_run: { type: "boolean", description: "Preview changes without writing (default: true)" }
|
|
64
|
+
},
|
|
65
|
+
required: ["pattern", "replacement", "files"]
|
|
66
|
+
},
|
|
67
|
+
async execute(input, ctx) {
|
|
68
|
+
if (!input?.pattern) {
|
|
69
|
+
throw new ToolValidationError({
|
|
70
|
+
message: "replace: pattern is required",
|
|
71
|
+
field: "pattern"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (input.replacement === void 0) {
|
|
75
|
+
throw new ToolValidationError({
|
|
76
|
+
message: "replace: replacement is required",
|
|
77
|
+
field: "replacement"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (!input?.files) {
|
|
81
|
+
throw new ToolValidationError({
|
|
82
|
+
message: "replace: files is required",
|
|
83
|
+
field: "files"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const replaceAll = input.replace_all ?? true;
|
|
87
|
+
const compiled = compileUserRegex(input.pattern, "g");
|
|
88
|
+
if (!compiled.ok) {
|
|
89
|
+
throw new ToolValidationError({
|
|
90
|
+
message: `replace: ${compiled.reason}`,
|
|
91
|
+
field: "pattern"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const re = compiled.regex;
|
|
95
|
+
const globRe = input.glob ? compileGlob(input.glob) : null;
|
|
96
|
+
const dryRun = input.dry_run ?? true;
|
|
97
|
+
const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
|
|
98
|
+
const fileList = await resolveFiles(filesInput, ctx, globRe);
|
|
99
|
+
const realRoot = await fs.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
100
|
+
const results = [];
|
|
101
|
+
let totalReplacements = 0;
|
|
102
|
+
let diffBytesUsed = 0;
|
|
103
|
+
let diffsOmitted = 0;
|
|
104
|
+
let diffsTruncated = 0;
|
|
105
|
+
for (const absPath of fileList) {
|
|
106
|
+
const lstat2 = await fs.lstat(absPath).catch((err) => {
|
|
107
|
+
if (err.code === "ENOENT") return null;
|
|
108
|
+
throw err;
|
|
109
|
+
});
|
|
110
|
+
if (!lstat2?.isFile()) continue;
|
|
111
|
+
if (lstat2.isSymbolicLink()) continue;
|
|
112
|
+
let realPath;
|
|
113
|
+
try {
|
|
114
|
+
realPath = await fs.realpath(absPath);
|
|
115
|
+
} catch {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const rel = path.relative(realRoot, realPath);
|
|
119
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
120
|
+
const stat2 = await fs.stat(realPath).catch(() => null);
|
|
121
|
+
if (!stat2?.isFile()) continue;
|
|
122
|
+
let content;
|
|
123
|
+
try {
|
|
124
|
+
const buf = await fs.readFile(realPath);
|
|
125
|
+
if (isBinaryBuffer(buf)) continue;
|
|
126
|
+
content = buf.toString("utf8");
|
|
127
|
+
} catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const style = detectNewlineStyle(content);
|
|
131
|
+
const contentLf = normalizeToLf(content);
|
|
132
|
+
re.lastIndex = 0;
|
|
133
|
+
const allMatches = [...contentLf.matchAll(re)];
|
|
134
|
+
if (allMatches.length === 0) continue;
|
|
135
|
+
const matches = replaceAll ? allMatches : allMatches.slice(0, 1);
|
|
136
|
+
const count = matches.length;
|
|
137
|
+
let newContentLf = contentLf;
|
|
138
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
139
|
+
const m = expectDefined(matches[i]);
|
|
140
|
+
newContentLf = newContentLf.slice(0, m.index) + expandReplacement(input.replacement, m) + newContentLf.slice(expectDefined(m.index) + m[0].length);
|
|
141
|
+
}
|
|
142
|
+
re.lastIndex = 0;
|
|
143
|
+
totalReplacements += count;
|
|
144
|
+
if (!dryRun) {
|
|
145
|
+
const newContent = toStyle(newContentLf, style);
|
|
146
|
+
await atomicWrite(realPath, newContent, { mode: stat2.mode & 511 });
|
|
147
|
+
const written = await fs.stat(realPath).catch(() => null);
|
|
148
|
+
if (written) {
|
|
149
|
+
ctx.recordRead?.(realPath, written.mtimeMs, "write", sha256hex(newContent));
|
|
150
|
+
}
|
|
151
|
+
ctx.session?.recordFileChange?.({
|
|
152
|
+
path: realPath,
|
|
153
|
+
action: "modified",
|
|
154
|
+
before: content,
|
|
155
|
+
after: newContent
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
let diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
159
|
+
fromFile: absPath,
|
|
160
|
+
toFile: absPath
|
|
161
|
+
}) : void 0;
|
|
162
|
+
if (diff !== void 0) {
|
|
163
|
+
const remaining = MAX_DIFF_BYTES - diffBytesUsed;
|
|
164
|
+
if (remaining <= 0) {
|
|
165
|
+
diff = void 0;
|
|
166
|
+
diffsOmitted++;
|
|
167
|
+
} else {
|
|
168
|
+
const capped = truncateDiffPayload(diff, remaining);
|
|
169
|
+
if (capped.truncated) diffsTruncated++;
|
|
170
|
+
diff = capped.text;
|
|
171
|
+
diffBytesUsed += Buffer.byteLength(diff, "utf8");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
results.push({
|
|
175
|
+
path: absPath,
|
|
176
|
+
replacements: matches.length,
|
|
177
|
+
diff
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (!dryRun && results.length > 0) {
|
|
181
|
+
try {
|
|
182
|
+
enqueueReindex({
|
|
183
|
+
projectRoot: ctx.projectRoot,
|
|
184
|
+
files: results.map((r) => r.path)
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const overBudget = diffsOmitted > 0 || diffsTruncated > 0;
|
|
190
|
+
return {
|
|
191
|
+
files_modified: results.length,
|
|
192
|
+
total_replacements: totalReplacements,
|
|
193
|
+
results,
|
|
194
|
+
dry_run: dryRun,
|
|
195
|
+
note: overBudget ? `Diff payload exceeded the 256 KiB output budget: ${diffsTruncated} diff(s) truncated, ${diffsOmitted} diff(s) omitted. Replacement counts are complete; use the read tool to inspect individual files.` : void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function expandReplacement(template, match) {
|
|
200
|
+
if (!template.includes("$")) return template;
|
|
201
|
+
let out = "";
|
|
202
|
+
for (let i = 0; i < template.length; i++) {
|
|
203
|
+
const ch = template[i];
|
|
204
|
+
if (ch !== "$") {
|
|
205
|
+
out += ch;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const next = template[i + 1];
|
|
209
|
+
if (next === "$") {
|
|
210
|
+
out += "$";
|
|
211
|
+
i++;
|
|
212
|
+
} else if (next === "&") {
|
|
213
|
+
out += match[0];
|
|
214
|
+
i++;
|
|
215
|
+
} else if (next !== void 0 && next >= "1" && next <= "9") {
|
|
216
|
+
const idx = next.charCodeAt(0) - 48;
|
|
217
|
+
if (idx < match.length) {
|
|
218
|
+
out += match[idx] ?? "";
|
|
219
|
+
i++;
|
|
220
|
+
} else {
|
|
221
|
+
out += "$";
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
out += "$";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
function passesExtraGlob(extraGlob, name, full) {
|
|
230
|
+
extraGlob.lastIndex = 0;
|
|
231
|
+
const nameMatch = extraGlob.test(name);
|
|
232
|
+
extraGlob.lastIndex = 0;
|
|
233
|
+
const fullMatch = extraGlob.test(full);
|
|
234
|
+
extraGlob.lastIndex = 0;
|
|
235
|
+
return nameMatch || fullMatch;
|
|
236
|
+
}
|
|
237
|
+
async function resolveFiles(filesInput, ctx, extraGlob) {
|
|
238
|
+
const base = ctx.cwd;
|
|
239
|
+
const normalized = filesInput.trim();
|
|
240
|
+
if (normalized.startsWith("**/") || normalized.startsWith("*") || normalized.includes("**")) {
|
|
241
|
+
return await globFiles(normalized, base, extraGlob);
|
|
242
|
+
}
|
|
243
|
+
const parts = normalized.split(",").map((s) => s.trim()).filter(Boolean);
|
|
244
|
+
const resolved = [];
|
|
245
|
+
for (const p of parts) {
|
|
246
|
+
const absPath = await safeResolveReal(p, ctx);
|
|
247
|
+
if (extraGlob && !passesExtraGlob(extraGlob, path.basename(absPath), absPath)) continue;
|
|
248
|
+
const stat2 = await fs.stat(absPath).catch(() => null);
|
|
249
|
+
if (stat2?.isFile()) {
|
|
250
|
+
resolved.push(absPath);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return resolved;
|
|
254
|
+
}
|
|
255
|
+
async function globFiles(pattern, base, extraGlob) {
|
|
256
|
+
const rgAvailable = await checkRg();
|
|
257
|
+
if (rgAvailable) {
|
|
258
|
+
try {
|
|
259
|
+
const { promise } = spawnRgFind(pattern, base);
|
|
260
|
+
const files = await promise;
|
|
261
|
+
if (extraGlob) {
|
|
262
|
+
return files.filter((f) => passesExtraGlob(extraGlob, path.basename(f), f));
|
|
263
|
+
}
|
|
264
|
+
return files;
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return await globNative(pattern, base, extraGlob);
|
|
269
|
+
}
|
|
270
|
+
var rgAvailabilityCache;
|
|
271
|
+
function __resetRgDetectionForTests() {
|
|
272
|
+
rgAvailabilityCache = void 0;
|
|
273
|
+
}
|
|
274
|
+
function checkRg() {
|
|
275
|
+
rgAvailabilityCache ??= new Promise((resolve) => {
|
|
276
|
+
try {
|
|
277
|
+
const p = spawn("rg", ["--version"], {
|
|
278
|
+
env: buildChildEnv(),
|
|
279
|
+
stdio: "ignore",
|
|
280
|
+
windowsHide: true
|
|
281
|
+
});
|
|
282
|
+
p.on("error", () => resolve(false));
|
|
283
|
+
p.on("close", (code) => resolve(code === 0));
|
|
284
|
+
} catch {
|
|
285
|
+
resolve(false);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
return rgAvailabilityCache;
|
|
289
|
+
}
|
|
290
|
+
function spawnRgFind(pattern, base) {
|
|
291
|
+
const args = ["--files", "--glob", pattern, base];
|
|
292
|
+
const child = spawn("rg", args, {
|
|
293
|
+
signal: AbortSignal.timeout(3e4),
|
|
294
|
+
env: buildChildEnv(),
|
|
295
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
296
|
+
windowsHide: true
|
|
297
|
+
});
|
|
298
|
+
const MAX_BUF_CHARS = 8 * 1024 * 1024;
|
|
299
|
+
let buf = "";
|
|
300
|
+
let truncated = false;
|
|
301
|
+
child.stdout?.on("data", (chunk) => {
|
|
302
|
+
if (truncated) return;
|
|
303
|
+
buf += chunk.toString();
|
|
304
|
+
if (buf.length > MAX_BUF_CHARS) {
|
|
305
|
+
truncated = true;
|
|
306
|
+
buf = buf.slice(0, buf.lastIndexOf("\n") + 1);
|
|
307
|
+
child.kill();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
promise: new Promise((resolve, reject) => {
|
|
312
|
+
child.on("error", reject);
|
|
313
|
+
child.on("close", () => {
|
|
314
|
+
resolve(buf.split("\n").filter(Boolean));
|
|
315
|
+
});
|
|
316
|
+
})
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async function globNative(pattern, base, extraGlob) {
|
|
320
|
+
const results = [];
|
|
321
|
+
const globRe = compileGlob(pattern);
|
|
322
|
+
const walk = async (dir) => {
|
|
323
|
+
let entries;
|
|
324
|
+
try {
|
|
325
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
326
|
+
} catch {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
for (const e of entries) {
|
|
330
|
+
if (DEFAULT_IGNORE.includes(e.name)) continue;
|
|
331
|
+
const full = path.join(dir, e.name);
|
|
332
|
+
try {
|
|
333
|
+
const stat2 = await fs.lstat(full);
|
|
334
|
+
if (stat2.isSymbolicLink()) continue;
|
|
335
|
+
} catch {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (e.isDirectory()) {
|
|
339
|
+
await walk(full);
|
|
340
|
+
} else if (e.isFile()) {
|
|
341
|
+
const name = e.name;
|
|
342
|
+
if (globRe.test(name) || globRe.test(full)) {
|
|
343
|
+
if (extraGlob && !extraGlob.test(name) && !extraGlob.test(full)) continue;
|
|
344
|
+
results.push(full);
|
|
345
|
+
}
|
|
346
|
+
globRe.lastIndex = 0;
|
|
347
|
+
if (extraGlob) extraGlob.lastIndex = 0;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
await walk(base);
|
|
352
|
+
return results;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
replaceTool,
|
|
357
|
+
__resetRgDetectionForTests
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=chunk-MZZN7VCD.js.map
|