@wrongstack/tools 0.250.0 → 0.256.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 +591 -48
- package/dist/audit.js.map +1 -1
- package/dist/{background-indexer-DwJsyAB0.d.ts → background-indexer-CJ5JiV5i.d.ts} +0 -8
- package/dist/bash.js +133 -23
- package/dist/bash.js.map +1 -1
- package/dist/batch-tool-use.js +1 -0
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/builtin.d.ts +25 -1
- package/dist/builtin.js +782 -535
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.d.ts +2 -2
- package/dist/codebase-index/index.js +16 -0
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/worker.js +11 -6
- package/dist/codebase-index/worker.js.map +1 -1
- package/dist/document.js +1 -0
- package/dist/document.js.map +1 -1
- package/dist/exec.js +115 -5
- package/dist/exec.js.map +1 -1
- package/dist/format.js +590 -48
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +380 -128
- package/dist/index.js.map +1 -1
- package/dist/install.js +590 -48
- package/dist/install.js.map +1 -1
- package/dist/json.js +1 -0
- package/dist/json.js.map +1 -1
- package/dist/lint.js +590 -47
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +1 -0
- package/dist/logs.js.map +1 -1
- package/dist/memory.js +4 -0
- package/dist/memory.js.map +1 -1
- package/dist/mode.js +1 -0
- package/dist/mode.js.map +1 -1
- package/dist/outdated.js +17 -3
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +746 -527
- package/dist/pack.js.map +1 -1
- package/dist/test.d.ts +1 -0
- package/dist/test.js +605 -55
- package/dist/test.js.map +1 -1
- package/dist/todo.js +1 -0
- package/dist/todo.js.map +1 -1
- package/dist/tool-help.js +1 -0
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-search.js +1 -0
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-use.js +1 -0
- package/dist/tool-use.js.map +1 -1
- package/dist/typecheck.js +591 -48
- package/dist/typecheck.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import * as fs4 from 'node:fs/promises';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { resolve, sep, dirname, join } from 'node:path';
|
|
4
4
|
import * as Core from '@wrongstack/core';
|
|
5
|
-
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, emptyTaskFile, formatTaskList, formatPlan, recordPackageAction, detectPackageEcosystem, mutateTasks, emptyPlan, computeTaskItemProgress, resolveWstackPaths, truncate } from '@wrongstack/core';
|
|
5
|
+
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, emptyTaskFile, formatTaskList, formatPlan, recordPackageAction, detectPackageEcosystem, mutateTasks, emptyPlan, computeTaskItemProgress, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
|
|
6
6
|
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
7
7
|
import * as os from 'node:os';
|
|
8
8
|
import * as fs7 from 'node:fs';
|
|
9
|
-
import { statSync,
|
|
9
|
+
import { statSync, mkdirSync, createWriteStream, writeFileSync } from 'node:fs';
|
|
10
10
|
import * as dns from 'node:dns/promises';
|
|
11
11
|
import * as net from 'node:net';
|
|
12
12
|
import { Agent } from 'undici';
|
|
@@ -18,14 +18,14 @@ import { randomUUID } from 'node:crypto';
|
|
|
18
18
|
|
|
19
19
|
// src/read.ts
|
|
20
20
|
async function detectPackageManager(cwd) {
|
|
21
|
-
const { stat:
|
|
21
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
22
22
|
try {
|
|
23
|
-
await
|
|
23
|
+
await stat11(`${cwd}/pnpm-lock.yaml`);
|
|
24
24
|
return "pnpm";
|
|
25
25
|
} catch {
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
|
-
await
|
|
28
|
+
await stat11(`${cwd}/yarn.lock`);
|
|
29
29
|
return "yarn";
|
|
30
30
|
} catch {
|
|
31
31
|
}
|
|
@@ -194,9 +194,9 @@ var readTool = {
|
|
|
194
194
|
async execute(input, ctx) {
|
|
195
195
|
if (!input?.path) throw new Error("read: path is required");
|
|
196
196
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
197
|
-
let
|
|
197
|
+
let stat11;
|
|
198
198
|
try {
|
|
199
|
-
|
|
199
|
+
stat11 = await fs4.stat(absPath);
|
|
200
200
|
} catch (err) {
|
|
201
201
|
const code = err.code;
|
|
202
202
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
@@ -204,9 +204,9 @@ var readTool = {
|
|
|
204
204
|
`read: failed to stat "${input.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
205
205
|
);
|
|
206
206
|
}
|
|
207
|
-
if (!
|
|
208
|
-
if (
|
|
209
|
-
throw new Error(`read: file too large (${
|
|
207
|
+
if (!stat11.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
208
|
+
if (stat11.size > MAX_BYTES) {
|
|
209
|
+
throw new Error(`read: file too large (${stat11.size} bytes, limit ${MAX_BYTES})`);
|
|
210
210
|
}
|
|
211
211
|
const buf = await fs4.readFile(absPath);
|
|
212
212
|
if (isBinaryBuffer(buf)) {
|
|
@@ -218,14 +218,14 @@ var readTool = {
|
|
|
218
218
|
const offset = Math.max(1, input.offset ?? 1);
|
|
219
219
|
const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
|
|
220
220
|
if (limit === 0) {
|
|
221
|
-
ctx.recordRead(absPath,
|
|
221
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
222
222
|
return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
|
|
223
223
|
}
|
|
224
224
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
225
225
|
const truncated = offset - 1 + slice.length < total;
|
|
226
226
|
const width = String(offset + slice.length - 1).length;
|
|
227
227
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
228
|
-
ctx.recordRead(absPath,
|
|
228
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
229
229
|
return {
|
|
230
230
|
text: numbered,
|
|
231
231
|
total_lines: total,
|
|
@@ -264,12 +264,12 @@ var writeTool = {
|
|
|
264
264
|
let existed = false;
|
|
265
265
|
let prev = "";
|
|
266
266
|
try {
|
|
267
|
-
const
|
|
268
|
-
existed =
|
|
267
|
+
const stat12 = await fs4.stat(absPath);
|
|
268
|
+
existed = stat12.isFile();
|
|
269
269
|
if (existed) {
|
|
270
270
|
if (!ctx.hasRead(absPath)) {
|
|
271
271
|
prev = await fs4.readFile(absPath, "utf8");
|
|
272
|
-
ctx.recordRead(absPath,
|
|
272
|
+
ctx.recordRead(absPath, stat12.mtimeMs);
|
|
273
273
|
} else {
|
|
274
274
|
prev = await fs4.readFile(absPath, "utf8");
|
|
275
275
|
}
|
|
@@ -282,8 +282,8 @@ var writeTool = {
|
|
|
282
282
|
await atomicWrite(absPath, input.content);
|
|
283
283
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
284
284
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
285
|
-
const
|
|
286
|
-
ctx.recordRead(absPath,
|
|
285
|
+
const stat11 = await fs4.stat(absPath);
|
|
286
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
287
287
|
ctx.session.recordFileChange({
|
|
288
288
|
path: absPath,
|
|
289
289
|
action: existed ? "modified" : "created",
|
|
@@ -323,13 +323,13 @@ var editTool = {
|
|
|
323
323
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
324
324
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
325
325
|
const absPath = await safeResolveReal(input.path, ctx);
|
|
326
|
-
const
|
|
326
|
+
const stat11 = await fs4.stat(absPath).catch((err) => {
|
|
327
327
|
if (err.code === "ENOENT") {
|
|
328
328
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
329
329
|
}
|
|
330
330
|
throw err;
|
|
331
331
|
});
|
|
332
|
-
if (!
|
|
332
|
+
if (!stat11.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
|
|
333
333
|
if (!ctx.hasRead(absPath)) {
|
|
334
334
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
335
335
|
}
|
|
@@ -524,8 +524,8 @@ var replaceTool = {
|
|
|
524
524
|
}
|
|
525
525
|
const rel = path.relative(realRoot, realPath);
|
|
526
526
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
527
|
-
const
|
|
528
|
-
if (!
|
|
527
|
+
const stat11 = await fs4.stat(realPath).catch(() => null);
|
|
528
|
+
if (!stat11 || !stat11.isFile()) continue;
|
|
529
529
|
let content;
|
|
530
530
|
try {
|
|
531
531
|
const buf = await fs4.readFile(realPath);
|
|
@@ -550,7 +550,7 @@ var replaceTool = {
|
|
|
550
550
|
totalReplacements += count;
|
|
551
551
|
if (!dryRun) {
|
|
552
552
|
const newContent = toStyle(newContentLf, style);
|
|
553
|
-
await atomicWrite(realPath, newContent, { mode:
|
|
553
|
+
await atomicWrite(realPath, newContent, { mode: stat11.mode & 511 });
|
|
554
554
|
}
|
|
555
555
|
const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
556
556
|
fromFile: absPath,
|
|
@@ -580,8 +580,8 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
|
|
|
580
580
|
const resolved = [];
|
|
581
581
|
for (const p of parts) {
|
|
582
582
|
const absPath = safeResolve(p, ctx);
|
|
583
|
-
const
|
|
584
|
-
if (
|
|
583
|
+
const stat11 = await fs4.stat(absPath).catch(() => null);
|
|
584
|
+
if (stat11?.isFile()) {
|
|
585
585
|
resolved.push(absPath);
|
|
586
586
|
}
|
|
587
587
|
}
|
|
@@ -644,8 +644,8 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
644
644
|
if (DEFAULT_IGNORE.includes(e.name)) continue;
|
|
645
645
|
const full = path.join(dir, e.name);
|
|
646
646
|
try {
|
|
647
|
-
const
|
|
648
|
-
if (
|
|
647
|
+
const stat11 = await fs4.lstat(full);
|
|
648
|
+
if (stat11.isSymbolicLink()) continue;
|
|
649
649
|
} catch {
|
|
650
650
|
continue;
|
|
651
651
|
}
|
|
@@ -996,8 +996,8 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
996
996
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
997
997
|
if (globRe) globRe.lastIndex = 0;
|
|
998
998
|
try {
|
|
999
|
-
const
|
|
1000
|
-
if (
|
|
999
|
+
const stat11 = await fs4.stat(full);
|
|
1000
|
+
if (stat11.size > 1e6) continue;
|
|
1001
1001
|
const head = await fs4.readFile(full);
|
|
1002
1002
|
if (isBinaryBuffer(head)) continue;
|
|
1003
1003
|
const text = head.toString("utf8");
|
|
@@ -1037,6 +1037,107 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
1037
1037
|
used: "native"
|
|
1038
1038
|
};
|
|
1039
1039
|
}
|
|
1040
|
+
var SPOOL_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1041
|
+
var SPOOL_WRITE_HWM_BYTES = 4 * 1024 * 1024;
|
|
1042
|
+
var sweepStarted = false;
|
|
1043
|
+
function toolOutputDir() {
|
|
1044
|
+
return path.join(wstackGlobalRoot(), "tool-output");
|
|
1045
|
+
}
|
|
1046
|
+
function sweepOldSpoolFiles(dir) {
|
|
1047
|
+
if (sweepStarted) return;
|
|
1048
|
+
sweepStarted = true;
|
|
1049
|
+
void (async () => {
|
|
1050
|
+
try {
|
|
1051
|
+
const now = Date.now();
|
|
1052
|
+
for (const name of await fs4.readdir(dir)) {
|
|
1053
|
+
if (!name.endsWith(".log")) continue;
|
|
1054
|
+
const p = path.join(dir, name);
|
|
1055
|
+
try {
|
|
1056
|
+
const st = await fs4.stat(p);
|
|
1057
|
+
if (now - st.mtimeMs > SPOOL_RETENTION_MS) await fs4.unlink(p);
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
} catch {
|
|
1062
|
+
}
|
|
1063
|
+
})();
|
|
1064
|
+
}
|
|
1065
|
+
function spoolNote(info) {
|
|
1066
|
+
const dropped = info.droppedBytes > 0 ? `, ~${info.droppedBytes} bytes dropped under backpressure` : "";
|
|
1067
|
+
return `
|
|
1068
|
+
[output truncated \u2014 full ${info.bytes} bytes at ${info.path}${dropped}; read/grep that file selectively instead of re-running with more output]`;
|
|
1069
|
+
}
|
|
1070
|
+
function createOutputSpool(opts) {
|
|
1071
|
+
const threshold = opts.thresholdBytes ?? 32768;
|
|
1072
|
+
const safeTool = opts.tool.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
1073
|
+
let head = "";
|
|
1074
|
+
let headBytes = 0;
|
|
1075
|
+
let totalBytes = 0;
|
|
1076
|
+
let droppedBytes = 0;
|
|
1077
|
+
let stream = null;
|
|
1078
|
+
let filePath = null;
|
|
1079
|
+
let failed = false;
|
|
1080
|
+
let finalized = false;
|
|
1081
|
+
const open = () => {
|
|
1082
|
+
if (stream || failed) return;
|
|
1083
|
+
try {
|
|
1084
|
+
const dir = toolOutputDir();
|
|
1085
|
+
mkdirSync(dir, { recursive: true });
|
|
1086
|
+
sweepOldSpoolFiles(dir);
|
|
1087
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1088
|
+
const rand = Math.random().toString(36).slice(2, 6);
|
|
1089
|
+
filePath = path.join(dir, `${stamp}-${safeTool}-${rand}.log`);
|
|
1090
|
+
stream = createWriteStream(filePath, { flags: "w", encoding: "utf8" });
|
|
1091
|
+
stream.on("error", () => {
|
|
1092
|
+
failed = true;
|
|
1093
|
+
stream = null;
|
|
1094
|
+
filePath = null;
|
|
1095
|
+
});
|
|
1096
|
+
stream.write(head);
|
|
1097
|
+
} catch {
|
|
1098
|
+
failed = true;
|
|
1099
|
+
stream = null;
|
|
1100
|
+
filePath = null;
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
return {
|
|
1104
|
+
write(text) {
|
|
1105
|
+
if (finalized || !text) return;
|
|
1106
|
+
totalBytes += Buffer.byteLength(text, "utf8");
|
|
1107
|
+
if (!stream && !failed) {
|
|
1108
|
+
if (headBytes + text.length <= threshold) {
|
|
1109
|
+
head += text;
|
|
1110
|
+
headBytes += text.length;
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
head += text;
|
|
1114
|
+
open();
|
|
1115
|
+
head = "";
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (stream) {
|
|
1119
|
+
if (stream.writableLength > SPOOL_WRITE_HWM_BYTES) {
|
|
1120
|
+
droppedBytes += Buffer.byteLength(text, "utf8");
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
stream.write(text);
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
finalize() {
|
|
1127
|
+
if (finalized) {
|
|
1128
|
+
return filePath ? { path: filePath, bytes: totalBytes, droppedBytes } : null;
|
|
1129
|
+
}
|
|
1130
|
+
finalized = true;
|
|
1131
|
+
head = "";
|
|
1132
|
+
if (!stream || !filePath) return null;
|
|
1133
|
+
try {
|
|
1134
|
+
stream.end();
|
|
1135
|
+
} catch {
|
|
1136
|
+
}
|
|
1137
|
+
return { path: filePath, bytes: totalBytes, droppedBytes };
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1040
1141
|
|
|
1041
1142
|
// src/circuit-breaker.ts
|
|
1042
1143
|
var DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;
|
|
@@ -1516,7 +1617,7 @@ var bashTool = {
|
|
|
1516
1617
|
})();
|
|
1517
1618
|
const args = isWin ? ["/c", input.command] : ["-c", input.command];
|
|
1518
1619
|
const env = buildChildEnv(ctx.session?.id);
|
|
1519
|
-
const detached = isWin
|
|
1620
|
+
const detached = !isWin;
|
|
1520
1621
|
const startedAt = Date.now();
|
|
1521
1622
|
if (input.background) {
|
|
1522
1623
|
let buf2 = "";
|
|
@@ -1525,10 +1626,14 @@ var bashTool = {
|
|
|
1525
1626
|
cwd: ctx.projectRoot,
|
|
1526
1627
|
env,
|
|
1527
1628
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1528
|
-
|
|
1529
|
-
//
|
|
1530
|
-
//
|
|
1531
|
-
//
|
|
1629
|
+
// win32: CreateProcess IGNORES CREATE_NO_WINDOW (windowsHide) when
|
|
1630
|
+
// DETACHED_PROCESS (detached: true) is set, so the console-less
|
|
1631
|
+
// cmd.exe's grandchildren (node, dev servers) each allocate a fresh
|
|
1632
|
+
// VISIBLE console window. detached: false lets CREATE_NO_WINDOW
|
|
1633
|
+
// apply: the child gets a hidden console that grandchildren inherit.
|
|
1634
|
+
// Windows children survive parent exit either way. POSIX keeps
|
|
1635
|
+
// detached for the process-group kill semantics.
|
|
1636
|
+
detached: !isWin,
|
|
1532
1637
|
windowsHide: true,
|
|
1533
1638
|
signal: opts.signal
|
|
1534
1639
|
});
|
|
@@ -1544,24 +1649,22 @@ var bashTool = {
|
|
|
1544
1649
|
});
|
|
1545
1650
|
child2.on("close", () => registry.unregister(pid2));
|
|
1546
1651
|
}
|
|
1547
|
-
|
|
1548
|
-
if (
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
}
|
|
1553
|
-
if (buf2.length >= MAX_OUTPUT) truncated = true;
|
|
1652
|
+
const onBgData = (chunk) => {
|
|
1653
|
+
if (truncated) return;
|
|
1654
|
+
const remain = MAX_OUTPUT - buf2.length;
|
|
1655
|
+
if (remain > 0) {
|
|
1656
|
+
buf2 += chunk.toString().slice(0, remain);
|
|
1554
1657
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
if (remain > 0) {
|
|
1560
|
-
buf2 += chunk.toString().slice(0, remain);
|
|
1561
|
-
}
|
|
1562
|
-
if (buf2.length >= MAX_OUTPUT) truncated = true;
|
|
1658
|
+
if (buf2.length >= MAX_OUTPUT) {
|
|
1659
|
+
truncated = true;
|
|
1660
|
+
child2.stdout?.off("data", onBgData);
|
|
1661
|
+
child2.stderr?.off("data", onBgData);
|
|
1563
1662
|
}
|
|
1564
|
-
}
|
|
1663
|
+
};
|
|
1664
|
+
child2.stdout?.on("data", onBgData);
|
|
1665
|
+
child2.stderr?.on("data", onBgData);
|
|
1666
|
+
child2.stdout?.unref?.();
|
|
1667
|
+
child2.stderr?.unref?.();
|
|
1565
1668
|
child2.on("close", () => {
|
|
1566
1669
|
registry.afterCall(Date.now() - startedAt, false, bypassBreaker);
|
|
1567
1670
|
});
|
|
@@ -1600,6 +1703,7 @@ var bashTool = {
|
|
|
1600
1703
|
let pending2 = "";
|
|
1601
1704
|
let timedOut = false;
|
|
1602
1705
|
const timers = [];
|
|
1706
|
+
const spool = createOutputSpool({ tool: "bash", thresholdBytes: MAX_OUTPUT });
|
|
1603
1707
|
function killWithTimeout(child2, timeoutMs2) {
|
|
1604
1708
|
if (isWin) {
|
|
1605
1709
|
if (typeof child2.pid === "number" && child2.exitCode === null && killWin32Tree(child2.pid)) {
|
|
@@ -1705,6 +1809,7 @@ var bashTool = {
|
|
|
1705
1809
|
if (buf.length < MAX_OUTPUT) {
|
|
1706
1810
|
buf += text.slice(0, MAX_OUTPUT - buf.length);
|
|
1707
1811
|
}
|
|
1812
|
+
spool.write(text);
|
|
1708
1813
|
pending2 += text;
|
|
1709
1814
|
push({ kind: "data", text });
|
|
1710
1815
|
pauseIfFlooded();
|
|
@@ -1732,10 +1837,11 @@ var bashTool = {
|
|
|
1732
1837
|
if (remainder !== null) {
|
|
1733
1838
|
yield { type: "partial_output", text: remainder };
|
|
1734
1839
|
}
|
|
1840
|
+
const spooled = spool.finalize();
|
|
1735
1841
|
yield {
|
|
1736
1842
|
type: "final",
|
|
1737
1843
|
output: {
|
|
1738
|
-
output: normalizeCommandOutput(buf),
|
|
1844
|
+
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : ""),
|
|
1739
1845
|
exit_code: c.code,
|
|
1740
1846
|
timed_out: timedOut
|
|
1741
1847
|
}
|
|
@@ -1750,6 +1856,7 @@ var bashTool = {
|
|
|
1750
1856
|
}
|
|
1751
1857
|
} finally {
|
|
1752
1858
|
for (const t of timers) clearTimeout(t);
|
|
1859
|
+
spool.finalize();
|
|
1753
1860
|
if (isWin) opts.signal.removeEventListener("abort", onAbort);
|
|
1754
1861
|
child.stdout?.off("data", onData);
|
|
1755
1862
|
child.stderr?.off("data", onData);
|
|
@@ -1990,6 +2097,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1990
2097
|
let stderr = "";
|
|
1991
2098
|
let killed = false;
|
|
1992
2099
|
const startedAt = Date.now();
|
|
2100
|
+
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
1993
2101
|
const resolved = resolveWin32Command(cmd);
|
|
1994
2102
|
const isWin = process.platform === "win32";
|
|
1995
2103
|
const needsShell = isWin && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
@@ -2022,10 +2130,14 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
2022
2130
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
2023
2131
|
}
|
|
2024
2132
|
child.stdout?.on("data", (chunk) => {
|
|
2025
|
-
|
|
2133
|
+
const text = chunk.toString();
|
|
2134
|
+
if (stdout.length < MAX_OUTPUT2) stdout += text;
|
|
2135
|
+
spool.write(text);
|
|
2026
2136
|
});
|
|
2027
2137
|
child.stderr?.on("data", (chunk) => {
|
|
2028
|
-
|
|
2138
|
+
const text = chunk.toString();
|
|
2139
|
+
if (stderr.length < MAX_OUTPUT2) stderr += text;
|
|
2140
|
+
spool.write(text);
|
|
2029
2141
|
});
|
|
2030
2142
|
child.on("close", (code) => {
|
|
2031
2143
|
clearTimeout(timer);
|
|
@@ -2034,10 +2146,11 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
2034
2146
|
const durationMs = Date.now() - startedAt;
|
|
2035
2147
|
const exitCode = killed ? 124 : code ?? 1;
|
|
2036
2148
|
registry.afterCall(durationMs, exitCode !== 0);
|
|
2149
|
+
const spooled = spool.finalize();
|
|
2037
2150
|
resolve7({
|
|
2038
2151
|
command: cmd,
|
|
2039
2152
|
args,
|
|
2040
|
-
stdout: normalizeCommandOutput(stdout),
|
|
2153
|
+
stdout: normalizeCommandOutput(stdout) + (spooled ? spoolNote(spooled) : ""),
|
|
2041
2154
|
stderr: normalizeCommandOutput(stderr),
|
|
2042
2155
|
exitCode,
|
|
2043
2156
|
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
|
|
@@ -2049,6 +2162,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
2049
2162
|
if (isWin) signal.removeEventListener("abort", onAbort);
|
|
2050
2163
|
if (typeof pid === "number") registry.unregister(pid);
|
|
2051
2164
|
registry.afterCall(Date.now() - startedAt, true);
|
|
2165
|
+
spool.finalize();
|
|
2052
2166
|
resolve7({
|
|
2053
2167
|
command: cmd,
|
|
2054
2168
|
args,
|
|
@@ -2614,6 +2728,7 @@ var todoTool = {
|
|
|
2614
2728
|
mutating: false,
|
|
2615
2729
|
// mutates only conversation state (ctx.todos), not external state — no confirmation needed
|
|
2616
2730
|
timeoutMs: 1e3,
|
|
2731
|
+
capabilities: ["session.todo"],
|
|
2617
2732
|
inputSchema: {
|
|
2618
2733
|
type: "object",
|
|
2619
2734
|
properties: {
|
|
@@ -3071,8 +3186,8 @@ function findGitDir(cwd, projectRoot) {
|
|
|
3071
3186
|
let dir = cwd;
|
|
3072
3187
|
for (let i = 0; i < 20; i++) {
|
|
3073
3188
|
try {
|
|
3074
|
-
const
|
|
3075
|
-
if (
|
|
3189
|
+
const stat11 = statSync(`${dir}/.git`);
|
|
3190
|
+
if (stat11.isDirectory() || stat11.isFile()) return dir;
|
|
3076
3191
|
} catch {
|
|
3077
3192
|
}
|
|
3078
3193
|
if (dir === root) break;
|
|
@@ -3311,6 +3426,7 @@ var jsonTool = {
|
|
|
3311
3426
|
permission: "auto",
|
|
3312
3427
|
mutating: false,
|
|
3313
3428
|
timeoutMs: 5e3,
|
|
3429
|
+
capabilities: ["fs.read"],
|
|
3314
3430
|
inputSchema: {
|
|
3315
3431
|
type: "object",
|
|
3316
3432
|
properties: {
|
|
@@ -3375,8 +3491,8 @@ var jsonTool = {
|
|
|
3375
3491
|
};
|
|
3376
3492
|
}
|
|
3377
3493
|
};
|
|
3378
|
-
function query(data,
|
|
3379
|
-
const parts =
|
|
3494
|
+
function query(data, path21) {
|
|
3495
|
+
const parts = path21.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
3380
3496
|
let current = data;
|
|
3381
3497
|
for (const part of parts) {
|
|
3382
3498
|
if (current === null || current === void 0) return void 0;
|
|
@@ -3505,8 +3621,8 @@ function findGitDir2(cwd) {
|
|
|
3505
3621
|
let dir = cwd;
|
|
3506
3622
|
for (let i = 0; i < 20; i++) {
|
|
3507
3623
|
try {
|
|
3508
|
-
const
|
|
3509
|
-
if (
|
|
3624
|
+
const stat11 = statSync(path.join(dir, ".git"));
|
|
3625
|
+
if (stat11.isDirectory()) return dir;
|
|
3510
3626
|
} catch {
|
|
3511
3627
|
}
|
|
3512
3628
|
const parent = path.dirname(dir);
|
|
@@ -3550,8 +3666,8 @@ async function fileDiff(input, ctx, _signal) {
|
|
|
3550
3666
|
const results = [];
|
|
3551
3667
|
for (const file of files) {
|
|
3552
3668
|
const absPath = safeResolve(file, ctx);
|
|
3553
|
-
const
|
|
3554
|
-
if (!
|
|
3669
|
+
const stat11 = await fs4.stat(absPath).catch(() => null);
|
|
3670
|
+
if (!stat11?.isFile()) continue;
|
|
3555
3671
|
const content = await fs4.readFile(absPath, "utf8");
|
|
3556
3672
|
const lines = content.split(/\r?\n/);
|
|
3557
3673
|
results.push(formatWithLineNumbers(file, lines));
|
|
@@ -3754,16 +3870,29 @@ async function* spawnStream(opts) {
|
|
|
3754
3870
|
let stderr = "";
|
|
3755
3871
|
let pending2 = "";
|
|
3756
3872
|
let error;
|
|
3873
|
+
const spool = createOutputSpool({ tool: opts.cmd, thresholdBytes: max });
|
|
3757
3874
|
const cmd = resolveWin32Command(opts.cmd);
|
|
3758
|
-
const
|
|
3875
|
+
const isWin = process.platform === "win32";
|
|
3876
|
+
const needsShell = isWin && (cmd.endsWith(".cmd") || cmd.endsWith(".bat"));
|
|
3759
3877
|
const child = spawn(cmd, opts.args, {
|
|
3760
3878
|
cwd: opts.cwd,
|
|
3761
|
-
signal: opts.signal,
|
|
3762
3879
|
env: buildChildEnv(),
|
|
3763
3880
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3764
3881
|
windowsHide: true,
|
|
3882
|
+
...isWin ? {} : { signal: opts.signal },
|
|
3765
3883
|
...needsShell ? { shell: true, windowsVerbatimArguments: true } : {}
|
|
3766
3884
|
});
|
|
3885
|
+
const registry = getProcessRegistry();
|
|
3886
|
+
const pid = child.pid;
|
|
3887
|
+
if (typeof pid === "number") {
|
|
3888
|
+
registry.register({
|
|
3889
|
+
pid,
|
|
3890
|
+
name: opts.cmd,
|
|
3891
|
+
command: redactCommand(`${opts.cmd} ${opts.args.join(" ")}`),
|
|
3892
|
+
startedAt: Date.now(),
|
|
3893
|
+
child
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3767
3896
|
const queue = [];
|
|
3768
3897
|
let waiter;
|
|
3769
3898
|
let paused = false;
|
|
@@ -3781,9 +3910,10 @@ async function* spawnStream(opts) {
|
|
|
3781
3910
|
child.stderr?.resume();
|
|
3782
3911
|
}
|
|
3783
3912
|
};
|
|
3784
|
-
|
|
3913
|
+
const onOut = (c) => {
|
|
3785
3914
|
const s = c.toString();
|
|
3786
3915
|
if (stdout.length < max) stdout += s;
|
|
3916
|
+
spool.write(s);
|
|
3787
3917
|
queue.push({ kind: "out", data: s });
|
|
3788
3918
|
wake();
|
|
3789
3919
|
if (!paused && queue.length >= maxQueue) {
|
|
@@ -3791,10 +3921,11 @@ async function* spawnStream(opts) {
|
|
|
3791
3921
|
child.stdout?.pause();
|
|
3792
3922
|
child.stderr?.pause();
|
|
3793
3923
|
}
|
|
3794
|
-
}
|
|
3795
|
-
|
|
3924
|
+
};
|
|
3925
|
+
const onErr = (c) => {
|
|
3796
3926
|
const s = c.toString();
|
|
3797
3927
|
if (stderr.length < max) stderr += s;
|
|
3928
|
+
spool.write(s);
|
|
3798
3929
|
queue.push({ kind: "err", data: s });
|
|
3799
3930
|
wake();
|
|
3800
3931
|
if (!paused && queue.length >= maxQueue) {
|
|
@@ -3802,51 +3933,92 @@ async function* spawnStream(opts) {
|
|
|
3802
3933
|
child.stdout?.pause();
|
|
3803
3934
|
child.stderr?.pause();
|
|
3804
3935
|
}
|
|
3805
|
-
}
|
|
3936
|
+
};
|
|
3937
|
+
child.stdout?.on("data", onOut);
|
|
3938
|
+
child.stderr?.on("data", onErr);
|
|
3806
3939
|
child.on("error", (e) => {
|
|
3807
3940
|
error = e.message;
|
|
3808
3941
|
queue.push({ kind: "error", data: e.message });
|
|
3809
3942
|
wake();
|
|
3810
3943
|
});
|
|
3811
3944
|
child.on("close", (code) => {
|
|
3945
|
+
if (typeof pid === "number") registry.unregister(pid);
|
|
3812
3946
|
queue.push({ kind: "close", data: "", code: code ?? 0 });
|
|
3813
3947
|
wake();
|
|
3814
3948
|
});
|
|
3949
|
+
const onAbort = () => {
|
|
3950
|
+
if (typeof pid === "number") {
|
|
3951
|
+
registry.kill(pid, { force: true });
|
|
3952
|
+
} else {
|
|
3953
|
+
try {
|
|
3954
|
+
child.kill("SIGKILL");
|
|
3955
|
+
} catch {
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
queue.push({ kind: "close", data: "", code: 124 });
|
|
3959
|
+
wake();
|
|
3960
|
+
};
|
|
3961
|
+
if (opts.signal.aborted) onAbort();
|
|
3962
|
+
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
3815
3963
|
let exitCode = 0;
|
|
3816
3964
|
let spawnFailed = false;
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
if (
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3965
|
+
try {
|
|
3966
|
+
for (; ; ) {
|
|
3967
|
+
while (queue.length === 0) {
|
|
3968
|
+
await new Promise((resolve7) => {
|
|
3969
|
+
waiter = resolve7;
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
const chunk = queue.shift();
|
|
3973
|
+
resume();
|
|
3974
|
+
if (chunk.kind === "close") {
|
|
3975
|
+
if (!spawnFailed) exitCode = chunk.code ?? 0;
|
|
3976
|
+
break;
|
|
3977
|
+
}
|
|
3978
|
+
if (chunk.kind === "error") {
|
|
3979
|
+
spawnFailed = true;
|
|
3980
|
+
exitCode = 1;
|
|
3981
|
+
continue;
|
|
3982
|
+
}
|
|
3983
|
+
pending2 += chunk.data;
|
|
3984
|
+
if (pending2.length >= flushAt) {
|
|
3985
|
+
yield { type: "partial_output", text: pending2 };
|
|
3986
|
+
pending2 = "";
|
|
3987
|
+
}
|
|
3833
3988
|
}
|
|
3834
|
-
pending2
|
|
3835
|
-
if (pending2.length >= flushAt) {
|
|
3989
|
+
if (pending2.length > 0) {
|
|
3836
3990
|
yield { type: "partial_output", text: pending2 };
|
|
3837
|
-
|
|
3991
|
+
}
|
|
3992
|
+
const spooled = spool.finalize();
|
|
3993
|
+
return {
|
|
3994
|
+
// The marker rides on stdout's tail so every consumer's head+tail
|
|
3995
|
+
// normalization keeps it without per-tool changes.
|
|
3996
|
+
stdout: spooled ? stdout + spoolNote(spooled) : stdout,
|
|
3997
|
+
stderr,
|
|
3998
|
+
exitCode,
|
|
3999
|
+
truncated: stdout.length >= max || stderr.length >= max,
|
|
4000
|
+
error,
|
|
4001
|
+
spoolPath: spooled?.path,
|
|
4002
|
+
spoolBytes: spooled?.bytes
|
|
4003
|
+
};
|
|
4004
|
+
} finally {
|
|
4005
|
+
spool.finalize();
|
|
4006
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
4007
|
+
child.stdout?.off("data", onOut);
|
|
4008
|
+
child.stderr?.off("data", onErr);
|
|
4009
|
+
child.stdout?.destroy();
|
|
4010
|
+
child.stderr?.destroy();
|
|
4011
|
+
if (child.exitCode === null && !child.killed) {
|
|
4012
|
+
if (typeof pid === "number") {
|
|
4013
|
+
registry.kill(pid, { force: true });
|
|
4014
|
+
} else {
|
|
4015
|
+
try {
|
|
4016
|
+
child.kill("SIGKILL");
|
|
4017
|
+
} catch {
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
3838
4020
|
}
|
|
3839
4021
|
}
|
|
3840
|
-
if (pending2.length > 0) {
|
|
3841
|
-
yield { type: "partial_output", text: pending2 };
|
|
3842
|
-
}
|
|
3843
|
-
return {
|
|
3844
|
-
stdout,
|
|
3845
|
-
stderr,
|
|
3846
|
-
exitCode,
|
|
3847
|
-
truncated: stdout.length >= max || stderr.length >= max,
|
|
3848
|
-
error
|
|
3849
|
-
};
|
|
3850
4022
|
}
|
|
3851
4023
|
|
|
3852
4024
|
// src/lint.ts
|
|
@@ -3858,6 +4030,7 @@ var lintTool = {
|
|
|
3858
4030
|
permission: "confirm",
|
|
3859
4031
|
mutating: false,
|
|
3860
4032
|
timeoutMs: 6e4,
|
|
4033
|
+
capabilities: ["shell.restricted"],
|
|
3861
4034
|
inputSchema: {
|
|
3862
4035
|
type: "object",
|
|
3863
4036
|
properties: {
|
|
@@ -3929,11 +4102,11 @@ var lintTool = {
|
|
|
3929
4102
|
}
|
|
3930
4103
|
};
|
|
3931
4104
|
async function detectLinter(cwd) {
|
|
3932
|
-
const { stat:
|
|
4105
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
3933
4106
|
const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
|
|
3934
4107
|
for (const f of checks) {
|
|
3935
4108
|
try {
|
|
3936
|
-
await
|
|
4109
|
+
await stat11(`${cwd}/${f}`);
|
|
3937
4110
|
if (f.includes("biome")) return "biome";
|
|
3938
4111
|
if (f.includes("eslint")) return "eslint";
|
|
3939
4112
|
if (f.includes("tslint")) return "tslint";
|
|
@@ -4031,13 +4204,13 @@ var formatTool = {
|
|
|
4031
4204
|
}
|
|
4032
4205
|
};
|
|
4033
4206
|
async function detectFixer(cwd) {
|
|
4034
|
-
const { stat:
|
|
4207
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
4035
4208
|
try {
|
|
4036
|
-
await
|
|
4209
|
+
await stat11(`${cwd}/biome.json`);
|
|
4037
4210
|
return "biome";
|
|
4038
4211
|
} catch {
|
|
4039
4212
|
try {
|
|
4040
|
-
await
|
|
4213
|
+
await stat11(`${cwd}/.prettierrc`);
|
|
4041
4214
|
return "prettier";
|
|
4042
4215
|
} catch {
|
|
4043
4216
|
return "biome";
|
|
@@ -4052,6 +4225,7 @@ var typecheckTool = {
|
|
|
4052
4225
|
permission: "confirm",
|
|
4053
4226
|
mutating: false,
|
|
4054
4227
|
timeoutMs: 12e4,
|
|
4228
|
+
capabilities: ["shell.restricted"],
|
|
4055
4229
|
inputSchema: {
|
|
4056
4230
|
type: "object",
|
|
4057
4231
|
properties: {
|
|
@@ -4115,11 +4289,11 @@ var typecheckTool = {
|
|
|
4115
4289
|
}
|
|
4116
4290
|
};
|
|
4117
4291
|
async function findTsConfig(cwd) {
|
|
4118
|
-
const { stat:
|
|
4292
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
4119
4293
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
4120
4294
|
for (const f of candidates) {
|
|
4121
4295
|
try {
|
|
4122
|
-
const s = await
|
|
4296
|
+
const s = await stat11(path.join(cwd, f));
|
|
4123
4297
|
if (s.isFile()) return path.join(cwd, f);
|
|
4124
4298
|
} catch {
|
|
4125
4299
|
}
|
|
@@ -4134,6 +4308,7 @@ var testTool = {
|
|
|
4134
4308
|
permission: "confirm",
|
|
4135
4309
|
mutating: false,
|
|
4136
4310
|
timeoutMs: 12e4,
|
|
4311
|
+
capabilities: ["shell.restricted"],
|
|
4137
4312
|
inputSchema: {
|
|
4138
4313
|
type: "object",
|
|
4139
4314
|
properties: {
|
|
@@ -4150,7 +4325,11 @@ var testTool = {
|
|
|
4150
4325
|
coverage: { type: "boolean", description: "Generate coverage report (default: false)" },
|
|
4151
4326
|
cwd: { type: "string", description: "Working directory (default: cwd)" },
|
|
4152
4327
|
grep: { type: "string", description: "Filter tests by name pattern (default: none)" },
|
|
4153
|
-
timeout: { type: "integer", description: "Test timeout in ms (default: 30000)" }
|
|
4328
|
+
timeout: { type: "integer", description: "Test timeout in ms (default: 30000)" },
|
|
4329
|
+
verbose: {
|
|
4330
|
+
type: "boolean",
|
|
4331
|
+
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)"
|
|
4332
|
+
}
|
|
4154
4333
|
}
|
|
4155
4334
|
},
|
|
4156
4335
|
async execute(input, ctx, opts) {
|
|
@@ -4198,11 +4377,11 @@ var testTool = {
|
|
|
4198
4377
|
}
|
|
4199
4378
|
};
|
|
4200
4379
|
async function detectRunner(cwd) {
|
|
4201
|
-
const { stat:
|
|
4380
|
+
const { stat: stat11 } = await import('node:fs/promises');
|
|
4202
4381
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
4203
4382
|
for (const f of candidates) {
|
|
4204
4383
|
try {
|
|
4205
|
-
await
|
|
4384
|
+
await stat11(path.join(cwd, f));
|
|
4206
4385
|
if (f.includes("vitest")) return "vitest";
|
|
4207
4386
|
if (f.includes("jest")) return "jest";
|
|
4208
4387
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -4216,17 +4395,14 @@ function buildArgs2(runner, input) {
|
|
|
4216
4395
|
const timeout = input.timeout ?? 3e4;
|
|
4217
4396
|
switch (runner) {
|
|
4218
4397
|
case "vitest":
|
|
4219
|
-
args.push("
|
|
4220
|
-
if (input.
|
|
4221
|
-
args[1] = "";
|
|
4222
|
-
args.push("watch");
|
|
4223
|
-
}
|
|
4398
|
+
args.push(input.watch ? "watch" : "run");
|
|
4399
|
+
if (input.verbose) args.push("--reporter=verbose");
|
|
4224
4400
|
if (input.coverage) args.push("--coverage");
|
|
4225
4401
|
if (input.grep) args.push("--testNamePattern", input.grep);
|
|
4226
4402
|
args.push("--testTimeout", String(timeout));
|
|
4227
4403
|
break;
|
|
4228
4404
|
case "jest":
|
|
4229
|
-
args.push("--verbose");
|
|
4405
|
+
if (input.verbose) args.push("--verbose");
|
|
4230
4406
|
if (input.watch) args.push("--watch");
|
|
4231
4407
|
if (input.coverage) args.push("--coverage");
|
|
4232
4408
|
if (input.grep) args.push("--testPathPattern", input.grep);
|
|
@@ -4270,7 +4446,13 @@ function parseResult(runner, result, duration) {
|
|
|
4270
4446
|
passed,
|
|
4271
4447
|
failed,
|
|
4272
4448
|
duration_ms: duration,
|
|
4273
|
-
|
|
4449
|
+
// A passing run only needs the tail summary in chat history — counts are
|
|
4450
|
+
// already parsed above and the FULL log is on disk (spool marker rides
|
|
4451
|
+
// the stdout tail). Failures keep the standard command-output cap so
|
|
4452
|
+
// the agent sees the failure details inline.
|
|
4453
|
+
output: normalizeCommandOutput(result.stdout || result.error || "", {
|
|
4454
|
+
maxBytes: result.exitCode === 0 ? 4096 : void 0
|
|
4455
|
+
}),
|
|
4274
4456
|
truncated: result.truncated
|
|
4275
4457
|
};
|
|
4276
4458
|
}
|
|
@@ -4419,6 +4601,7 @@ var auditTool = {
|
|
|
4419
4601
|
usageHint: "CRITICAL SECURITY TOOL:\n\n- Run regularly and especially before any release.\n- Use `level` to focus on high/critical issues.\n- `fix` can attempt automatic remediation for some vulnerabilities.\nThis is one of the most important tools for supply chain security.",
|
|
4420
4602
|
permission: "confirm",
|
|
4421
4603
|
mutating: false,
|
|
4604
|
+
capabilities: ["shell.restricted"],
|
|
4422
4605
|
timeoutMs: 6e4,
|
|
4423
4606
|
inputSchema: {
|
|
4424
4607
|
type: "object",
|
|
@@ -4512,9 +4695,23 @@ var outdatedTool = {
|
|
|
4512
4695
|
name: "outdated",
|
|
4513
4696
|
category: "Package Management",
|
|
4514
4697
|
description: "Check for outdated dependencies in the project. Reports current, wanted (semver range), and latest versions available.",
|
|
4515
|
-
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-
|
|
4516
|
-
permission: "
|
|
4517
|
-
mutating:
|
|
4698
|
+
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.",
|
|
4699
|
+
permission: "confirm",
|
|
4700
|
+
// Network side-effecting (registry HTTP). Pairs with `mutating: true`
|
|
4701
|
+
// so the H7 invariant test (`no auto-permission tool declares
|
|
4702
|
+
// mutating: true`) passes — a tool claiming `'auto'` must be purely
|
|
4703
|
+
// read-only, but `outdated` makes outbound HTTP calls to the
|
|
4704
|
+
// registry. The 'confirm' permission routes the call through the
|
|
4705
|
+
// tool.confirm_needed flow on every invocation. M-1 originally
|
|
4706
|
+
// fixed four sibling tools (mcp_control, shellcheck, shellcheck_scan,
|
|
4707
|
+
// web_search) but missed this one; applying the same contract here.
|
|
4708
|
+
mutating: true,
|
|
4709
|
+
// Capability is just "network" — the tool only hits the package
|
|
4710
|
+
// registry over HTTP, never touches the filesystem or runs shell.
|
|
4711
|
+
// The H7 invariant test requires this array to be non-empty for
|
|
4712
|
+
// any mutating:true tool (meta-tools whitelisted). See
|
|
4713
|
+
// tests/permission-mutating-invariant.test.ts:92.
|
|
4714
|
+
capabilities: ["network"],
|
|
4518
4715
|
timeoutMs: 6e4,
|
|
4519
4716
|
inputSchema: {
|
|
4520
4717
|
type: "object",
|
|
@@ -4615,6 +4812,7 @@ var logsTool = {
|
|
|
4615
4812
|
permission: "confirm",
|
|
4616
4813
|
mutating: false,
|
|
4617
4814
|
timeoutMs: 3e4,
|
|
4815
|
+
capabilities: ["shell.restricted"],
|
|
4618
4816
|
inputSchema: {
|
|
4619
4817
|
type: "object",
|
|
4620
4818
|
properties: {
|
|
@@ -4736,7 +4934,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
4736
4934
|
}
|
|
4737
4935
|
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
4738
4936
|
var MAX_TAIL_LINES = 1e5;
|
|
4739
|
-
async function fileLogs(
|
|
4937
|
+
async function fileLogs(path21, lines, filterRe, stream) {
|
|
4740
4938
|
const { createInterface } = await import('node:readline');
|
|
4741
4939
|
const { createReadStream } = await import('node:fs');
|
|
4742
4940
|
const entries = [];
|
|
@@ -4745,7 +4943,7 @@ async function fileLogs(path20, lines, filterRe, stream) {
|
|
|
4745
4943
|
let writeIdx = 0;
|
|
4746
4944
|
let totalLines = 0;
|
|
4747
4945
|
const rl = createInterface({
|
|
4748
|
-
input: createReadStream(
|
|
4946
|
+
input: createReadStream(path21),
|
|
4749
4947
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
4750
4948
|
});
|
|
4751
4949
|
for await (const line of rl) {
|
|
@@ -4766,7 +4964,7 @@ async function fileLogs(path20, lines, filterRe, stream) {
|
|
|
4766
4964
|
if (parsed) entries.push(parsed);
|
|
4767
4965
|
}
|
|
4768
4966
|
return {
|
|
4769
|
-
source:
|
|
4967
|
+
source: path21,
|
|
4770
4968
|
entries,
|
|
4771
4969
|
total: entries.length,
|
|
4772
4970
|
truncated: totalLines > effLines,
|
|
@@ -4816,6 +5014,7 @@ var documentTool = {
|
|
|
4816
5014
|
permission: "auto",
|
|
4817
5015
|
mutating: false,
|
|
4818
5016
|
timeoutMs: 3e4,
|
|
5017
|
+
capabilities: ["fs.read"],
|
|
4819
5018
|
inputSchema: {
|
|
4820
5019
|
type: "object",
|
|
4821
5020
|
properties: {
|
|
@@ -4889,8 +5088,8 @@ async function resolveFiles2(filesInput, cwd) {
|
|
|
4889
5088
|
for (const f of files) {
|
|
4890
5089
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
4891
5090
|
try {
|
|
4892
|
-
const
|
|
4893
|
-
if (
|
|
5091
|
+
const stat11 = await fs4.stat(absPath);
|
|
5092
|
+
if (stat11.isFile()) resolved.push(absPath);
|
|
4894
5093
|
} catch {
|
|
4895
5094
|
}
|
|
4896
5095
|
}
|
|
@@ -5149,6 +5348,7 @@ var toolSearchTool = {
|
|
|
5149
5348
|
permission: "auto",
|
|
5150
5349
|
mutating: false,
|
|
5151
5350
|
timeoutMs: 1e3,
|
|
5351
|
+
capabilities: ["tool.meta"],
|
|
5152
5352
|
inputSchema: {
|
|
5153
5353
|
type: "object",
|
|
5154
5354
|
properties: {
|
|
@@ -5227,6 +5427,7 @@ var toolUseTool = {
|
|
|
5227
5427
|
permission: "confirm",
|
|
5228
5428
|
mutating: true,
|
|
5229
5429
|
timeoutMs: 6e4,
|
|
5430
|
+
capabilities: ["tool.mutate.any"],
|
|
5230
5431
|
inputSchema: {
|
|
5231
5432
|
type: "object",
|
|
5232
5433
|
properties: {
|
|
@@ -5296,6 +5497,7 @@ var batchToolUseTool = {
|
|
|
5296
5497
|
permission: "confirm",
|
|
5297
5498
|
mutating: true,
|
|
5298
5499
|
timeoutMs: 12e4,
|
|
5500
|
+
capabilities: ["tool.mutate.any"],
|
|
5299
5501
|
inputSchema: {
|
|
5300
5502
|
type: "object",
|
|
5301
5503
|
properties: {
|
|
@@ -5400,6 +5602,7 @@ var toolHelpTool = {
|
|
|
5400
5602
|
permission: "auto",
|
|
5401
5603
|
mutating: false,
|
|
5402
5604
|
timeoutMs: 5e3,
|
|
5605
|
+
capabilities: ["tool.meta"],
|
|
5403
5606
|
inputSchema: {
|
|
5404
5607
|
type: "object",
|
|
5405
5608
|
properties: {
|
|
@@ -5523,6 +5726,7 @@ function rememberTool(memory) {
|
|
|
5523
5726
|
permission: "auto",
|
|
5524
5727
|
mutating: true,
|
|
5525
5728
|
timeoutMs: 2e3,
|
|
5729
|
+
capabilities: ["memory.write"],
|
|
5526
5730
|
inputSchema: {
|
|
5527
5731
|
type: "object",
|
|
5528
5732
|
properties: {
|
|
@@ -5574,6 +5778,7 @@ function forgetTool(memory) {
|
|
|
5574
5778
|
permission: "confirm",
|
|
5575
5779
|
mutating: true,
|
|
5576
5780
|
timeoutMs: 2e3,
|
|
5781
|
+
capabilities: ["memory.delete"],
|
|
5577
5782
|
inputSchema: {
|
|
5578
5783
|
type: "object",
|
|
5579
5784
|
properties: {
|
|
@@ -5599,6 +5804,7 @@ function searchMemoryTool(memory) {
|
|
|
5599
5804
|
permission: "auto",
|
|
5600
5805
|
mutating: false,
|
|
5601
5806
|
timeoutMs: 2e3,
|
|
5807
|
+
capabilities: ["memory.read"],
|
|
5602
5808
|
inputSchema: {
|
|
5603
5809
|
type: "object",
|
|
5604
5810
|
properties: {
|
|
@@ -5645,6 +5851,7 @@ function relatedMemoryTool(memory) {
|
|
|
5645
5851
|
permission: "auto",
|
|
5646
5852
|
mutating: false,
|
|
5647
5853
|
timeoutMs: 2e3,
|
|
5854
|
+
capabilities: ["memory.read"],
|
|
5648
5855
|
inputSchema: {
|
|
5649
5856
|
type: "object",
|
|
5650
5857
|
properties: {
|
|
@@ -5693,6 +5900,7 @@ function createModeTool(modeStore) {
|
|
|
5693
5900
|
permission: "confirm",
|
|
5694
5901
|
mutating: true,
|
|
5695
5902
|
timeoutMs: 5e3,
|
|
5903
|
+
capabilities: ["session.mode"],
|
|
5696
5904
|
inputSchema: {
|
|
5697
5905
|
type: "object",
|
|
5698
5906
|
properties: {
|
|
@@ -7902,20 +8110,20 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7902
8110
|
await yieldEventLoop();
|
|
7903
8111
|
throwIfAborted(signal);
|
|
7904
8112
|
}
|
|
7905
|
-
let
|
|
8113
|
+
let stat11;
|
|
7906
8114
|
try {
|
|
7907
8115
|
const statOpts = signal ? { signal } : {};
|
|
7908
|
-
|
|
8116
|
+
stat11 = await fs4.stat(file, statOpts);
|
|
7909
8117
|
} catch (e) {
|
|
7910
8118
|
if (isAbortError(e)) throw e;
|
|
7911
8119
|
store.deleteFile(file);
|
|
7912
8120
|
continue;
|
|
7913
8121
|
}
|
|
7914
|
-
if (!
|
|
8122
|
+
if (!stat11.isFile()) continue;
|
|
7915
8123
|
const lang = detectLang(file);
|
|
7916
8124
|
if (!lang) continue;
|
|
7917
8125
|
const meta = existingMeta.get(file);
|
|
7918
|
-
if (!force && meta && meta.mtimeMs === Math.floor(
|
|
8126
|
+
if (!force && meta && meta.mtimeMs === Math.floor(stat11.mtimeMs)) {
|
|
7919
8127
|
langStats[lang] = (langStats[lang] ?? 0) + meta.symbolCount;
|
|
7920
8128
|
symbolsIndexed += meta.symbolCount;
|
|
7921
8129
|
filesIndexed++;
|
|
@@ -7942,7 +8150,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7942
8150
|
store.upsertFile({
|
|
7943
8151
|
file,
|
|
7944
8152
|
lang,
|
|
7945
|
-
mtimeMs: Math.floor(
|
|
8153
|
+
mtimeMs: Math.floor(stat11.mtimeMs),
|
|
7946
8154
|
symbolCount: 0,
|
|
7947
8155
|
lastIndexed: Date.now()
|
|
7948
8156
|
});
|
|
@@ -7968,7 +8176,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7968
8176
|
store.upsertFile({
|
|
7969
8177
|
file,
|
|
7970
8178
|
lang,
|
|
7971
|
-
mtimeMs: Math.floor(
|
|
8179
|
+
mtimeMs: Math.floor(stat11.mtimeMs),
|
|
7972
8180
|
symbolCount: count,
|
|
7973
8181
|
lastIndexed: Date.now()
|
|
7974
8182
|
});
|
|
@@ -8249,6 +8457,13 @@ function debounceKey(indexDir, file) {
|
|
|
8249
8457
|
function isIndexableFile(filePath) {
|
|
8250
8458
|
return detectLang(filePath) !== null;
|
|
8251
8459
|
}
|
|
8460
|
+
function isUniqueConstraintError(err) {
|
|
8461
|
+
if (err instanceof Error) {
|
|
8462
|
+
const msg = err.message.toLowerCase();
|
|
8463
|
+
return msg.includes("unique constraint") || msg.includes("UNIQUE constraint");
|
|
8464
|
+
}
|
|
8465
|
+
return false;
|
|
8466
|
+
}
|
|
8252
8467
|
async function runStartupIndex(opts) {
|
|
8253
8468
|
if (!indexCircuitBreaker.allowRequest()) throw circuitOpenError();
|
|
8254
8469
|
_indexing = true;
|
|
@@ -8278,6 +8493,15 @@ async function runStartupIndex(opts) {
|
|
|
8278
8493
|
return result;
|
|
8279
8494
|
} catch (err) {
|
|
8280
8495
|
_lastError = err instanceof Error ? err.message : String(err);
|
|
8496
|
+
if (isUniqueConstraintError(err) && !opts.force) {
|
|
8497
|
+
_lastError = null;
|
|
8498
|
+
const rebuildResult = await runStartupIndex({
|
|
8499
|
+
...opts,
|
|
8500
|
+
force: true
|
|
8501
|
+
});
|
|
8502
|
+
_ready = true;
|
|
8503
|
+
return rebuildResult;
|
|
8504
|
+
}
|
|
8281
8505
|
_ready = true;
|
|
8282
8506
|
if (!opts.signal?.aborted) indexCircuitBreaker.recordFailure(err);
|
|
8283
8507
|
throw err;
|
|
@@ -8884,6 +9108,34 @@ ${formatTaskList(file.tasks)}` : file.tasks.length > 0 ? formatTaskList(file.tas
|
|
|
8884
9108
|
};
|
|
8885
9109
|
|
|
8886
9110
|
// src/builtin.ts
|
|
9111
|
+
var OPTIONAL_TOOLS = [
|
|
9112
|
+
installTool,
|
|
9113
|
+
auditTool,
|
|
9114
|
+
outdatedTool,
|
|
9115
|
+
logsTool,
|
|
9116
|
+
documentTool,
|
|
9117
|
+
scaffoldTool,
|
|
9118
|
+
toolSearchTool,
|
|
9119
|
+
toolUseTool,
|
|
9120
|
+
batchToolUseTool,
|
|
9121
|
+
toolHelpTool,
|
|
9122
|
+
codebaseIndexTool,
|
|
9123
|
+
codebaseSearchTool,
|
|
9124
|
+
codebaseStatsTool,
|
|
9125
|
+
setWorkingDirTool
|
|
9126
|
+
];
|
|
9127
|
+
var TIER1_TOOLS = [
|
|
9128
|
+
readTool,
|
|
9129
|
+
writeTool,
|
|
9130
|
+
editTool,
|
|
9131
|
+
bashTool,
|
|
9132
|
+
grepTool,
|
|
9133
|
+
globTool,
|
|
9134
|
+
diffTool,
|
|
9135
|
+
patchTool,
|
|
9136
|
+
jsonTool,
|
|
9137
|
+
searchTool
|
|
9138
|
+
];
|
|
8887
9139
|
var builtinTools = [
|
|
8888
9140
|
readTool,
|
|
8889
9141
|
writeTool,
|
|
@@ -8930,6 +9182,6 @@ var builtinToolsPack = {
|
|
|
8930
9182
|
tools: builtinTools
|
|
8931
9183
|
};
|
|
8932
9184
|
|
|
8933
|
-
export { CircuitBreaker, CircuitOpenError, IndexCircuitBreaker, IndexTimeoutError, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetIndexCircuitBreaker, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
9185
|
+
export { CircuitBreaker, CircuitOpenError, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetIndexCircuitBreaker, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
8934
9186
|
//# sourceMappingURL=index.js.map
|
|
8935
9187
|
//# sourceMappingURL=index.js.map
|