@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.
Files changed (135) hide show
  1. package/dist/_shell-pick.d.ts +5 -10
  2. package/dist/audit.js +10 -4055
  3. package/dist/bash.js +9 -2332
  4. package/dist/batch-tool-use.js +2 -187
  5. package/dist/browser/index.js +25 -1339
  6. package/dist/builtin.js +69 -33985
  7. package/dist/chunk-2CTB3UK5.js +197 -0
  8. package/dist/chunk-2ME7ZWDD.js +1272 -0
  9. package/dist/chunk-2TFQDEPU.js +113 -0
  10. package/dist/chunk-2ZCJNKFE.js +315 -0
  11. package/dist/chunk-3PVYQFRQ.js +472 -0
  12. package/dist/chunk-3QMQBO5Z.js +89 -0
  13. package/dist/chunk-42AKKX7O.js +385 -0
  14. package/dist/chunk-42SKEETB.js +113 -0
  15. package/dist/chunk-4BHUSMNL.js +104 -0
  16. package/dist/chunk-4I4LWFOO.js +431 -0
  17. package/dist/chunk-553IQUMO.js +478 -0
  18. package/dist/chunk-55KF77OK.js +1944 -0
  19. package/dist/chunk-6HLZK37X.js +359 -0
  20. package/dist/chunk-6RNIFLG4.js +305 -0
  21. package/dist/chunk-A6BRWIXN.js +317 -0
  22. package/dist/chunk-AHHDWNXC.js +200 -0
  23. package/dist/chunk-B2CIOKWP.js +899 -0
  24. package/dist/chunk-BTSFTGIL.js +7 -0
  25. package/dist/chunk-D33S4RQG.js +222 -0
  26. package/dist/chunk-DIJKHSUO.js +134 -0
  27. package/dist/chunk-DPL47QGS.js +3515 -0
  28. package/dist/chunk-EMRGN4KM.js +170 -0
  29. package/dist/chunk-FGOABE4Z.js +955 -0
  30. package/dist/chunk-FW65TP4G.js +520 -0
  31. package/dist/chunk-GOIZNNOA.js +221 -0
  32. package/dist/chunk-H5MJUAA6.js +295 -0
  33. package/dist/chunk-H5OHH6F4.js +223 -0
  34. package/dist/chunk-HC5RM4WY.js +199 -0
  35. package/dist/chunk-HEQFMLZ7.js +190 -0
  36. package/dist/chunk-HFXSNKCV.js +196 -0
  37. package/dist/chunk-HXSBQ2SE.js +1723 -0
  38. package/dist/chunk-ILESNGNM.js +163 -0
  39. package/dist/chunk-INF5CGAO.js +137 -0
  40. package/dist/chunk-IWDFZ6XR.js +337 -0
  41. package/dist/chunk-IXMNTNW5.js +238 -0
  42. package/dist/chunk-J4SRJKBW.js +428 -0
  43. package/dist/chunk-J5SKD6LN.js +235 -0
  44. package/dist/chunk-JRJJX3L5.js +424 -0
  45. package/dist/chunk-KXNDHMUB.js +366 -0
  46. package/dist/chunk-LIJQMYYX.js +59 -0
  47. package/dist/chunk-MAN26PZ5.js +260 -0
  48. package/dist/chunk-MKRQMLZA.js +474 -0
  49. package/dist/chunk-NBDZMMG2.js +169 -0
  50. package/dist/chunk-NHA6BEIH.js +101 -0
  51. package/dist/chunk-OA6YJIWL.js +4412 -0
  52. package/dist/chunk-OZC255IC.js +2830 -0
  53. package/dist/chunk-PUR6WU2B.js +154 -0
  54. package/dist/chunk-Q5JPPCZC.js +12 -0
  55. package/dist/chunk-QBV725YG.js +128 -0
  56. package/dist/chunk-QHVW4AAM.js +892 -0
  57. package/dist/chunk-QNJ7AQ4B.js +1000 -0
  58. package/dist/chunk-RGISBZBD.js +231 -0
  59. package/dist/chunk-RJZQWEMS.js +84 -0
  60. package/dist/chunk-RP2ZA47V.js +15 -0
  61. package/dist/chunk-RXISBFR5.js +315 -0
  62. package/dist/chunk-S5CCLZL3.js +99 -0
  63. package/dist/chunk-TVTCSBJF.js +122 -0
  64. package/dist/chunk-U4D5S3VQ.js +21 -0
  65. package/dist/chunk-U4VAATKP.js +193 -0
  66. package/dist/chunk-UKU2ZX4C.js +203 -0
  67. package/dist/chunk-UNSKG6DC.js +159 -0
  68. package/dist/chunk-URFKEIHM.js +1091 -0
  69. package/dist/chunk-UTV7YAW7.js +178 -0
  70. package/dist/chunk-V43EQ6VR.js +68 -0
  71. package/dist/chunk-VB3MJGCV.js +610 -0
  72. package/dist/chunk-WHIIIREL.js +223 -0
  73. package/dist/chunk-XMIFYKUT.js +1371 -0
  74. package/dist/chunk-YF2RSV34.js +54 -0
  75. package/dist/chunk-Z6DAKQ3U.js +1 -0
  76. package/dist/chunk-ZIGAZFWE.js +522 -0
  77. package/dist/circuit-breaker.js +3 -191
  78. package/dist/codebase-index/index.js +84 -13688
  79. package/dist/codebase-index/parser-worker-script.js +8 -3995
  80. package/dist/codebase-index/project-server.js +60 -8548
  81. package/dist/codebase-index/worker.js +20 -8416
  82. package/dist/design.js +2 -299
  83. package/dist/diff.js +4 -256
  84. package/dist/document.js +4 -222
  85. package/dist/e2e.js +5 -476
  86. package/dist/edit.js +14 -10200
  87. package/dist/exec.js +16 -3131
  88. package/dist/fetch.js +8 -398
  89. package/dist/format.js +10 -3750
  90. package/dist/generic-parser-VUXDIAOZ.js +13 -0
  91. package/dist/git.js +4 -380
  92. package/dist/glob.js +6 -280
  93. package/dist/go-parser-6CA77WZ4.js +469 -0
  94. package/dist/grep.js +7 -604
  95. package/dist/index.js +318 -35423
  96. package/dist/install.js +10 -4091
  97. package/dist/json-parser-7YMDJSBJ.js +213 -0
  98. package/dist/json.js +4 -665
  99. package/dist/kanban.js +3 -2096
  100. package/dist/languages/index.js +23 -4664
  101. package/dist/legacy-bridge-7UKZT46G.js +18 -0
  102. package/dist/lint.js +10 -3706
  103. package/dist/logs.js +4 -287
  104. package/dist/memory.js +6 -215
  105. package/dist/mode.js +3 -99
  106. package/dist/outdated.js +4 -5170
  107. package/dist/pack.js +65 -33933
  108. package/dist/parser-dispatch-VPOFDO2U.js +14 -0
  109. package/dist/patch.js +13 -9935
  110. package/dist/plan.js +5 -3286
  111. package/dist/process-registry.js +8 -651
  112. package/dist/ps-slash.js +12 -1439
  113. package/dist/py-parser-GPM5VYRG.js +16 -0
  114. package/dist/read.js +13 -9816
  115. package/dist/replace.js +15 -10014
  116. package/dist/rs-parser-WVQ4KYCX.js +81 -0
  117. package/dist/scaffold.js +4 -253
  118. package/dist/search.js +5 -585
  119. package/dist/session-kanban.js +30 -972
  120. package/dist/skill.js +2 -215
  121. package/dist/task.js +5 -3410
  122. package/dist/test.js +10 -3773
  123. package/dist/todo.js +4 -2891
  124. package/dist/tool-help.js +3 -123
  125. package/dist/tool-search.js +3 -79
  126. package/dist/tool-tier.js +68 -34020
  127. package/dist/tool-use.js +2 -93
  128. package/dist/tree-sitter-parser-YCILHGZD.js +17 -0
  129. package/dist/tree.js +4 -288
  130. package/dist/ts-parser-YH4ZMWMH.js +232 -0
  131. package/dist/typecheck.js +10 -3753
  132. package/dist/win32.js +6 -60
  133. package/dist/write.js +14 -9882
  134. package/dist/yaml-parser-GFAOKV53.js +157 -0
  135. 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