@wrongstack/tools 0.319.0 → 0.319.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 (95) hide show
  1. package/dist/_edit-match.d.ts +1 -3
  2. package/dist/_util.d.ts +7 -0
  3. package/dist/audit.js +4 -4
  4. package/dist/bash.js +2 -2
  5. package/dist/builtin.d.ts +10 -0
  6. package/dist/builtin.js +44 -42
  7. package/dist/{chunk-2472NT76.js → chunk-2OXHS3KZ.js} +17 -5
  8. package/dist/{chunk-X5K3U6GI.js → chunk-3M7LLN7W.js} +71 -7
  9. package/dist/{chunk-VFF3Q5Z3.js → chunk-5EUJF3WC.js} +36 -8
  10. package/dist/{chunk-QZDOQRF7.js → chunk-64VIJPFV.js} +34 -13
  11. package/dist/{chunk-UMXVT7ST.js → chunk-6AAOT4PW.js} +58 -10
  12. package/dist/{chunk-WNUJR3H4.js → chunk-AKHYMB4V.js} +36 -12
  13. package/dist/{chunk-ZDUGI5BC.js → chunk-ATSKPZUK.js} +2 -2
  14. package/dist/{chunk-H6AWAZ2Y.js → chunk-B4HTSV6Z.js} +57 -6
  15. package/dist/{chunk-5ZYEGP2P.js → chunk-BK732J7I.js} +72 -27
  16. package/dist/{chunk-PMHVSHMU.js → chunk-BSJSNVGA.js} +73 -14
  17. package/dist/{chunk-KD5EHYA2.js → chunk-C2DQKVLO.js} +61 -28
  18. package/dist/{chunk-PRXZOIEW.js → chunk-CFLJFDYV.js} +100 -58
  19. package/dist/{chunk-TX3R5NVV.js → chunk-CMOUCKGC.js} +2 -2
  20. package/dist/{chunk-SYXW3Y5Z.js → chunk-DC4KMPJO.js} +2 -2
  21. package/dist/{chunk-MRKWZCHZ.js → chunk-DP6CXMJZ.js} +3 -3
  22. package/dist/{chunk-ODFVG54U.js → chunk-EADULOZV.js} +36 -12
  23. package/dist/{chunk-6RNIFLG4.js → chunk-ELRRTWNP.js} +38 -6
  24. package/dist/{chunk-YZHK4TLF.js → chunk-FQNXYA6W.js} +45 -6
  25. package/dist/{chunk-65EMYN5G.js → chunk-GMZYW52L.js} +3 -3
  26. package/dist/{chunk-HBM4QNXU.js → chunk-IU4DV2PA.js} +18 -8
  27. package/dist/{chunk-COUNXG4X.js → chunk-IWS56OY7.js} +30 -7
  28. package/dist/{chunk-Z4VBTVUM.js → chunk-IYKXK356.js} +98 -11
  29. package/dist/{chunk-OJ4FIBAN.js → chunk-JDPQOR7C.js} +14 -7
  30. package/dist/{chunk-TFMVHYJI.js → chunk-KY4PXYSO.js} +9 -15
  31. package/dist/{chunk-ZKSBHXVB.js → chunk-LOBRWKCR.js} +63 -14
  32. package/dist/{chunk-HWDB2G7J.js → chunk-MNP3GE7H.js} +38 -27
  33. package/dist/{chunk-MYRMD6PZ.js → chunk-O44IMQCN.js} +31 -7
  34. package/dist/{chunk-7WV3D4S3.js → chunk-OG2BEUBY.js} +69 -34
  35. package/dist/{chunk-VALQFAY2.js → chunk-OTINMOKK.js} +153 -40
  36. package/dist/{chunk-PQTONQ4P.js → chunk-QPNYDH5B.js} +19 -5
  37. package/dist/{chunk-4Q7LFU4T.js → chunk-RLMX75EM.js} +19 -8
  38. package/dist/{chunk-C3W3V54D.js → chunk-RNWUCZ2U.js} +53 -25
  39. package/dist/{chunk-RK47IWO3.js → chunk-RTZWBNTY.js} +59 -28
  40. package/dist/{chunk-UC3RKRIV.js → chunk-TTZOK6IY.js} +2 -2
  41. package/dist/{chunk-W4GAAI3M.js → chunk-UDVKRX5R.js} +2 -2
  42. package/dist/{chunk-Y7QXSE7N.js → chunk-UQQS3VNT.js} +43 -9
  43. package/dist/{chunk-D4CXUZIF.js → chunk-VD2LV5WO.js} +82 -13
  44. package/dist/{chunk-DPKTXJQS.js → chunk-VPALDFYM.js} +2 -2
  45. package/dist/{chunk-FJEQZHZN.js → chunk-W2CECGTG.js} +24 -14
  46. package/dist/{chunk-OYVPBPCL.js → chunk-X5FOFGXX.js} +72 -33
  47. package/dist/{chunk-CWALNI6K.js → chunk-X6KXNHUO.js} +1 -1
  48. package/dist/{chunk-OCUXN6Y3.js → chunk-XRNN44QY.js} +1 -1
  49. package/dist/{chunk-N5O3FK5Q.js → chunk-YOKD4GWJ.js} +67 -27
  50. package/dist/{chunk-QLF74MSV.js → chunk-ZUFHAHTE.js} +12 -6
  51. package/dist/codebase-index/index.js +7 -7
  52. package/dist/codebase-index/parser-worker-script.js +1 -1
  53. package/dist/codebase-index/project-server.js +3 -3
  54. package/dist/codebase-index/worker.js +2 -2
  55. package/dist/design.d.ts +1 -1
  56. package/dist/design.js +1 -1
  57. package/dist/diff.js +3 -2
  58. package/dist/document.js +2 -2
  59. package/dist/e2e.js +2 -2
  60. package/dist/edit.js +6 -6
  61. package/dist/exec.js +2 -2
  62. package/dist/fetch.js +2 -2
  63. package/dist/format.js +4 -4
  64. package/dist/git.js +2 -2
  65. package/dist/glob.js +2 -2
  66. package/dist/grep.d.ts +2 -0
  67. package/dist/grep.js +4 -2
  68. package/dist/index.js +44 -44
  69. package/dist/install.js +4 -4
  70. package/dist/json.js +2 -2
  71. package/dist/kanban-tool-types.d.ts +2 -0
  72. package/dist/kanban.js +2 -2
  73. package/dist/languages/index.js +2 -2
  74. package/dist/{legacy-bridge-EC7GM2IW.js → legacy-bridge-3T7SR4SK.js} +4 -4
  75. package/dist/lint.js +4 -4
  76. package/dist/logs.js +2 -2
  77. package/dist/outdated.js +4 -2
  78. package/dist/pack.js +43 -43
  79. package/dist/{parser-dispatch-3PDBKIQ7.js → parser-dispatch-QGC4HSP5.js} +2 -2
  80. package/dist/patch.js +6 -6
  81. package/dist/plan.js +4 -4
  82. package/dist/read.js +6 -6
  83. package/dist/replace.js +7 -6
  84. package/dist/scaffold.js +2 -2
  85. package/dist/search.js +1 -1
  86. package/dist/session-kanban.js +1 -1
  87. package/dist/task.js +4 -4
  88. package/dist/test.js +4 -4
  89. package/dist/todo.js +3 -3
  90. package/dist/tool-tier.js +44 -44
  91. package/dist/{tree-sitter-parser-FGKHJGSX.js → tree-sitter-parser-GILYQLS4.js} +2 -2
  92. package/dist/tree.js +2 -2
  93. package/dist/typecheck.js +4 -4
  94. package/dist/write.js +6 -6
  95. package/package.json +5 -5
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  tryLegacyPackageOperation
3
- } from "./chunk-ZDUGI5BC.js";
3
+ } from "./chunk-ATSKPZUK.js";
4
4
  import {
5
5
  spawnStream
6
6
  } from "./chunk-ZHH3BEWU.js";
@@ -8,7 +8,7 @@ import {
8
8
  detectPackageManager,
9
9
  normalizeCommandOutput,
10
10
  safeResolveReal
11
- } from "./chunk-QLF74MSV.js";
11
+ } from "./chunk-ZUFHAHTE.js";
12
12
 
13
13
  // src/install.ts
14
14
  import { join } from "node:path";
@@ -16,6 +16,7 @@ import {
16
16
  detectEcosystem as detectPackageEcosystem,
17
17
  recordPackageAction
18
18
  } from "@wrongstack/core/coordination";
19
+ import { ToolValidationError } from "@wrongstack/core/types";
19
20
  var installTool = {
20
21
  name: "install",
21
22
  category: "Package Management",
@@ -74,8 +75,17 @@ var installTool = {
74
75
  async *executeStream(input, ctx, opts) {
75
76
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
76
77
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
78
+ signal.throwIfAborted();
79
+ const VALID_SAVES = /* @__PURE__ */ new Set(["dependency", "dev", "optional"]);
80
+ if (input.save !== void 0 && !VALID_SAVES.has(input.save)) {
81
+ throw new ToolValidationError({
82
+ message: `install: invalid save option "${input.save}". Allowed: dependency, dev, optional`,
83
+ field: "save"
84
+ });
85
+ }
77
86
  if (!input.global) {
78
- const pkgList2 = input.packages ? Array.isArray(input.packages) ? input.packages : input.packages.split(",").map((p) => p.trim()) : [];
87
+ const rawList2 = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()).filter(Boolean) : [];
88
+ const pkgList2 = Array.from(new Set(rawList2));
79
89
  const bridge = await tryLegacyPackageOperation(
80
90
  "package-install",
81
91
  {
@@ -107,9 +117,8 @@ var installTool = {
107
117
  yield { type: "log", text: `Resolving with ${pkgManager}\u2026`, data: { phase: "resolve" } };
108
118
  const globalFlag = input.global ? ["-g"] : [];
109
119
  const ignoreScripts = input.lifecycleScripts !== true;
110
- const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
111
- (p) => p.trim()
112
- ) : [];
120
+ const rawList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()).filter(Boolean) : [];
121
+ const pkgList = Array.from(new Set(rawList));
113
122
  const PKG_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9^~><=*.+-]+)?$/i;
114
123
  for (const pkg of pkgList) {
115
124
  if (!PKG_NAME_RE.test(pkg) || pkg.startsWith("-") || pkg.length > 200) {
@@ -166,10 +175,12 @@ var installTool = {
166
175
  signal,
167
176
  maxBytes: 1e5
168
177
  });
178
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
179
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
169
180
  const output = {
170
181
  packages: pkgList,
171
182
  exit_code: result.exitCode,
172
- output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
183
+ output: normalizeCommandOutput(rawOutput),
173
184
  dry_run: args.includes("--dry-run"),
174
185
  truncated: result.truncated
175
186
  };
@@ -221,4 +232,4 @@ function resolveManifestPath(cwd, pkgManager) {
221
232
  export {
222
233
  installTool
223
234
  };
224
- //# sourceMappingURL=chunk-4Q7LFU4T.js.map
235
+ //# sourceMappingURL=chunk-RLMX75EM.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  getIndexState,
3
3
  searchCodebaseIndex
4
- } from "./chunk-MRKWZCHZ.js";
4
+ } from "./chunk-DP6CXMJZ.js";
5
5
  import {
6
6
  codebaseIndexDirOverride
7
- } from "./chunk-TFMVHYJI.js";
7
+ } from "./chunk-KY4PXYSO.js";
8
8
  import {
9
9
  isBinaryBuffer,
10
10
  safeResolveReal,
11
11
  sha256hex
12
- } from "./chunk-QLF74MSV.js";
12
+ } from "./chunk-ZUFHAHTE.js";
13
13
 
14
14
  // src/read.ts
15
15
  import * as fs from "node:fs/promises";
@@ -113,8 +113,8 @@ var readTool = {
113
113
  const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
114
114
  const prior = getReadRangeRecord(ctx, absPath);
115
115
  const requestedEnd = prior ? Math.min(offset + limit - 1, prior.totalLines) : offset + limit - 1;
116
- if (input.mode !== "summary" && limit > 0 && prior && coversRange(prior, stat2.mtimeMs, offset, requestedEnd)) {
117
- ctx.recordRead(absPath, stat2.mtimeMs);
116
+ if (input.mode !== "summary" && limit > 0 && prior && offset <= requestedEnd && coversRange(prior, stat2.mtimeMs, stat2.size, offset, requestedEnd)) {
117
+ ctx.recordRead(absPath, stat2.mtimeMs, "user", ctx.lastReadHash?.(absPath));
118
118
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
119
119
  return {
120
120
  text: `[unchanged since previous read: "${input.path}" mtime=${Math.round(stat2.mtimeMs)}; requested lines ${offset}-${requestedEnd} were already shown. Use offset/limit for a new range if needed.]`,
@@ -142,13 +142,14 @@ var readTool = {
142
142
  const total = allLines.length;
143
143
  if (input.mode === "summary") {
144
144
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
145
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, 1, Math.min(total, 200));
145
+ rememberReadRange(ctx, absPath, stat2.mtimeMs, stat2.size, total, 1, Math.min(total, 200));
146
146
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
147
+ const summary = summarizeFile(input.path, stat2.size, allLines);
147
148
  return {
148
- text: summarizeFile(input.path, stat2.size, allLines),
149
+ text: summary.text,
149
150
  total_lines: total,
150
151
  encoding: "utf8",
151
- truncated: total > 200,
152
+ truncated: summary.truncated,
152
153
  note: mergeSymbolNote(
153
154
  "Summary mode returned compact structure instead of full file content.",
154
155
  symResult2?.note
@@ -158,7 +159,7 @@ var readTool = {
158
159
  }
159
160
  if (limit === 0) {
160
161
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
161
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, 1, 0);
162
+ rememberReadRange(ctx, absPath, stat2.mtimeMs, stat2.size, total, 1, 0);
162
163
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
163
164
  return {
164
165
  text: "",
@@ -171,7 +172,6 @@ var readTool = {
171
172
  }
172
173
  if (offset > total) {
173
174
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
174
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, total + 1, total + 1);
175
175
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
176
176
  return {
177
177
  text: `[offset ${offset} is past end of file "${input.path}" \u2014 file has ${total} line(s). Do not retry this offset.]`,
@@ -184,10 +184,26 @@ var readTool = {
184
184
  }
185
185
  const slice = allLines.slice(offset - 1, offset - 1 + limit);
186
186
  const truncated = offset - 1 + slice.length < total;
187
- const width = String(offset + slice.length - 1).length;
188
- const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
187
+ const sliceCount = slice.length;
188
+ const width = String(offset + sliceCount - 1).length;
189
+ const parts = new Array(sliceCount);
190
+ for (let i = 0; i < sliceCount; i++) {
191
+ const numStr = String(offset + i);
192
+ const padLen = width - numStr.length;
193
+ const pad = padLen > 0 ? " ".repeat(padLen) : "";
194
+ parts[i] = `${pad}${numStr}\u2192${slice[i]}`;
195
+ }
196
+ const numbered = parts.join("\n");
189
197
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
190
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, offset, offset + slice.length - 1);
198
+ rememberReadRange(
199
+ ctx,
200
+ absPath,
201
+ stat2.mtimeMs,
202
+ stat2.size,
203
+ total,
204
+ offset,
205
+ offset + slice.length - 1
206
+ );
191
207
  const symResult = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
192
208
  return {
193
209
  text: numbered,
@@ -208,7 +224,7 @@ async function fetchSymbolsForFile(absPath, ctx, signal) {
208
224
  projectRoot: ctx.projectRoot,
209
225
  indexDir: codebaseIndexDirOverride(ctx),
210
226
  query: "",
211
- file: absPath,
227
+ file: absPath.replace(/\\/g, "/"),
212
228
  limit: 500
213
229
  },
214
230
  { signal }
@@ -248,20 +264,23 @@ function getReadRanges(ctx) {
248
264
  function getReadRangeRecord(ctx, absPath) {
249
265
  return getReadRanges(ctx)[absPath];
250
266
  }
251
- function rememberReadRange(ctx, absPath, mtimeMs, totalLines, start, end) {
267
+ var MTIME_TOLERANCE_MS = process.platform === "win32" ? 2e3 : 1;
268
+ function rememberReadRange(ctx, absPath, mtimeMs, size, totalLines, start, end) {
252
269
  if (end < start) return;
253
270
  const ranges = getReadRanges(ctx);
254
271
  const prior = ranges[absPath];
255
- const nextRanges = prior && Math.abs(prior.mtimeMs - mtimeMs) <= 1 ? prior.ranges.slice() : [];
272
+ const nextRanges = prior && prior.size === size && Math.abs(prior.mtimeMs - mtimeMs) <= MTIME_TOLERANCE_MS ? prior.ranges.slice() : [];
256
273
  nextRanges.push({ start, end });
257
274
  ranges[absPath] = {
258
275
  mtimeMs,
276
+ size,
259
277
  totalLines,
260
278
  ranges: mergeRanges(nextRanges)
261
279
  };
262
280
  }
263
- function coversRange(record, mtimeMs, start, end) {
264
- if (Math.abs(record.mtimeMs - mtimeMs) > 1) return false;
281
+ function coversRange(record, mtimeMs, size, start, end) {
282
+ if (record.size !== void 0 && record.size !== size) return false;
283
+ if (Math.abs(record.mtimeMs - mtimeMs) > MTIME_TOLERANCE_MS) return false;
265
284
  return record.ranges.some((range) => range.start <= start && range.end >= end);
266
285
  }
267
286
  function mergeRanges(ranges) {
@@ -278,21 +297,30 @@ function mergeRanges(ranges) {
278
297
  return merged;
279
298
  }
280
299
  function summarizeFile(filePath, bytes, lines) {
281
- const interesting = lines.map((line, index) => ({ line: line.trim(), number: index + 1 })).filter(
282
- ({ line }) => /^(import\s|export\s|class\s|interface\s|type\s|function\s|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=|def\s+|async\s+function\s)/.test(
283
- line
284
- )
285
- ).slice(0, 80).map(({ line, number }) => `${number}: ${line}`);
286
- return [
300
+ const interesting = [];
301
+ const symbolRegex = /^(import\s|export\s|class\s|interface\s|type\s|function\s|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=|def\s+|async\s+(?:def|function)\s|func\s+|fn\s+|pub\s+(?:fn|struct|enum|trait|type|const)\s+|struct\s+|enum\s+|impl\s+)/;
302
+ let truncated = false;
303
+ for (let i = 0; i < lines.length; i++) {
304
+ if (interesting.length >= 80) {
305
+ truncated = true;
306
+ break;
307
+ }
308
+ const trimmed = lines[i].trim();
309
+ if (symbolRegex.test(trimmed)) {
310
+ interesting.push(`${i + 1}: ${trimmed}`);
311
+ }
312
+ }
313
+ const text = [
287
314
  `summary: ${filePath}`,
288
315
  `bytes=${bytes}`,
289
316
  `total_lines=${lines.length}`,
290
317
  interesting.length > 0 ? `symbols/imports:
291
318
  ${interesting.join("\n")}` : "symbols/imports: (none detected)"
292
319
  ].join("\n");
320
+ return { text, truncated };
293
321
  }
294
322
 
295
323
  export {
296
324
  readTool
297
325
  };
298
- //# sourceMappingURL=chunk-C3W3V54D.js.map
326
+ //# sourceMappingURL=chunk-RNWUCZ2U.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseTreeSitterAst
3
- } from "./chunk-OCUXN6Y3.js";
3
+ } from "./chunk-XRNN44QY.js";
4
4
  import {
5
5
  spawnStream
6
6
  } from "./chunk-ZHH3BEWU.js";
@@ -13,7 +13,7 @@ import {
13
13
  outgoingCallsService,
14
14
  runStartupIndex,
15
15
  searchCodebaseIndex
16
- } from "./chunk-MRKWZCHZ.js";
16
+ } from "./chunk-DP6CXMJZ.js";
17
17
  import {
18
18
  IndexTimeoutError,
19
19
  SCHEMA_VERSION,
@@ -21,7 +21,7 @@ import {
21
21
  indexCircuitBreaker,
22
22
  indexStorePool,
23
23
  resolveIndexDir
24
- } from "./chunk-TFMVHYJI.js";
24
+ } from "./chunk-KY4PXYSO.js";
25
25
  import {
26
26
  EXT_TO_LANG,
27
27
  detectLang,
@@ -30,7 +30,7 @@ import {
30
30
  import {
31
31
  normalizeCommandOutput,
32
32
  safeResolveProjectPath
33
- } from "./chunk-QLF74MSV.js";
33
+ } from "./chunk-ZUFHAHTE.js";
34
34
 
35
35
  // src/codebase-index/codebase-ast-replace-tool.ts
36
36
  import * as path2 from "node:path";
@@ -1088,9 +1088,10 @@ var codebaseIncomingCallsTool = {
1088
1088
  };
1089
1089
 
1090
1090
  // src/codebase-index/codebase-index-tool.ts
1091
- import { ToolValidationError } from "@wrongstack/core/types";
1091
+ import { ToolValidationError as ToolValidationError2 } from "@wrongstack/core/types";
1092
1092
 
1093
1093
  // src/codebase-index/codebase-search-tool.ts
1094
+ import { ToolValidationError } from "@wrongstack/core/types";
1094
1095
  import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
1095
1096
  var INDEXABLE_LANG_IDS = [
1096
1097
  ...new Set(Object.values(EXT_TO_LANG))
@@ -1167,6 +1168,18 @@ var codebaseSearchTool = {
1167
1168
  required: ["query"]
1168
1169
  },
1169
1170
  async execute(input, ctx, execOpts) {
1171
+ if (!input?.query || typeof input.query !== "string" || !input.query.trim()) {
1172
+ throw new ToolValidationError({
1173
+ message: "codebase-search: query is required and cannot be empty",
1174
+ field: "query"
1175
+ });
1176
+ }
1177
+ if (input.lspKind !== void 0 && (!Number.isInteger(input.lspKind) || input.lspKind < 1 || input.lspKind > 26)) {
1178
+ throw new ToolValidationError({
1179
+ message: "codebase-search: lspKind must be an integer between 1 and 26",
1180
+ field: "lspKind"
1181
+ });
1182
+ }
1170
1183
  const signal = execOpts?.signal ?? ctx?.signal;
1171
1184
  signal?.throwIfAborted();
1172
1185
  const state = getIndexState();
@@ -1190,7 +1203,7 @@ var codebaseSearchTool = {
1190
1203
  query: input.query,
1191
1204
  kind: input.kind?.toLowerCase(),
1192
1205
  lang: input.lang?.toLowerCase(),
1193
- file: input.file,
1206
+ file: input.file ? input.file.replace(/\\/g, "/").replace(/^\.\//, "") : void 0,
1194
1207
  lspKind: input.lspKind,
1195
1208
  limit
1196
1209
  },
@@ -1224,7 +1237,7 @@ var codebaseSearchTool = {
1224
1237
  try {
1225
1238
  const stats = await codebaseIndexStats(
1226
1239
  { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
1227
- { signal: execOpts?.signal }
1240
+ { signal }
1228
1241
  );
1229
1242
  hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
1230
1243
  } catch {
@@ -1276,12 +1289,20 @@ var codebaseIndexTool = {
1276
1289
  }
1277
1290
  },
1278
1291
  async execute(input, ctx, execOpts) {
1292
+ const signal = execOpts?.signal ?? ctx?.signal;
1293
+ signal?.throwIfAborted();
1279
1294
  if (input.langs) {
1295
+ if (!Array.isArray(input.langs) || input.langs.length === 0) {
1296
+ throw new ToolValidationError2({
1297
+ message: "codebase-index: langs cannot be an empty array. Pass at least one valid language or omit langs.",
1298
+ field: "langs"
1299
+ });
1300
+ }
1280
1301
  const unknown = input.langs.filter(
1281
1302
  (lang) => !INDEXABLE_LANG_IDS.includes(lang)
1282
1303
  );
1283
1304
  if (unknown.length > 0) {
1284
- throw new ToolValidationError({
1305
+ throw new ToolValidationError2({
1285
1306
  message: `codebase-index: unknown lang(s) ${unknown.map((l) => `"${l}"`).join(", ")}. Valid ids: ${INDEXABLE_LANG_IDS.join(", ")}.`,
1286
1307
  field: "langs"
1287
1308
  });
@@ -1297,23 +1318,27 @@ var codebaseIndexTool = {
1297
1318
  note: "A full index is already in progress. Retry codebase-index after it completes (check codebase-stats)."
1298
1319
  };
1299
1320
  }
1300
- const circuit = indexCircuitBreaker.snapshot();
1301
- if (circuit.state === "open" && circuit.cooldownRemainingMs > 0) {
1302
- return {
1303
- filesIndexed: 0,
1304
- symbolsIndexed: 0,
1305
- langStats: {},
1306
- durationMs: 0,
1307
- errors: [],
1308
- note: `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}). Auto-retry possible in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s; the user can run /codebase-reindex to retry immediately.`
1309
- };
1321
+ if (input.force) {
1322
+ indexCircuitBreaker.reset();
1323
+ } else {
1324
+ const circuit = indexCircuitBreaker.snapshot();
1325
+ if (circuit.state === "open" && circuit.cooldownRemainingMs > 0) {
1326
+ return {
1327
+ filesIndexed: 0,
1328
+ symbolsIndexed: 0,
1329
+ langStats: {},
1330
+ durationMs: 0,
1331
+ errors: [],
1332
+ note: `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}). Auto-retry possible in ${Math.max(1, Math.ceil(circuit.cooldownRemainingMs / 1e3))}s; use force: true or run /codebase-reindex to retry immediately.`
1333
+ };
1334
+ }
1310
1335
  }
1311
1336
  const result = await runStartupIndex({
1312
1337
  projectRoot: ctx.projectRoot,
1313
1338
  force: input.force ?? false,
1314
1339
  langs: input.langs,
1315
1340
  indexDir: codebaseIndexDirOverride(ctx),
1316
- signal: execOpts?.signal
1341
+ signal
1317
1342
  });
1318
1343
  if (result.errors.length > MAX_REPORTED_ERRORS) {
1319
1344
  const hidden = result.errors.length - MAX_REPORTED_ERRORS;
@@ -2047,7 +2072,9 @@ var codebaseSkeletonTool = {
2047
2072
  required: ["path"],
2048
2073
  additionalProperties: false
2049
2074
  },
2050
- async execute(input, ctx) {
2075
+ async execute(input, ctx, execOpts) {
2076
+ const signal = execOpts?.signal ?? ctx?.signal;
2077
+ signal?.throwIfAborted();
2051
2078
  const targetPath = await safeResolveProjectPath(input.path, ctx);
2052
2079
  let stat3;
2053
2080
  try {
@@ -2103,6 +2130,7 @@ var codebaseSkeletonTool = {
2103
2130
  };
2104
2131
 
2105
2132
  // src/codebase-index/codebase-stats-tool.ts
2133
+ import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
2106
2134
  var codebaseStatsTool = {
2107
2135
  name: "codebase-stats",
2108
2136
  category: "Project",
@@ -2122,6 +2150,8 @@ var codebaseStatsTool = {
2122
2150
  additionalProperties: false
2123
2151
  },
2124
2152
  async execute(_input, ctx, execOpts) {
2153
+ const signal = execOpts?.signal ?? ctx?.signal;
2154
+ signal?.throwIfAborted();
2125
2155
  const idxState = getIndexState();
2126
2156
  const indexPath = resolveIndexDir(ctx.projectRoot, codebaseIndexDirOverride(ctx));
2127
2157
  if (idxState.indexing) {
@@ -2146,11 +2176,11 @@ var codebaseStatsTool = {
2146
2176
  try {
2147
2177
  stats = await codebaseIndexStats(
2148
2178
  { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
2149
- { signal: execOpts?.signal }
2179
+ { signal }
2150
2180
  );
2151
2181
  } catch (err) {
2152
- if (!(err instanceof IndexTimeoutError) && err?.name !== "IndexTimeoutError") {
2153
- throw err;
2182
+ if (signal?.aborted) {
2183
+ signal.throwIfAborted();
2154
2184
  }
2155
2185
  return {
2156
2186
  totalSymbols: 0,
@@ -2162,14 +2192,15 @@ var codebaseStatsTool = {
2162
2192
  indexPath,
2163
2193
  version: SCHEMA_VERSION,
2164
2194
  statsAvailable: false,
2165
- indexStatus: "Index statistics timed out. Do not repeatedly retry stats; try codebase-search directly or use the appropriate grep/glob/tree fallback."
2195
+ indexStatus: err instanceof IndexTimeoutError || err?.name === "IndexTimeoutError" ? "Index statistics timed out. Do not repeatedly retry stats; try codebase-search directly or use the appropriate grep/glob/tree fallback." : `Index statistics query failed: ${toErrorMessage9(err)}. The index may be corrupted or inaccessible; try /codebase-reindex.`
2166
2196
  };
2167
2197
  }
2168
2198
  const circuit = idxState.circuit;
2169
2199
  const hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
2170
2200
  let indexStatus;
2171
2201
  if (circuit.state === "open") {
2172
- indexStatus = `${hasPersistedIndex ? "Indexing" : "No persisted index data found, and indexing"} is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}); auto-retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s, or run /codebase-reindex. ` + (hasPersistedIndex ? "Stats reflect the last successful build." : "Build the index after recovery.");
2202
+ const cooldownSec = Math.max(1, Math.ceil(circuit.cooldownRemainingMs / 1e3));
2203
+ indexStatus = `${hasPersistedIndex ? "Indexing" : "No persisted index data found, and indexing"} is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}); auto-retry in ${cooldownSec}s, or run /codebase-reindex. ` + (hasPersistedIndex ? "Stats reflect the last successful build." : "Build the index after recovery.");
2173
2204
  } else if (!hasPersistedIndex) {
2174
2205
  indexStatus = "No persisted index data found. Run codebase-index to build it before broad code exploration.";
2175
2206
  }
@@ -2191,7 +2222,7 @@ var codebaseStatsTool = {
2191
2222
  // src/codebase-index/codebase-targeted-test-tool.ts
2192
2223
  import * as fs6 from "node:fs/promises";
2193
2224
  import * as path6 from "node:path";
2194
- import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
2225
+ import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
2195
2226
  var TEST_FILE_REGEX2 = /(?:test|spec|mock|fixture|bench)[s]?[/\\._]/i;
2196
2227
  async function findConventionTestFiles(projectRoot, sourceRelPath) {
2197
2228
  const parsed = path6.parse(sourceRelPath);
@@ -2344,7 +2375,7 @@ ${streamRes.stderr}`.trim(),
2344
2375
  failed: 0,
2345
2376
  durationMs: 0,
2346
2377
  output: "",
2347
- error: toErrorMessage9(err)
2378
+ error: toErrorMessage10(err)
2348
2379
  };
2349
2380
  }
2350
2381
  }
@@ -2834,4 +2865,4 @@ export {
2834
2865
  deadCodeScanTool,
2835
2866
  runDeadCodeScan
2836
2867
  };
2837
- //# sourceMappingURL=chunk-RK47IWO3.js.map
2868
+ //# sourceMappingURL=chunk-RTZWBNTY.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  normalizeCommandOutput,
6
6
  safeResolveReal
7
- } from "./chunk-QLF74MSV.js";
7
+ } from "./chunk-ZUFHAHTE.js";
8
8
 
9
9
  // src/languages/detect.ts
10
10
  import { createHash } from "node:crypto";
@@ -3515,4 +3515,4 @@ export {
3515
3515
  languagePackageTool,
3516
3516
  languageInfoTool
3517
3517
  };
3518
- //# sourceMappingURL=chunk-UC3RKRIV.js.map
3518
+ //# sourceMappingURL=chunk-TTZOK6IY.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveIndexDir
3
- } from "./chunk-TFMVHYJI.js";
3
+ } from "./chunk-KY4PXYSO.js";
4
4
 
5
5
  // src/codebase-index/binary-frame.ts
6
6
  import { decode, encode } from "@msgpack/msgpack";
@@ -120,4 +120,4 @@ export {
120
120
  PROJECT_INDEX_SERVER_MAX_FRAME_CHARS,
121
121
  encodeProjectServerMessage
122
122
  };
123
- //# sourceMappingURL=chunk-W4GAAI3M.js.map
123
+ //# sourceMappingURL=chunk-UDVKRX5R.js.map
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  tryLegacyCodeOperation
3
- } from "./chunk-ZDUGI5BC.js";
3
+ } from "./chunk-ATSKPZUK.js";
4
4
  import {
5
5
  spawnStream
6
6
  } from "./chunk-ZHH3BEWU.js";
7
7
  import {
8
8
  normalizeCommandOutput,
9
9
  safeResolveReal
10
- } from "./chunk-QLF74MSV.js";
10
+ } from "./chunk-ZUFHAHTE.js";
11
11
 
12
12
  // src/test.ts
13
13
  import * as path from "node:path";
14
+ import { ToolValidationError } from "@wrongstack/core/types";
14
15
  var testTool = {
15
16
  name: "test",
16
17
  category: "Code Quality",
@@ -57,6 +58,14 @@ var testTool = {
57
58
  async *executeStream(input, ctx, opts) {
58
59
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
59
60
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
61
+ signal.throwIfAborted();
62
+ const VALID_RUNNERS = /* @__PURE__ */ new Set(["vitest", "jest", "mocha", "auto", "none"]);
63
+ if (input.runner !== void 0 && !VALID_RUNNERS.has(input.runner)) {
64
+ throw new ToolValidationError({
65
+ message: `test: unsupported runner "${input.runner}". Allowed runners: vitest, jest, mocha, auto, none`,
66
+ field: "runner"
67
+ });
68
+ }
60
69
  const runner = input.runner ?? "auto";
61
70
  if (runner === "auto") {
62
71
  const bridge = await tryLegacyCodeOperation("test", {
@@ -83,7 +92,7 @@ var testTool = {
83
92
  }
84
93
  }
85
94
  const detected = runner === "auto" ? await detectRunner(cwd) : runner;
86
- if (!detected) {
95
+ if (!detected || detected === "none") {
87
96
  yield {
88
97
  type: "final",
89
98
  output: {
@@ -115,7 +124,21 @@ var testTool = {
115
124
  };
116
125
  async function detectRunner(cwd) {
117
126
  const { stat } = await import("node:fs/promises");
118
- const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
127
+ const candidates = [
128
+ "vitest.config.ts",
129
+ "vitest.config.js",
130
+ "vitest.config.mjs",
131
+ "vitest.config.mts",
132
+ "vitest.config.cjs",
133
+ "vitest.config.cts",
134
+ "jest.config.ts",
135
+ "jest.config.js",
136
+ "jest.config.mjs",
137
+ "jest.config.cjs",
138
+ ".mocharc.json",
139
+ ".mocharc.js",
140
+ ".mocharc.cjs"
141
+ ];
119
142
  for (const f of candidates) {
120
143
  try {
121
144
  await stat(path.join(cwd, f));
@@ -129,7 +152,8 @@ async function detectRunner(cwd) {
129
152
  }
130
153
  function buildArgs(runner, input) {
131
154
  const args = [];
132
- const timeout = input.timeout ?? 3e4;
155
+ const rawTimeout = typeof input.timeout === "number" && Number.isFinite(input.timeout) && input.timeout > 0 ? Math.floor(input.timeout) : 3e4;
156
+ const timeout = Math.max(100, rawTimeout);
133
157
  switch (runner) {
134
158
  case "vitest":
135
159
  args.push(input.watch ? "watch" : "run");
@@ -152,8 +176,10 @@ function buildArgs(runner, input) {
152
176
  break;
153
177
  }
154
178
  if (input.files) {
155
- const files = Array.isArray(input.files) ? input.files : input.files.split(",");
156
- args.push("--", ...files.map((f) => f.trim()));
179
+ const files = (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim().replace(/\\/g, "/")).filter(Boolean);
180
+ if (files.length > 0) {
181
+ args.push("--", ...files);
182
+ }
157
183
  }
158
184
  return args;
159
185
  }
@@ -175,7 +201,15 @@ function parseResult(runner, result, duration) {
175
201
  tests_run = Number.parseInt(suitesMatch?.[1] ?? "0", 10);
176
202
  passed = Number.parseInt(passedMatch?.[1] ?? "0", 10);
177
203
  failed = Number.parseInt(failedMatch?.[1] ?? "0", 10);
204
+ } else if (runner === "mocha") {
205
+ const passedMatch = out.match(/(\d+)\s+passing/);
206
+ const failedMatch = out.match(/(\d+)\s+failing/);
207
+ if (passedMatch?.[1]) passed = Number.parseInt(passedMatch[1], 10);
208
+ if (failedMatch?.[1]) failed = Number.parseInt(failedMatch[1], 10);
209
+ tests_run = passed + failed;
178
210
  }
211
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
212
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
179
213
  return {
180
214
  runner,
181
215
  exit_code: result.exitCode,
@@ -187,7 +221,7 @@ function parseResult(runner, result, duration) {
187
221
  // already parsed above and the FULL log is on disk (spool marker rides
188
222
  // the stdout tail). Failures keep the standard command-output cap so
189
223
  // the agent sees the failure details inline.
190
- output: normalizeCommandOutput(result.stdout || result.error || "", {
224
+ output: normalizeCommandOutput(rawOutput, {
191
225
  maxBytes: result.exitCode === 0 ? 4096 : void 0
192
226
  }),
193
227
  truncated: result.truncated
@@ -197,4 +231,4 @@ function parseResult(runner, result, duration) {
197
231
  export {
198
232
  testTool
199
233
  };
200
- //# sourceMappingURL=chunk-Y7QXSE7N.js.map
234
+ //# sourceMappingURL=chunk-UQQS3VNT.js.map