@wrongstack/tools 0.31.1 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +15 -15
- package/dist/audit.js.map +1 -1
- package/dist/bash.js.map +1 -1
- package/dist/builtin.js +105 -110
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.js +2 -1
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/diff.js +10 -9
- package/dist/diff.js.map +1 -1
- package/dist/document.js +4 -4
- package/dist/document.js.map +1 -1
- package/dist/edit.js.map +1 -1
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js.map +1 -1
- package/dist/format.js +1 -0
- package/dist/format.js.map +1 -1
- package/dist/git.js.map +1 -1
- package/dist/glob.js.map +1 -1
- package/dist/grep.js.map +1 -1
- package/dist/index.js +105 -110
- package/dist/index.js.map +1 -1
- package/dist/install.js +14 -14
- package/dist/install.js.map +1 -1
- package/dist/lint.js.map +1 -1
- package/dist/logs.js.map +1 -1
- package/dist/outdated.js +16 -16
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +105 -110
- package/dist/pack.js.map +1 -1
- package/dist/patch.js.map +1 -1
- package/dist/read.js.map +1 -1
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js.map +1 -1
- package/dist/test.js.map +1 -1
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.js.map +1 -1
- package/dist/write.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as fs4 from 'node:fs/promises';
|
|
2
|
-
import { stat } from 'node:fs/promises';
|
|
3
2
|
import * as path from 'node:path';
|
|
4
3
|
import { resolve, sep, dirname } from 'node:path';
|
|
5
4
|
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, stripAnsi, resolveWstackPaths } from '@wrongstack/core';
|
|
@@ -19,6 +18,20 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
19
18
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
20
19
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
21
20
|
});
|
|
21
|
+
async function detectPackageManager(cwd) {
|
|
22
|
+
const { stat: stat10 } = await import('node:fs/promises');
|
|
23
|
+
try {
|
|
24
|
+
await stat10(`${cwd}/pnpm-lock.yaml`);
|
|
25
|
+
return "pnpm";
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
await stat10(`${cwd}/yarn.lock`);
|
|
30
|
+
return "yarn";
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
return "npm";
|
|
34
|
+
}
|
|
22
35
|
function resolvePath(input, ctx) {
|
|
23
36
|
return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
|
|
24
37
|
}
|
|
@@ -182,9 +195,9 @@ var readTool = {
|
|
|
182
195
|
async execute(input, ctx) {
|
|
183
196
|
if (!input?.path) throw new Error("read: path is required");
|
|
184
197
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
185
|
-
let
|
|
198
|
+
let stat10;
|
|
186
199
|
try {
|
|
187
|
-
|
|
200
|
+
stat10 = await fs4.stat(absPath);
|
|
188
201
|
} catch (err) {
|
|
189
202
|
const code = err.code;
|
|
190
203
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
@@ -192,9 +205,9 @@ var readTool = {
|
|
|
192
205
|
`read: failed to stat "${input.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
193
206
|
);
|
|
194
207
|
}
|
|
195
|
-
if (!
|
|
196
|
-
if (
|
|
197
|
-
throw new Error(`read: file too large (${
|
|
208
|
+
if (!stat10.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
209
|
+
if (stat10.size > MAX_BYTES) {
|
|
210
|
+
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES})`);
|
|
198
211
|
}
|
|
199
212
|
const buf = await fs4.readFile(absPath);
|
|
200
213
|
if (isBinaryBuffer(buf)) {
|
|
@@ -206,14 +219,14 @@ var readTool = {
|
|
|
206
219
|
const offset = Math.max(1, input.offset ?? 1);
|
|
207
220
|
const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
|
|
208
221
|
if (limit === 0) {
|
|
209
|
-
ctx.recordRead(absPath,
|
|
222
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
210
223
|
return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
|
|
211
224
|
}
|
|
212
225
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
213
226
|
const truncated = offset - 1 + slice.length < total;
|
|
214
227
|
const width = String(offset + slice.length - 1).length;
|
|
215
228
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
216
|
-
ctx.recordRead(absPath,
|
|
229
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
217
230
|
return {
|
|
218
231
|
text: numbered,
|
|
219
232
|
total_lines: total,
|
|
@@ -252,12 +265,12 @@ var writeTool = {
|
|
|
252
265
|
let existed = false;
|
|
253
266
|
let prev = "";
|
|
254
267
|
try {
|
|
255
|
-
const
|
|
256
|
-
existed =
|
|
268
|
+
const stat11 = await fs4.stat(absPath);
|
|
269
|
+
existed = stat11.isFile();
|
|
257
270
|
if (existed) {
|
|
258
271
|
if (!ctx.hasRead(absPath)) {
|
|
259
272
|
prev = await fs4.readFile(absPath, "utf8");
|
|
260
|
-
ctx.recordRead(absPath,
|
|
273
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
261
274
|
} else {
|
|
262
275
|
prev = await fs4.readFile(absPath, "utf8");
|
|
263
276
|
}
|
|
@@ -270,8 +283,8 @@ var writeTool = {
|
|
|
270
283
|
await atomicWrite(absPath, input.content);
|
|
271
284
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
272
285
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
273
|
-
const
|
|
274
|
-
ctx.recordRead(absPath,
|
|
286
|
+
const stat10 = await fs4.stat(absPath);
|
|
287
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
275
288
|
ctx.session.recordFileChange({
|
|
276
289
|
path: absPath,
|
|
277
290
|
action: existed ? "modified" : "created",
|
|
@@ -311,13 +324,13 @@ var editTool = {
|
|
|
311
324
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
312
325
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
313
326
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
314
|
-
const
|
|
327
|
+
const stat10 = await fs4.stat(absPath).catch((err) => {
|
|
315
328
|
if (err.code === "ENOENT") {
|
|
316
329
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
317
330
|
}
|
|
318
331
|
throw err;
|
|
319
332
|
});
|
|
320
|
-
if (!
|
|
333
|
+
if (!stat10.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
|
|
321
334
|
if (!ctx.hasRead(absPath)) {
|
|
322
335
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
323
336
|
}
|
|
@@ -511,8 +524,8 @@ var replaceTool = {
|
|
|
511
524
|
}
|
|
512
525
|
const rel = path.relative(realRoot, realPath);
|
|
513
526
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
514
|
-
const
|
|
515
|
-
if (!
|
|
527
|
+
const stat10 = await fs4.stat(realPath).catch(() => null);
|
|
528
|
+
if (!stat10 || !stat10.isFile()) continue;
|
|
516
529
|
let content;
|
|
517
530
|
try {
|
|
518
531
|
const buf = await fs4.readFile(realPath);
|
|
@@ -537,7 +550,7 @@ var replaceTool = {
|
|
|
537
550
|
totalReplacements += count;
|
|
538
551
|
if (!dryRun) {
|
|
539
552
|
const newContent = toStyle(newContentLf, style);
|
|
540
|
-
await atomicWrite(realPath, newContent, { mode:
|
|
553
|
+
await atomicWrite(realPath, newContent, { mode: stat10.mode & 511 });
|
|
541
554
|
}
|
|
542
555
|
const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
543
556
|
fromFile: absPath,
|
|
@@ -567,8 +580,8 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
|
|
|
567
580
|
const resolved = [];
|
|
568
581
|
for (const p of parts) {
|
|
569
582
|
const absPath = safeResolve(p, ctx);
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
583
|
+
const stat10 = await fs4.stat(absPath).catch(() => null);
|
|
584
|
+
if (stat10?.isFile()) {
|
|
572
585
|
resolved.push(absPath);
|
|
573
586
|
}
|
|
574
587
|
}
|
|
@@ -626,8 +639,8 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
626
639
|
if (DEFAULT_IGNORE.includes(e.name)) continue;
|
|
627
640
|
const full = path.join(dir, e.name);
|
|
628
641
|
try {
|
|
629
|
-
const
|
|
630
|
-
if (
|
|
642
|
+
const stat10 = await fs4.lstat(full);
|
|
643
|
+
if (stat10.isSymbolicLink()) continue;
|
|
631
644
|
} catch {
|
|
632
645
|
continue;
|
|
633
646
|
}
|
|
@@ -976,8 +989,8 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
976
989
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
977
990
|
if (globRe) globRe.lastIndex = 0;
|
|
978
991
|
try {
|
|
979
|
-
const
|
|
980
|
-
if (
|
|
992
|
+
const stat10 = await fs4.stat(full);
|
|
993
|
+
if (stat10.size > 1e6) continue;
|
|
981
994
|
const head = await fs4.readFile(full);
|
|
982
995
|
if (isBinaryBuffer(head)) continue;
|
|
983
996
|
const text = head.toString("utf8");
|
|
@@ -2510,15 +2523,26 @@ var planTool = {
|
|
|
2510
2523
|
category: "Session",
|
|
2511
2524
|
description: "Manage a persistent strategic plan for the current session. Unlike todos, plans are meant for higher-level, multi-phase approaches and survive across conversation resumptions. Use this to outline big-picture work, then promote concrete items into the todo list when ready to execute.",
|
|
2512
2525
|
usageHint: 'RECOMMENDED FOR COMPLEX, MULTI-PHASE WORK:\n\n- Start by creating a high-level plan with `action: "add"` or using templates (`template_use`).\n- Use `promote` to turn a plan item into actionable todos.\n- Keep plans at the "why and what" level, and todos at the "how and next step" level.\n- Common templates: "new-feature", "bug-fix", "refactor", "release", "security-audit".\n\nThis tool is excellent for maintaining long-term direction across many turns or even multiple sessions.',
|
|
2513
|
-
permission: "
|
|
2514
|
-
mutating:
|
|
2526
|
+
permission: "confirm",
|
|
2527
|
+
mutating: true,
|
|
2528
|
+
capabilities: ["fs.write"],
|
|
2515
2529
|
timeoutMs: 2e3,
|
|
2516
2530
|
inputSchema: {
|
|
2517
2531
|
type: "object",
|
|
2518
2532
|
properties: {
|
|
2519
2533
|
action: {
|
|
2520
2534
|
type: "string",
|
|
2521
|
-
enum: [
|
|
2535
|
+
enum: [
|
|
2536
|
+
"show",
|
|
2537
|
+
"add",
|
|
2538
|
+
"start",
|
|
2539
|
+
"done",
|
|
2540
|
+
"remove",
|
|
2541
|
+
"promote",
|
|
2542
|
+
"derive",
|
|
2543
|
+
"template_use",
|
|
2544
|
+
"clear"
|
|
2545
|
+
],
|
|
2522
2546
|
description: "The operation to perform on the plan board."
|
|
2523
2547
|
},
|
|
2524
2548
|
title: {
|
|
@@ -2611,7 +2635,12 @@ var planTool = {
|
|
|
2611
2635
|
plan = derived.plan;
|
|
2612
2636
|
await savePlan(planPath, plan);
|
|
2613
2637
|
ctx.state.replaceTodos(derived.todos);
|
|
2614
|
-
return mkResult(
|
|
2638
|
+
return mkResult(
|
|
2639
|
+
plan,
|
|
2640
|
+
true,
|
|
2641
|
+
`${input.action} ok \u2014 ${derived.todos.length} todo(s) created.`,
|
|
2642
|
+
derived.todos
|
|
2643
|
+
);
|
|
2615
2644
|
}
|
|
2616
2645
|
case "template_use": {
|
|
2617
2646
|
const templateName = input.template?.trim();
|
|
@@ -2626,7 +2655,11 @@ var planTool = {
|
|
|
2626
2655
|
({ plan } = addPlanItem(plan, item.title, item.details));
|
|
2627
2656
|
}
|
|
2628
2657
|
await savePlan(planPath, plan);
|
|
2629
|
-
return mkResult(
|
|
2658
|
+
return mkResult(
|
|
2659
|
+
plan,
|
|
2660
|
+
true,
|
|
2661
|
+
`Applied template "${template.name}" \u2014 ${template.items.length} items added.`
|
|
2662
|
+
);
|
|
2630
2663
|
}
|
|
2631
2664
|
case "clear":
|
|
2632
2665
|
plan = clearPlan(plan);
|
|
@@ -2772,8 +2805,8 @@ function findGitDir(cwd, projectRoot) {
|
|
|
2772
2805
|
let dir = cwd;
|
|
2773
2806
|
for (let i = 0; i < 20; i++) {
|
|
2774
2807
|
try {
|
|
2775
|
-
const
|
|
2776
|
-
if (
|
|
2808
|
+
const stat10 = statSync(`${dir}/.git`);
|
|
2809
|
+
if (stat10.isDirectory() || stat10.isFile()) return dir;
|
|
2777
2810
|
} catch {
|
|
2778
2811
|
}
|
|
2779
2812
|
if (dir === root) break;
|
|
@@ -3127,8 +3160,8 @@ function toYaml(data, indent = 0) {
|
|
|
3127
3160
|
var diffTool = {
|
|
3128
3161
|
name: "diff",
|
|
3129
3162
|
category: "Filesystem",
|
|
3130
|
-
description: "Show
|
|
3131
|
-
usageHint: 'USE FOR CODE REVIEW AND CHANGE INSPECTION:\n\n- `files` + no `a`/`b` \u2192
|
|
3163
|
+
description: "Show file content with line numbers, staged/working-tree diffs via git, or commit/branch diffs. A safer and more structured alternative to raw `git diff` via shell.",
|
|
3164
|
+
usageHint: 'USE FOR CODE REVIEW AND CHANGE INSPECTION:\n\n- `files` + no `a`/`b` \u2192 show file content with line numbers (NOT a unified diff; no +/- prefixes).\n- `a` and/or `b` \u2192 git-style commit/branch diff (unified format, real +/- prefixes).\n- `staged: true` \u2192 only show staged changes.\n- `mode` can be "unified", "stat", or "side-by-side" (only affects the git-diff path).\n\nNOTE: For a true file-vs-file unified diff, supply `a` and `b` so the tool delegates to `git diff`. The `files`-only path is a line-numbered dump, not a diff.\n\nThis tool has important safety guards against flag injection (see previous security findings).',
|
|
3132
3165
|
permission: "auto",
|
|
3133
3166
|
mutating: false,
|
|
3134
3167
|
capabilities: ["fs.read"],
|
|
@@ -3205,8 +3238,8 @@ function findGitDir2(cwd) {
|
|
|
3205
3238
|
let dir = cwd;
|
|
3206
3239
|
for (let i = 0; i < 20; i++) {
|
|
3207
3240
|
try {
|
|
3208
|
-
const
|
|
3209
|
-
if (
|
|
3241
|
+
const stat10 = statSync(path.join(dir, ".git"));
|
|
3242
|
+
if (stat10.isDirectory()) return dir;
|
|
3210
3243
|
} catch {
|
|
3211
3244
|
}
|
|
3212
3245
|
const parent = path.dirname(dir);
|
|
@@ -3236,7 +3269,7 @@ function runGit2(args, cwd, signal) {
|
|
|
3236
3269
|
});
|
|
3237
3270
|
}
|
|
3238
3271
|
async function fileDiff(input, ctx, _signal) {
|
|
3239
|
-
input.context
|
|
3272
|
+
void input.context;
|
|
3240
3273
|
const files = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim()).filter(Boolean) : [];
|
|
3241
3274
|
if (files.length === 0) {
|
|
3242
3275
|
return {
|
|
@@ -3249,23 +3282,24 @@ async function fileDiff(input, ctx, _signal) {
|
|
|
3249
3282
|
const results = [];
|
|
3250
3283
|
for (const file of files) {
|
|
3251
3284
|
const absPath = safeResolve(file, ctx);
|
|
3252
|
-
const
|
|
3253
|
-
if (!
|
|
3285
|
+
const stat10 = await fs4.stat(absPath).catch(() => null);
|
|
3286
|
+
if (!stat10?.isFile()) continue;
|
|
3254
3287
|
const content = await fs4.readFile(absPath, "utf8");
|
|
3255
3288
|
const lines = content.split(/\r?\n/);
|
|
3256
|
-
results.push(
|
|
3257
|
-
+++ ${file}
|
|
3258
|
-
${formatUnified(lines)}`);
|
|
3289
|
+
results.push(formatWithLineNumbers(file, lines));
|
|
3259
3290
|
}
|
|
3260
3291
|
return {
|
|
3261
|
-
diff: results.join("\n"),
|
|
3292
|
+
diff: results.join("\n\n"),
|
|
3262
3293
|
files,
|
|
3263
3294
|
truncated: false,
|
|
3264
3295
|
mode: input.mode ?? "unified"
|
|
3265
3296
|
};
|
|
3266
3297
|
}
|
|
3267
|
-
function
|
|
3268
|
-
|
|
3298
|
+
function formatWithLineNumbers(file, lines) {
|
|
3299
|
+
const width = String(lines.length).length;
|
|
3300
|
+
const numbered = lines.map((line, i) => `${String(i + 1).padStart(width)} | ${line}`).join("\n");
|
|
3301
|
+
return `--- ${file} (line-numbered dump, not a unified diff) ---
|
|
3302
|
+
${numbered}`;
|
|
3269
3303
|
}
|
|
3270
3304
|
var DEFAULT_IGNORE4 = [
|
|
3271
3305
|
"node_modules",
|
|
@@ -3599,11 +3633,11 @@ var lintTool = {
|
|
|
3599
3633
|
}
|
|
3600
3634
|
};
|
|
3601
3635
|
async function detectLinter(cwd) {
|
|
3602
|
-
const { stat:
|
|
3636
|
+
const { stat: stat10 } = await import('node:fs/promises');
|
|
3603
3637
|
const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
|
|
3604
3638
|
for (const f of checks) {
|
|
3605
3639
|
try {
|
|
3606
|
-
await
|
|
3640
|
+
await stat10(`${cwd}/${f}`);
|
|
3607
3641
|
if (f.includes("biome")) return "biome";
|
|
3608
3642
|
if (f.includes("eslint")) return "eslint";
|
|
3609
3643
|
if (f.includes("tslint")) return "tslint";
|
|
@@ -3621,6 +3655,7 @@ var formatTool = {
|
|
|
3621
3655
|
usageHint: "RUN REGULARLY:\n\n- Use on changed files before committing.\n- `check: true` verifies formatting without making changes (useful in CI-like flows).\nThis project has very consistent formatting expectations. Always ensure your changes are formatted.",
|
|
3622
3656
|
permission: "confirm",
|
|
3623
3657
|
mutating: true,
|
|
3658
|
+
capabilities: ["fs.write", "shell.exec"],
|
|
3624
3659
|
timeoutMs: 6e4,
|
|
3625
3660
|
inputSchema: {
|
|
3626
3661
|
type: "object",
|
|
@@ -3698,13 +3733,13 @@ var formatTool = {
|
|
|
3698
3733
|
}
|
|
3699
3734
|
};
|
|
3700
3735
|
async function detectFixer(cwd) {
|
|
3701
|
-
const { stat:
|
|
3736
|
+
const { stat: stat10 } = await import('node:fs/promises');
|
|
3702
3737
|
try {
|
|
3703
|
-
await
|
|
3738
|
+
await stat10(`${cwd}/biome.json`);
|
|
3704
3739
|
return "biome";
|
|
3705
3740
|
} catch {
|
|
3706
3741
|
try {
|
|
3707
|
-
await
|
|
3742
|
+
await stat10(`${cwd}/.prettierrc`);
|
|
3708
3743
|
return "prettier";
|
|
3709
3744
|
} catch {
|
|
3710
3745
|
return "biome";
|
|
@@ -3780,11 +3815,11 @@ var typecheckTool = {
|
|
|
3780
3815
|
}
|
|
3781
3816
|
};
|
|
3782
3817
|
async function findTsConfig(cwd) {
|
|
3783
|
-
const { stat:
|
|
3818
|
+
const { stat: stat10 } = await import('node:fs/promises');
|
|
3784
3819
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
3785
3820
|
for (const f of candidates) {
|
|
3786
3821
|
try {
|
|
3787
|
-
const s = await
|
|
3822
|
+
const s = await stat10(path.join(cwd, f));
|
|
3788
3823
|
if (s.isFile()) return path.join(cwd, f);
|
|
3789
3824
|
} catch {
|
|
3790
3825
|
}
|
|
@@ -3861,11 +3896,11 @@ var testTool = {
|
|
|
3861
3896
|
}
|
|
3862
3897
|
};
|
|
3863
3898
|
async function detectRunner(cwd) {
|
|
3864
|
-
const { stat:
|
|
3899
|
+
const { stat: stat10 } = await import('node:fs/promises');
|
|
3865
3900
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
3866
3901
|
for (const f of candidates) {
|
|
3867
3902
|
try {
|
|
3868
|
-
await
|
|
3903
|
+
await stat10(path.join(cwd, f));
|
|
3869
3904
|
if (f.includes("vitest")) return "vitest";
|
|
3870
3905
|
if (f.includes("jest")) return "jest";
|
|
3871
3906
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -4042,20 +4077,6 @@ var installTool = {
|
|
|
4042
4077
|
};
|
|
4043
4078
|
}
|
|
4044
4079
|
};
|
|
4045
|
-
async function detectPackageManager(cwd) {
|
|
4046
|
-
const { stat: stat11 } = await import('node:fs/promises');
|
|
4047
|
-
try {
|
|
4048
|
-
await stat11(`${cwd}/pnpm-lock.yaml`);
|
|
4049
|
-
return "pnpm";
|
|
4050
|
-
} catch {
|
|
4051
|
-
try {
|
|
4052
|
-
await stat11(`${cwd}/yarn.lock`);
|
|
4053
|
-
return "yarn";
|
|
4054
|
-
} catch {
|
|
4055
|
-
return "npm";
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4058
|
-
}
|
|
4059
4080
|
|
|
4060
4081
|
// src/audit.ts
|
|
4061
4082
|
var auditTool = {
|
|
@@ -4089,7 +4110,7 @@ var auditTool = {
|
|
|
4089
4110
|
},
|
|
4090
4111
|
async *executeStream(input, ctx, opts) {
|
|
4091
4112
|
const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
|
|
4092
|
-
const manager = await
|
|
4113
|
+
const manager = await detectPackageManager(cwd);
|
|
4093
4114
|
yield { type: "log", text: `Auditing with ${manager}\u2026`, data: { manager } };
|
|
4094
4115
|
const args = ["audit", "--json"];
|
|
4095
4116
|
if (input.fix) args.push("--fix");
|
|
@@ -4107,20 +4128,6 @@ var auditTool = {
|
|
|
4107
4128
|
yield { type: "final", output: parseAuditOutput(result.stdout, result.exitCode) };
|
|
4108
4129
|
}
|
|
4109
4130
|
};
|
|
4110
|
-
async function detectManager(cwd) {
|
|
4111
|
-
const { stat: stat11 } = await import('node:fs/promises');
|
|
4112
|
-
try {
|
|
4113
|
-
await stat11(`${cwd}/pnpm-lock.yaml`);
|
|
4114
|
-
return "pnpm";
|
|
4115
|
-
} catch {
|
|
4116
|
-
}
|
|
4117
|
-
try {
|
|
4118
|
-
await stat11(`${cwd}/yarn.lock`);
|
|
4119
|
-
return "yarn";
|
|
4120
|
-
} catch {
|
|
4121
|
-
}
|
|
4122
|
-
return "npm";
|
|
4123
|
-
}
|
|
4124
4131
|
function parseAuditOutput(json, exitCode) {
|
|
4125
4132
|
if (!json) {
|
|
4126
4133
|
return {
|
|
@@ -4172,7 +4179,7 @@ var outdatedTool = {
|
|
|
4172
4179
|
description: "Check for outdated dependencies in the project. Reports current, wanted (semver range), and latest versions available.",
|
|
4173
4180
|
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- Safe, read-only operation.\nUse the output to decide on upgrades. Prefer this over manual shell commands for dependency hygiene.",
|
|
4174
4181
|
permission: "auto",
|
|
4175
|
-
mutating:
|
|
4182
|
+
mutating: false,
|
|
4176
4183
|
timeoutMs: 6e4,
|
|
4177
4184
|
inputSchema: {
|
|
4178
4185
|
type: "object",
|
|
@@ -4195,26 +4202,13 @@ var outdatedTool = {
|
|
|
4195
4202
|
},
|
|
4196
4203
|
async execute(input, ctx, opts) {
|
|
4197
4204
|
const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
|
|
4198
|
-
const manager = await
|
|
4205
|
+
const manager = await detectPackageManager(cwd);
|
|
4199
4206
|
const args = ["outdated", "--json"];
|
|
4200
4207
|
if (input.format === "table") args.push("--table");
|
|
4201
4208
|
if (input.include_deprecated) args.push("--include", "deprecated");
|
|
4202
4209
|
return runOutdated(manager, args, cwd, opts.signal);
|
|
4203
4210
|
}
|
|
4204
4211
|
};
|
|
4205
|
-
async function detectManager2(cwd) {
|
|
4206
|
-
try {
|
|
4207
|
-
await stat(`${cwd}/pnpm-lock.yaml`);
|
|
4208
|
-
return "pnpm";
|
|
4209
|
-
} catch {
|
|
4210
|
-
}
|
|
4211
|
-
try {
|
|
4212
|
-
await stat(`${cwd}/yarn.lock`);
|
|
4213
|
-
return "yarn";
|
|
4214
|
-
} catch {
|
|
4215
|
-
}
|
|
4216
|
-
return "npm";
|
|
4217
|
-
}
|
|
4218
4212
|
function runOutdated(manager, args, cwd, signal) {
|
|
4219
4213
|
return new Promise((resolve7) => {
|
|
4220
4214
|
let stdout = "";
|
|
@@ -4480,10 +4474,10 @@ function parseLine(line) {
|
|
|
4480
4474
|
var documentTool = {
|
|
4481
4475
|
name: "document",
|
|
4482
4476
|
category: "Project",
|
|
4483
|
-
description: "
|
|
4484
|
-
usageHint: "USE FOR IMPROVING CODE DOCUMENTATION:\n\n- Good for adding missing docs to public APIs or complex functions.\n-
|
|
4485
|
-
permission: "
|
|
4486
|
-
mutating:
|
|
4477
|
+
description: "Preview documentation comments (JSDoc/TSDoc style) that would be generated for code symbols. Returns a list of candidates with status `skipped` \u2014 the tool is currently a read-only preview and does NOT write to files.",
|
|
4478
|
+
usageHint: "USE FOR IMPROVING CODE DOCUMENTATION:\n\n- Good for adding missing docs to public APIs or complex functions.\n- Currently this is a PREVIEW-ONLY tool: it does not modify files.\n- Use the output to decide which symbols to document manually, or pass the candidates to `edit` / `patch`.\n- `overwrite`, `style`, and `target` parameters are accepted for future expansion but are ignored today.\nAlways review the proposed documentation before applying it \u2014 the model can hallucinate details.",
|
|
4479
|
+
permission: "auto",
|
|
4480
|
+
mutating: false,
|
|
4487
4481
|
timeoutMs: 3e4,
|
|
4488
4482
|
inputSchema: {
|
|
4489
4483
|
type: "object",
|
|
@@ -4558,8 +4552,8 @@ async function resolveFiles2(filesInput, cwd) {
|
|
|
4558
4552
|
for (const f of files) {
|
|
4559
4553
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
4560
4554
|
try {
|
|
4561
|
-
const
|
|
4562
|
-
if (
|
|
4555
|
+
const stat10 = await fs4.stat(absPath);
|
|
4556
|
+
if (stat10.isFile()) resolved.push(absPath);
|
|
4563
4557
|
} catch {
|
|
4564
4558
|
}
|
|
4565
4559
|
}
|
|
@@ -6987,18 +6981,18 @@ async function runIndexer(_ctx, opts) {
|
|
|
6987
6981
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
6988
6982
|
}
|
|
6989
6983
|
for (const file of files) {
|
|
6990
|
-
let
|
|
6984
|
+
let stat10;
|
|
6991
6985
|
try {
|
|
6992
|
-
|
|
6986
|
+
stat10 = await fs4.stat(file);
|
|
6993
6987
|
} catch {
|
|
6994
6988
|
store.deleteFile(file);
|
|
6995
6989
|
continue;
|
|
6996
6990
|
}
|
|
6997
|
-
if (!
|
|
6991
|
+
if (!stat10.isFile()) continue;
|
|
6998
6992
|
const lang = detectLang(file);
|
|
6999
6993
|
if (!lang) continue;
|
|
7000
6994
|
const meta = existingMeta.get(file);
|
|
7001
|
-
if (!force && meta && meta.mtimeMs === Math.floor(
|
|
6995
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat10.mtimeMs)) {
|
|
7002
6996
|
langStats[lang] = (langStats[lang] ?? 0) + meta.symbolCount;
|
|
7003
6997
|
symbolsIndexed += meta.symbolCount;
|
|
7004
6998
|
filesIndexed++;
|
|
@@ -7024,7 +7018,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
7024
7018
|
store.upsertFile({
|
|
7025
7019
|
file,
|
|
7026
7020
|
lang,
|
|
7027
|
-
mtimeMs: Math.floor(
|
|
7021
|
+
mtimeMs: Math.floor(stat10.mtimeMs),
|
|
7028
7022
|
symbolCount: 0,
|
|
7029
7023
|
lastIndexed: Date.now()
|
|
7030
7024
|
});
|
|
@@ -7050,7 +7044,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
7050
7044
|
store.upsertFile({
|
|
7051
7045
|
file,
|
|
7052
7046
|
lang,
|
|
7053
|
-
mtimeMs: Math.floor(
|
|
7047
|
+
mtimeMs: Math.floor(stat10.mtimeMs),
|
|
7054
7048
|
symbolCount: count,
|
|
7055
7049
|
lastIndexed: Date.now()
|
|
7056
7050
|
});
|
|
@@ -7081,8 +7075,9 @@ var codebaseIndexTool = {
|
|
|
7081
7075
|
category: "Project",
|
|
7082
7076
|
description: "Build or incrementally update the project-wide symbol index. This powers fast codebase search and understanding. By default it only processes files that have changed since the last indexing run.",
|
|
7083
7077
|
usageHint: "IMPORTANT FOR LARGE CODEBASES:\n\n- First run (or after major changes): consider `force: true` for a clean rebuild.\n- Normal usage: call without arguments for fast incremental updates.\n- Use `langs` to restrict to specific languages if you only care about certain parts of the project.\nThis tool is relatively expensive \u2014 do not call it on every turn. Use it when the index is stale or before heavy codebase-search sessions.",
|
|
7084
|
-
permission: "
|
|
7078
|
+
permission: "confirm",
|
|
7085
7079
|
mutating: true,
|
|
7080
|
+
capabilities: ["fs.write.outside-project"],
|
|
7086
7081
|
timeoutMs: 12e4,
|
|
7087
7082
|
inputSchema: {
|
|
7088
7083
|
type: "object",
|