@wrongstack/tools 0.310.1 → 0.313.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/_shell-pick.d.ts +5 -10
- package/dist/audit.js +10 -4055
- package/dist/bash.js +9 -2332
- package/dist/batch-tool-use.js +2 -187
- package/dist/browser/index.js +25 -1339
- package/dist/builtin.js +69 -33985
- package/dist/chunk-2CTB3UK5.js +197 -0
- package/dist/chunk-2ME7ZWDD.js +1272 -0
- package/dist/chunk-2TFQDEPU.js +113 -0
- package/dist/chunk-2ZCJNKFE.js +315 -0
- package/dist/chunk-3PVYQFRQ.js +472 -0
- package/dist/chunk-3QMQBO5Z.js +89 -0
- package/dist/chunk-42AKKX7O.js +385 -0
- package/dist/chunk-42SKEETB.js +113 -0
- package/dist/chunk-4BHUSMNL.js +104 -0
- package/dist/chunk-4I4LWFOO.js +431 -0
- package/dist/chunk-553IQUMO.js +478 -0
- package/dist/chunk-55KF77OK.js +1944 -0
- package/dist/chunk-6HLZK37X.js +359 -0
- package/dist/chunk-6RNIFLG4.js +305 -0
- package/dist/chunk-A6BRWIXN.js +317 -0
- package/dist/chunk-AHHDWNXC.js +200 -0
- package/dist/chunk-B2CIOKWP.js +899 -0
- package/dist/chunk-BTSFTGIL.js +7 -0
- package/dist/chunk-D33S4RQG.js +222 -0
- package/dist/chunk-DIJKHSUO.js +134 -0
- package/dist/chunk-DPL47QGS.js +3515 -0
- package/dist/chunk-EMRGN4KM.js +170 -0
- package/dist/chunk-FGOABE4Z.js +955 -0
- package/dist/chunk-FW65TP4G.js +520 -0
- package/dist/chunk-GOIZNNOA.js +221 -0
- package/dist/chunk-H5MJUAA6.js +295 -0
- package/dist/chunk-H5OHH6F4.js +223 -0
- package/dist/chunk-HC5RM4WY.js +199 -0
- package/dist/chunk-HEQFMLZ7.js +190 -0
- package/dist/chunk-HFXSNKCV.js +196 -0
- package/dist/chunk-HXSBQ2SE.js +1723 -0
- package/dist/chunk-ILESNGNM.js +163 -0
- package/dist/chunk-INF5CGAO.js +137 -0
- package/dist/chunk-IWDFZ6XR.js +337 -0
- package/dist/chunk-IXMNTNW5.js +238 -0
- package/dist/chunk-J4SRJKBW.js +428 -0
- package/dist/chunk-J5SKD6LN.js +235 -0
- package/dist/chunk-JRJJX3L5.js +424 -0
- package/dist/chunk-KXNDHMUB.js +366 -0
- package/dist/chunk-LIJQMYYX.js +59 -0
- package/dist/chunk-MAN26PZ5.js +260 -0
- package/dist/chunk-MKRQMLZA.js +474 -0
- package/dist/chunk-NBDZMMG2.js +169 -0
- package/dist/chunk-NHA6BEIH.js +101 -0
- package/dist/chunk-OA6YJIWL.js +4412 -0
- package/dist/chunk-OZC255IC.js +2830 -0
- package/dist/chunk-PUR6WU2B.js +154 -0
- package/dist/chunk-Q5JPPCZC.js +12 -0
- package/dist/chunk-QBV725YG.js +128 -0
- package/dist/chunk-QHVW4AAM.js +892 -0
- package/dist/chunk-QNJ7AQ4B.js +1000 -0
- package/dist/chunk-RGISBZBD.js +231 -0
- package/dist/chunk-RJZQWEMS.js +84 -0
- package/dist/chunk-RP2ZA47V.js +15 -0
- package/dist/chunk-RXISBFR5.js +315 -0
- package/dist/chunk-S5CCLZL3.js +99 -0
- package/dist/chunk-TVTCSBJF.js +122 -0
- package/dist/chunk-U4D5S3VQ.js +21 -0
- package/dist/chunk-U4VAATKP.js +193 -0
- package/dist/chunk-UKU2ZX4C.js +203 -0
- package/dist/chunk-UNSKG6DC.js +159 -0
- package/dist/chunk-URFKEIHM.js +1091 -0
- package/dist/chunk-UTV7YAW7.js +178 -0
- package/dist/chunk-V43EQ6VR.js +68 -0
- package/dist/chunk-VB3MJGCV.js +610 -0
- package/dist/chunk-WHIIIREL.js +223 -0
- package/dist/chunk-XMIFYKUT.js +1371 -0
- package/dist/chunk-YF2RSV34.js +54 -0
- package/dist/chunk-Z6DAKQ3U.js +1 -0
- package/dist/chunk-ZIGAZFWE.js +522 -0
- package/dist/circuit-breaker.js +3 -191
- package/dist/codebase-index/index.js +84 -13688
- package/dist/codebase-index/parser-worker-script.js +8 -3995
- package/dist/codebase-index/project-server.js +60 -8548
- package/dist/codebase-index/worker.js +20 -8416
- 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-6CA77WZ4.js +469 -0
- package/dist/grep.js +7 -604
- package/dist/index.js +318 -35423
- package/dist/install.js +10 -4091
- package/dist/json-parser-7YMDJSBJ.js +213 -0
- package/dist/json.js +4 -665
- package/dist/kanban.js +3 -2096
- package/dist/languages/index.js +23 -4664
- package/dist/legacy-bridge-7UKZT46G.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 -33933
- package/dist/parser-dispatch-VPOFDO2U.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/py-parser-GPM5VYRG.js +16 -0
- package/dist/read.js +13 -9816
- package/dist/replace.js +15 -10014
- package/dist/rs-parser-WVQ4KYCX.js +81 -0
- package/dist/scaffold.js +4 -253
- package/dist/search.js +5 -585
- package/dist/session-kanban.js +30 -972
- package/dist/skill.js +2 -215
- 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 -34020
- package/dist/tool-use.js +2 -93
- package/dist/tree-sitter-parser-YCILHGZD.js +17 -0
- package/dist/tree.js +4 -288
- package/dist/ts-parser-YH4ZMWMH.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-GFAOKV53.js +157 -0
- package/package.json +5 -5
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryLegacyPackageOperation
|
|
3
|
+
} from "./chunk-NHA6BEIH.js";
|
|
4
|
+
import {
|
|
5
|
+
spawnStream
|
|
6
|
+
} from "./chunk-MAN26PZ5.js";
|
|
7
|
+
import {
|
|
8
|
+
detectPackageManager,
|
|
9
|
+
normalizeCommandOutput,
|
|
10
|
+
safeResolveReal
|
|
11
|
+
} from "./chunk-D33S4RQG.js";
|
|
12
|
+
|
|
13
|
+
// src/install.ts
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
detectEcosystem as detectPackageEcosystem,
|
|
17
|
+
recordPackageAction
|
|
18
|
+
} from "@wrongstack/core/coordination";
|
|
19
|
+
var installTool = {
|
|
20
|
+
name: "install",
|
|
21
|
+
category: "Package Management",
|
|
22
|
+
description: "Install, update or manage packages using the detected package manager (pnpm/npm/yarn). Strongly preferred over raw shell commands for dependency management because it is structured and safer.",
|
|
23
|
+
usageHint: "ALWAYS USE THIS INSTEAD OF BASH FOR PACKAGE WORK:\n\n- Empty `packages` \u2192 normal `install` (respects lockfile).\n- Provide names \u2192 adds/updates specific packages.\n- `dry_run: true` for safe preview.\n- Set `save` appropriately.\nThis tool has proper capability declaration and is heavily recommended in the security posture of the project.",
|
|
24
|
+
permission: "confirm",
|
|
25
|
+
// WS-046: gives permission decisions something to key on.
|
|
26
|
+
// What gets installed is the subject — installing a package runs its
|
|
27
|
+
// lifecycle scripts, so `express` and `evil-pkg` must not be interchangeable.
|
|
28
|
+
subjectKey: "packages",
|
|
29
|
+
mutating: true,
|
|
30
|
+
riskTier: "standard",
|
|
31
|
+
icon: "package",
|
|
32
|
+
timeoutMs: 12e4,
|
|
33
|
+
capabilities: ["package.install", "shell.restricted"],
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
packages: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Package(s) to install: single name, comma-separated list, or empty for all deps"
|
|
40
|
+
},
|
|
41
|
+
save: {
|
|
42
|
+
type: "string",
|
|
43
|
+
enum: ["dependency", "dev", "optional"],
|
|
44
|
+
description: 'Where to save the package(s): "dependency", "devDependencies", or "optionalDependencies".'
|
|
45
|
+
},
|
|
46
|
+
cwd: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Working directory for the install command (must stay inside project)."
|
|
49
|
+
},
|
|
50
|
+
dry_run: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
description: "If true, show what would be installed without actually modifying package.json or node_modules."
|
|
53
|
+
},
|
|
54
|
+
global: {
|
|
55
|
+
type: "boolean",
|
|
56
|
+
description: "Whether to perform a global install (use with caution)."
|
|
57
|
+
},
|
|
58
|
+
lifecycleScripts: {
|
|
59
|
+
type: "boolean",
|
|
60
|
+
description: "Opt in to running package lifecycle scripts (preinstall / install / postinstall / prepare / \u2026). Default: false \u2014 installs pass --ignore-scripts so a malicious package cannot execute arbitrary code at install time. Set true to opt back in to the legacy npm/pnpm/yarn default."
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
async execute(input, ctx, opts) {
|
|
65
|
+
let final;
|
|
66
|
+
const executeStream = installTool.executeStream;
|
|
67
|
+
if (!executeStream) throw new Error("installTool: stream execution unavailable");
|
|
68
|
+
for await (const ev of executeStream(input, ctx, opts)) {
|
|
69
|
+
if (ev.type === "final") final = ev.output;
|
|
70
|
+
}
|
|
71
|
+
if (!final) throw new Error("install: stream ended without final event");
|
|
72
|
+
return final;
|
|
73
|
+
},
|
|
74
|
+
async *executeStream(input, ctx, opts) {
|
|
75
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
76
|
+
if (!input.global) {
|
|
77
|
+
const pkgList2 = input.packages ? Array.isArray(input.packages) ? input.packages : input.packages.split(",").map((p) => p.trim()) : [];
|
|
78
|
+
const bridge = await tryLegacyPackageOperation(
|
|
79
|
+
"package-install",
|
|
80
|
+
{
|
|
81
|
+
cwd,
|
|
82
|
+
projectRoot: ctx.projectRoot,
|
|
83
|
+
signal: opts.signal
|
|
84
|
+
},
|
|
85
|
+
pkgList2
|
|
86
|
+
);
|
|
87
|
+
if (bridge?.outcome) {
|
|
88
|
+
const outcome = bridge.outcome;
|
|
89
|
+
const run = outcome.run;
|
|
90
|
+
yield {
|
|
91
|
+
type: "final",
|
|
92
|
+
output: {
|
|
93
|
+
exit_code: run?.exitCode ?? 0,
|
|
94
|
+
packages: [],
|
|
95
|
+
output: normalizeCommandOutput(
|
|
96
|
+
run?.output || outcome.manifestsChanged.join(", ") || "ok"
|
|
97
|
+
),
|
|
98
|
+
truncated: run?.truncated ?? false,
|
|
99
|
+
dry_run: input.dry_run ?? false
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const pkgManager = await detectPackageManager(cwd, ctx.projectRoot);
|
|
106
|
+
yield { type: "log", text: `Resolving with ${pkgManager}\u2026`, data: { phase: "resolve" } };
|
|
107
|
+
const globalFlag = input.global ? ["-g"] : [];
|
|
108
|
+
const ignoreScripts = input.lifecycleScripts !== true;
|
|
109
|
+
const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
|
|
110
|
+
(p) => p.trim()
|
|
111
|
+
) : [];
|
|
112
|
+
const PKG_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9^~><=*.+-]+)?$/i;
|
|
113
|
+
for (const pkg of pkgList) {
|
|
114
|
+
if (!PKG_NAME_RE.test(pkg) || pkg.startsWith("-") || pkg.length > 200) {
|
|
115
|
+
yield {
|
|
116
|
+
type: "final",
|
|
117
|
+
output: {
|
|
118
|
+
packages: pkgList,
|
|
119
|
+
exit_code: 1,
|
|
120
|
+
output: `Invalid package name "${pkg}". Names must match ${PKG_NAME_RE} and not start with "-".`,
|
|
121
|
+
dry_run: Boolean(input.dry_run),
|
|
122
|
+
truncated: false
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const hasPkgs = pkgList.length > 0;
|
|
129
|
+
const args = [];
|
|
130
|
+
if (input.dry_run) args.push("--dry-run");
|
|
131
|
+
if (ignoreScripts) args.push("--ignore-scripts");
|
|
132
|
+
if (pkgManager === "pnpm") {
|
|
133
|
+
if (hasPkgs) {
|
|
134
|
+
if (input.save === "dev") args.push("-D");
|
|
135
|
+
else if (input.save === "optional") args.push("-O");
|
|
136
|
+
args.push("add", ...globalFlag);
|
|
137
|
+
} else {
|
|
138
|
+
args.push("install", ...globalFlag);
|
|
139
|
+
}
|
|
140
|
+
} else if (pkgManager === "yarn") {
|
|
141
|
+
if (hasPkgs) {
|
|
142
|
+
args.push("add", ...globalFlag);
|
|
143
|
+
if (input.save === "dev") args.push("--dev");
|
|
144
|
+
else if (input.save === "optional") args.push("--optional");
|
|
145
|
+
} else {
|
|
146
|
+
args.push("install", ...globalFlag);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
args.push("install", ...globalFlag);
|
|
150
|
+
if (hasPkgs) {
|
|
151
|
+
if (input.save === "dev") args.push("--save-dev");
|
|
152
|
+
else if (input.save === "optional") args.push("--save-optional");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (hasPkgs) args.push(...pkgList);
|
|
156
|
+
yield {
|
|
157
|
+
type: "log",
|
|
158
|
+
text: `Fetching ${pkgList.length || "all"} packages\u2026`,
|
|
159
|
+
data: { phase: "fetch" }
|
|
160
|
+
};
|
|
161
|
+
const result = yield* spawnStream({
|
|
162
|
+
cmd: pkgManager,
|
|
163
|
+
args,
|
|
164
|
+
cwd,
|
|
165
|
+
signal: opts.signal,
|
|
166
|
+
maxBytes: 1e5
|
|
167
|
+
});
|
|
168
|
+
const output = {
|
|
169
|
+
packages: pkgList,
|
|
170
|
+
exit_code: result.exitCode,
|
|
171
|
+
output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
|
|
172
|
+
dry_run: args.includes("--dry-run"),
|
|
173
|
+
truncated: result.truncated
|
|
174
|
+
};
|
|
175
|
+
const isSuccess = result.exitCode === 0 && !output.dry_run && !input.global;
|
|
176
|
+
if (isSuccess && pkgList.length > 0) {
|
|
177
|
+
const trackerOpts = ctx.meta?.["packageTrackerOpts"];
|
|
178
|
+
if (trackerOpts) {
|
|
179
|
+
const manifestPath = resolveManifestPath(cwd, pkgManager);
|
|
180
|
+
for (const pkg of pkgList) {
|
|
181
|
+
try {
|
|
182
|
+
await recordPackageAction(trackerOpts, {
|
|
183
|
+
manifestPath,
|
|
184
|
+
packageName: pkg,
|
|
185
|
+
versionSpec: "latest",
|
|
186
|
+
// exact version resolved by package manager at install time
|
|
187
|
+
ecosystem: detectPackageEcosystem(manifestPath),
|
|
188
|
+
agentId: ctx.agentId,
|
|
189
|
+
agentName: ctx.agentName,
|
|
190
|
+
sessionId: ctx.session.id
|
|
191
|
+
});
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
ctx.recordSideEffect?.({
|
|
198
|
+
toolUseId: `install-${Date.now()}`,
|
|
199
|
+
toolName: "install",
|
|
200
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
201
|
+
input: { packages: pkgList, cwd, dry_run: Boolean(input.dry_run) },
|
|
202
|
+
outcome: output.dry_run ? "dry run" : result.exitCode === 0 ? `installed ${pkgList.length || "all"} packages` : `failed (exit ${result.exitCode})`,
|
|
203
|
+
risk: "package"
|
|
204
|
+
});
|
|
205
|
+
yield { type: "final", output };
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
function resolveManifestPath(cwd, pkgManager) {
|
|
209
|
+
switch (pkgManager) {
|
|
210
|
+
case "pnpm":
|
|
211
|
+
case "yarn":
|
|
212
|
+
case "npm":
|
|
213
|
+
return join(cwd, "package.json");
|
|
214
|
+
/* v8 ignore next 2 -- pkgManager is always pnpm/yarn/npm; the default is defensive. */
|
|
215
|
+
default:
|
|
216
|
+
return join(cwd, "package.json");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
installTool
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=chunk-H5OHH6F4.js.map
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryLegacyCodeOperation
|
|
3
|
+
} from "./chunk-NHA6BEIH.js";
|
|
4
|
+
import {
|
|
5
|
+
spawnStream
|
|
6
|
+
} from "./chunk-MAN26PZ5.js";
|
|
7
|
+
import {
|
|
8
|
+
normalizeCommandOutput,
|
|
9
|
+
safeResolveReal
|
|
10
|
+
} from "./chunk-D33S4RQG.js";
|
|
11
|
+
|
|
12
|
+
// src/test.ts
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
var testTool = {
|
|
15
|
+
name: "test",
|
|
16
|
+
category: "Code Quality",
|
|
17
|
+
description: "Execute the project's test suite. This is one of the most critical tools for validating that your changes are correct.",
|
|
18
|
+
usageHint: "ESSENTIAL BEFORE CONSIDERING WORK DONE:\n\n- Use `files` or `grep` to run only relevant tests during development.\n- `coverage: true` is useful when working on critical paths.\nRun tests frequently. A clean test run is usually required before the task can be considered complete.",
|
|
19
|
+
permission: "confirm",
|
|
20
|
+
mutating: false,
|
|
21
|
+
icon: "test",
|
|
22
|
+
timeoutMs: 12e4,
|
|
23
|
+
capabilities: ["shell.restricted"],
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
files: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: 'Test files: single path, comma-separated list, or glob (e.g. "**/*.test.ts")'
|
|
30
|
+
},
|
|
31
|
+
runner: {
|
|
32
|
+
type: "string",
|
|
33
|
+
enum: ["vitest", "jest", "mocha", "auto"],
|
|
34
|
+
description: "Test runner (default: auto-detect)"
|
|
35
|
+
},
|
|
36
|
+
watch: { type: "boolean", description: "Run in watch mode (default: false)" },
|
|
37
|
+
coverage: { type: "boolean", description: "Generate coverage report (default: false)" },
|
|
38
|
+
cwd: { type: "string", description: "Working directory (default: cwd)" },
|
|
39
|
+
grep: { type: "string", description: "Filter tests by name pattern (default: none)" },
|
|
40
|
+
timeout: { type: "integer", description: "Test timeout in ms (default: 30000)" },
|
|
41
|
+
verbose: {
|
|
42
|
+
type: "boolean",
|
|
43
|
+
description: "Per-test verbose reporter output (default: false \u2014 the summary reporter is used; full output is always saved to a log file referenced in the result)"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
async execute(input, ctx, opts) {
|
|
48
|
+
let final;
|
|
49
|
+
const executeStream = testTool.executeStream;
|
|
50
|
+
if (!executeStream) throw new Error("testTool: stream execution unavailable");
|
|
51
|
+
for await (const ev of executeStream(input, ctx, opts)) {
|
|
52
|
+
if (ev.type === "final") final = ev.output;
|
|
53
|
+
}
|
|
54
|
+
if (!final) throw new Error("test: stream ended without final event");
|
|
55
|
+
return final;
|
|
56
|
+
},
|
|
57
|
+
async *executeStream(input, ctx, opts) {
|
|
58
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
59
|
+
const runner = input.runner ?? "auto";
|
|
60
|
+
if (runner === "auto") {
|
|
61
|
+
const bridge = await tryLegacyCodeOperation("test", {
|
|
62
|
+
cwd,
|
|
63
|
+
projectRoot: ctx.projectRoot,
|
|
64
|
+
signal: opts.signal
|
|
65
|
+
});
|
|
66
|
+
if (bridge?.run) {
|
|
67
|
+
const run = bridge.run;
|
|
68
|
+
yield {
|
|
69
|
+
type: "final",
|
|
70
|
+
output: {
|
|
71
|
+
runner: bridge.language,
|
|
72
|
+
exit_code: run.exitCode ?? 0,
|
|
73
|
+
tests_run: (run.summary.passed ?? 0) + (run.summary.failed ?? 0),
|
|
74
|
+
passed: run.summary.passed ?? 0,
|
|
75
|
+
failed: run.summary.failed ?? 0,
|
|
76
|
+
duration_ms: run.durationMs,
|
|
77
|
+
output: normalizeCommandOutput(run.output || run.error || ""),
|
|
78
|
+
truncated: run.truncated
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const detected = runner === "auto" ? await detectRunner(cwd) : runner;
|
|
85
|
+
if (!detected) {
|
|
86
|
+
yield {
|
|
87
|
+
type: "final",
|
|
88
|
+
output: {
|
|
89
|
+
runner: "none",
|
|
90
|
+
exit_code: 0,
|
|
91
|
+
tests_run: 0,
|
|
92
|
+
passed: 0,
|
|
93
|
+
failed: 0,
|
|
94
|
+
duration_ms: 0,
|
|
95
|
+
output: "No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)",
|
|
96
|
+
truncated: false
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
yield { type: "log", text: `Running ${detected}\u2026`, data: { runner: detected } };
|
|
102
|
+
const start = Date.now();
|
|
103
|
+
const args = buildArgs(detected, input);
|
|
104
|
+
const result = yield* spawnStream({
|
|
105
|
+
cmd: detected,
|
|
106
|
+
args,
|
|
107
|
+
cwd,
|
|
108
|
+
signal: opts.signal,
|
|
109
|
+
maxBytes: 2e5
|
|
110
|
+
});
|
|
111
|
+
const duration = Date.now() - start;
|
|
112
|
+
yield { type: "final", output: parseResult(detected, result, duration) };
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
async function detectRunner(cwd) {
|
|
116
|
+
const { stat } = await import("node:fs/promises");
|
|
117
|
+
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
118
|
+
for (const f of candidates) {
|
|
119
|
+
try {
|
|
120
|
+
await stat(path.join(cwd, f));
|
|
121
|
+
if (f.includes("vitest")) return "vitest";
|
|
122
|
+
if (f.includes("jest")) return "jest";
|
|
123
|
+
if (f.includes("mocha")) return "mocha";
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
function buildArgs(runner, input) {
|
|
130
|
+
const args = [];
|
|
131
|
+
const timeout = input.timeout ?? 3e4;
|
|
132
|
+
switch (runner) {
|
|
133
|
+
case "vitest":
|
|
134
|
+
args.push(input.watch ? "watch" : "run");
|
|
135
|
+
if (input.verbose) args.push("--reporter=verbose");
|
|
136
|
+
if (input.coverage) args.push("--coverage");
|
|
137
|
+
if (input.grep) args.push("--testNamePattern", input.grep);
|
|
138
|
+
args.push("--testTimeout", String(timeout));
|
|
139
|
+
break;
|
|
140
|
+
case "jest":
|
|
141
|
+
if (input.verbose) args.push("--verbose");
|
|
142
|
+
if (input.watch) args.push("--watch");
|
|
143
|
+
if (input.coverage) args.push("--coverage");
|
|
144
|
+
if (input.grep) args.push("--testPathPattern", input.grep);
|
|
145
|
+
args.push("--testTimeout", String(timeout));
|
|
146
|
+
break;
|
|
147
|
+
case "mocha":
|
|
148
|
+
args.push("--reporter", "spec");
|
|
149
|
+
if (input.grep) args.push("--grep", input.grep);
|
|
150
|
+
args.push("--timeout", String(timeout));
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
if (input.files) {
|
|
154
|
+
const files = Array.isArray(input.files) ? input.files : input.files.split(",");
|
|
155
|
+
args.push("--", ...files.map((f) => f.trim()));
|
|
156
|
+
}
|
|
157
|
+
return args;
|
|
158
|
+
}
|
|
159
|
+
function parseResult(runner, result, duration) {
|
|
160
|
+
const out = result.stdout + result.stderr;
|
|
161
|
+
let tests_run = 0;
|
|
162
|
+
let passed = 0;
|
|
163
|
+
let failed = 0;
|
|
164
|
+
if (runner === "vitest") {
|
|
165
|
+
const passedMatch = out.match(/(\d+) passed/);
|
|
166
|
+
const failedMatch = out.match(/(\d+) failed/);
|
|
167
|
+
if (passedMatch?.[1]) passed = Number.parseInt(passedMatch[1], 10);
|
|
168
|
+
if (failedMatch?.[1]) failed = Number.parseInt(failedMatch[1], 10);
|
|
169
|
+
tests_run = passed + failed;
|
|
170
|
+
} else if (runner === "jest") {
|
|
171
|
+
const suitesMatch = out.match(/Test Suites:\s+(\d+)\s+total/);
|
|
172
|
+
const passedMatch = out.match(/Tests:\s+(\d+)\s+passed/);
|
|
173
|
+
const failedMatch = out.match(/Tests:\s+(\d+)\s+failed/);
|
|
174
|
+
tests_run = Number.parseInt(suitesMatch?.[1] ?? "0", 10);
|
|
175
|
+
passed = Number.parseInt(passedMatch?.[1] ?? "0", 10);
|
|
176
|
+
failed = Number.parseInt(failedMatch?.[1] ?? "0", 10);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
runner,
|
|
180
|
+
exit_code: result.exitCode,
|
|
181
|
+
tests_run,
|
|
182
|
+
passed,
|
|
183
|
+
failed,
|
|
184
|
+
duration_ms: duration,
|
|
185
|
+
// A passing run only needs the tail summary in chat history — counts are
|
|
186
|
+
// already parsed above and the FULL log is on disk (spool marker rides
|
|
187
|
+
// the stdout tail). Failures keep the standard command-output cap so
|
|
188
|
+
// the agent sees the failure details inline.
|
|
189
|
+
output: normalizeCommandOutput(result.stdout || result.error || "", {
|
|
190
|
+
maxBytes: result.exitCode === 0 ? 4096 : void 0
|
|
191
|
+
}),
|
|
192
|
+
truncated: result.truncated
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
testTool
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=chunk-HC5RM4WY.js.map
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryLegacyPackageOperation
|
|
3
|
+
} from "./chunk-NHA6BEIH.js";
|
|
4
|
+
import {
|
|
5
|
+
spawnStream
|
|
6
|
+
} from "./chunk-MAN26PZ5.js";
|
|
7
|
+
import {
|
|
8
|
+
COMMAND_OUTPUT_MAX_BYTES,
|
|
9
|
+
detectPackageManager,
|
|
10
|
+
normalizeCommandOutput,
|
|
11
|
+
safeResolveReal
|
|
12
|
+
} from "./chunk-D33S4RQG.js";
|
|
13
|
+
|
|
14
|
+
// src/audit.ts
|
|
15
|
+
var SEVERITY_RANK = {
|
|
16
|
+
info: 0,
|
|
17
|
+
low: 1,
|
|
18
|
+
moderate: 2,
|
|
19
|
+
high: 3,
|
|
20
|
+
critical: 4
|
|
21
|
+
};
|
|
22
|
+
var auditTool = {
|
|
23
|
+
name: "audit",
|
|
24
|
+
category: "Package Management",
|
|
25
|
+
description: "Run a security audit against project dependencies (using pnpm/npm audit). Reports known vulnerabilities with severity.",
|
|
26
|
+
usageHint: "CRITICAL SECURITY TOOL:\n\n- Run regularly and especially before any release.\n- Use `level` to focus on high/critical issues.\n- This tool is read-only: to remediate, use `install` (or `language_package`) to upgrade the affected packages.\nThis is one of the most important tools for supply chain security.",
|
|
27
|
+
permission: "confirm",
|
|
28
|
+
mutating: false,
|
|
29
|
+
capabilities: ["shell.restricted"],
|
|
30
|
+
icon: "package",
|
|
31
|
+
timeoutMs: 6e4,
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
cwd: { type: "string", description: "Working directory (default: cwd)" },
|
|
36
|
+
level: {
|
|
37
|
+
type: "string",
|
|
38
|
+
enum: ["low", "moderate", "high", "critical"],
|
|
39
|
+
description: "Minimum severity level to report"
|
|
40
|
+
},
|
|
41
|
+
fix: {
|
|
42
|
+
type: "boolean",
|
|
43
|
+
description: "Deprecated and rejected \u2014 this tool is read-only and never modifies dependencies. Use `install` (or `language_package`) to remediate vulnerabilities."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
async execute(input, ctx, opts) {
|
|
48
|
+
let final;
|
|
49
|
+
const executeStream = auditTool.executeStream;
|
|
50
|
+
if (!executeStream) throw new Error("auditTool: stream execution unavailable");
|
|
51
|
+
for await (const ev of executeStream(input, ctx, opts)) {
|
|
52
|
+
if (ev.type === "final") final = ev.output;
|
|
53
|
+
}
|
|
54
|
+
if (!final) throw new Error("audit: stream ended without final event");
|
|
55
|
+
return final;
|
|
56
|
+
},
|
|
57
|
+
async *executeStream(input, ctx, opts) {
|
|
58
|
+
if (input.fix === true) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"audit: `fix: true` is not supported \u2014 this tool is read-only (mutating: false). To remediate vulnerabilities, upgrade the affected packages with the `install` tool (or `language_package` for non-JS ecosystems)."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
64
|
+
const bridge = await tryLegacyPackageOperation("package-audit", {
|
|
65
|
+
cwd,
|
|
66
|
+
projectRoot: ctx.projectRoot,
|
|
67
|
+
signal: opts.signal
|
|
68
|
+
});
|
|
69
|
+
if (bridge?.outcome) {
|
|
70
|
+
const outcome = bridge.outcome;
|
|
71
|
+
const vulns = outcome.vulnerabilities.map((v) => ({
|
|
72
|
+
severity: v.severity,
|
|
73
|
+
package: v.package,
|
|
74
|
+
title: v.advisory ?? "Unknown vulnerability",
|
|
75
|
+
url: v.url ?? ""
|
|
76
|
+
}));
|
|
77
|
+
const total = vulns.length;
|
|
78
|
+
const summary = total === 0 ? "No vulnerabilities found" : `Found ${total} vulnerabilities: ${vulns.filter((a) => a.severity === "critical").length} critical, ${vulns.filter((a) => a.severity === "high").length} high`;
|
|
79
|
+
yield {
|
|
80
|
+
type: "final",
|
|
81
|
+
output: {
|
|
82
|
+
exit_code: outcome.run?.exitCode ?? 0,
|
|
83
|
+
vulnerabilities: vulns,
|
|
84
|
+
total,
|
|
85
|
+
summary,
|
|
86
|
+
output: outcome.run?.output ?? "",
|
|
87
|
+
truncated: outcome.run?.truncated ?? false
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const manager = await detectPackageManager(cwd, ctx.projectRoot);
|
|
93
|
+
yield { type: "log", text: `Auditing with ${manager}\u2026`, data: { manager } };
|
|
94
|
+
const args = ["audit", "--json"];
|
|
95
|
+
if (input.level && (manager === "npm" || manager === "pnpm")) {
|
|
96
|
+
args.push(`--audit-level=${input.level}`);
|
|
97
|
+
}
|
|
98
|
+
const result = yield* spawnStream({
|
|
99
|
+
cmd: manager,
|
|
100
|
+
args,
|
|
101
|
+
cwd,
|
|
102
|
+
signal: opts.signal,
|
|
103
|
+
maxBytes: 1e5
|
|
104
|
+
});
|
|
105
|
+
yield {
|
|
106
|
+
type: "final",
|
|
107
|
+
output: parseAuditOutput(result.stdout, result.exitCode, {
|
|
108
|
+
level: input.level,
|
|
109
|
+
spawnTruncated: result.truncated
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function parseAuditOutput(json, exitCode, opts = {}) {
|
|
115
|
+
if (!json) {
|
|
116
|
+
return {
|
|
117
|
+
exit_code: exitCode,
|
|
118
|
+
vulnerabilities: [],
|
|
119
|
+
total: 0,
|
|
120
|
+
summary: exitCode === 0 ? "No vulnerabilities found" : "Audit failed",
|
|
121
|
+
output: "",
|
|
122
|
+
truncated: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const cappedOutput = normalizeCommandOutput(json);
|
|
126
|
+
const truncated = opts.spawnTruncated === true || Buffer.byteLength(json, "utf8") > COMMAND_OUTPUT_MAX_BYTES;
|
|
127
|
+
try {
|
|
128
|
+
const data = JSON.parse(json);
|
|
129
|
+
let advisories = extractAdvisories(data);
|
|
130
|
+
const minRank = opts.level ? SEVERITY_RANK[opts.level] ?? 0 : 0;
|
|
131
|
+
if (minRank > 0) {
|
|
132
|
+
advisories = advisories.filter((a) => (SEVERITY_RANK[a.severity] ?? 0) >= minRank);
|
|
133
|
+
}
|
|
134
|
+
const total = advisories.length;
|
|
135
|
+
const summary = total === 0 ? "No vulnerabilities found" : `Found ${total} vulnerabilities: ${advisories.filter((a) => a.severity === "critical").length} critical, ${advisories.filter((a) => a.severity === "high").length} high`;
|
|
136
|
+
return {
|
|
137
|
+
exit_code: exitCode,
|
|
138
|
+
vulnerabilities: advisories,
|
|
139
|
+
total,
|
|
140
|
+
summary,
|
|
141
|
+
output: cappedOutput,
|
|
142
|
+
truncated
|
|
143
|
+
};
|
|
144
|
+
} catch {
|
|
145
|
+
return {
|
|
146
|
+
exit_code: exitCode,
|
|
147
|
+
vulnerabilities: [],
|
|
148
|
+
total: 0,
|
|
149
|
+
summary: "Could not parse audit output",
|
|
150
|
+
output: cappedOutput,
|
|
151
|
+
truncated
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function extractAdvisories(data) {
|
|
156
|
+
const advisories = [];
|
|
157
|
+
const ads = data["advisories"];
|
|
158
|
+
if (ads && typeof ads === "object") {
|
|
159
|
+
for (const [id, value] of Object.entries(ads)) {
|
|
160
|
+
const adv = value ?? {};
|
|
161
|
+
advisories.push({
|
|
162
|
+
severity: typeof adv["severity"] === "string" ? adv["severity"] : "unknown",
|
|
163
|
+
package: typeof adv["module_name"] === "string" ? adv["module_name"] : id,
|
|
164
|
+
title: typeof adv["title"] === "string" ? adv["title"] : "Unknown vulnerability",
|
|
165
|
+
url: typeof adv["url"] === "string" ? adv["url"] : ""
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return advisories;
|
|
169
|
+
}
|
|
170
|
+
const vulns = data["vulnerabilities"];
|
|
171
|
+
if (vulns && typeof vulns === "object") {
|
|
172
|
+
for (const [pkg, value] of Object.entries(vulns)) {
|
|
173
|
+
const vuln = value ?? {};
|
|
174
|
+
const via = Array.isArray(vuln["via"]) ? vuln["via"] : [];
|
|
175
|
+
const detail = via.find((v) => !!v && typeof v === "object");
|
|
176
|
+
advisories.push({
|
|
177
|
+
severity: typeof vuln["severity"] === "string" ? vuln["severity"] : "unknown",
|
|
178
|
+
package: pkg,
|
|
179
|
+
title: detail && typeof detail["title"] === "string" ? detail["title"] : "Unknown vulnerability",
|
|
180
|
+
url: detail && typeof detail["url"] === "string" ? detail["url"] : ""
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return advisories;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export {
|
|
188
|
+
auditTool
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=chunk-HEQFMLZ7.js.map
|