@wrongstack/tools 0.319.0 → 0.320.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 (119) hide show
  1. package/dist/_edit-match.d.ts +1 -3
  2. package/dist/_util.d.ts +7 -0
  3. package/dist/audit.js +8 -8
  4. package/dist/bash.js +6 -6
  5. package/dist/browser/index.js +1 -1
  6. package/dist/builtin.d.ts +10 -0
  7. package/dist/builtin.js +54 -52
  8. package/dist/{chunk-65EMYN5G.js → chunk-23EX5WDO.js} +6 -3
  9. package/dist/{chunk-2472NT76.js → chunk-2OXHS3KZ.js} +17 -5
  10. package/dist/{chunk-7WV3D4S3.js → chunk-34VLZ35Z.js} +75 -36
  11. package/dist/{chunk-MYRMD6PZ.js → chunk-3GIKYHLA.js} +32 -8
  12. package/dist/{chunk-X5K3U6GI.js → chunk-3M7LLN7W.js} +71 -7
  13. package/dist/{chunk-VALQFAY2.js → chunk-4BABEJC2.js} +158 -45
  14. package/dist/{chunk-42BHI7TN.js → chunk-4CQWNZ62.js} +2 -2
  15. package/dist/{chunk-WNUJR3H4.js → chunk-5UFVCIUB.js} +40 -16
  16. package/dist/{chunk-UMXVT7ST.js → chunk-6AAOT4PW.js} +58 -10
  17. package/dist/{chunk-H6AWAZ2Y.js → chunk-B4HTSV6Z.js} +57 -6
  18. package/dist/{chunk-5ZYEGP2P.js → chunk-BK732J7I.js} +72 -27
  19. package/dist/{chunk-KD5EHYA2.js → chunk-C2DQKVLO.js} +61 -28
  20. package/dist/{chunk-Z4VBTVUM.js → chunk-DFQDJT22.js} +99 -12
  21. package/dist/{chunk-6RNIFLG4.js → chunk-ELRRTWNP.js} +38 -6
  22. package/dist/{chunk-3PVYQFRQ.js → chunk-ESLPBT5A.js} +2 -1
  23. package/dist/{chunk-YZHK4TLF.js → chunk-FQNXYA6W.js} +45 -6
  24. package/dist/{chunk-OYVPBPCL.js → chunk-HR67OWEP.js} +72 -33
  25. package/dist/{chunk-ZDUGI5BC.js → chunk-I7FBZOEP.js} +2 -2
  26. package/dist/{chunk-HBM4QNXU.js → chunk-IU4DV2PA.js} +18 -8
  27. package/dist/{chunk-TFMVHYJI.js → chunk-J43O3BOX.js} +9 -15
  28. package/dist/{chunk-ODFVG54U.js → chunk-J74TMK4M.js} +37 -13
  29. package/dist/{chunk-COUNXG4X.js → chunk-JLYA4X7K.js} +75 -8
  30. package/dist/{chunk-FQZOHLG3.js → chunk-JSNSMG5R.js} +3 -2
  31. package/dist/{chunk-SYXW3Y5Z.js → chunk-JVRLTEO7.js} +2 -2
  32. package/dist/{chunk-V43EQ6VR.js → chunk-KE7A76VC.js} +3 -2
  33. package/dist/{chunk-ZKSBHXVB.js → chunk-KWXIKGE3.js} +64 -15
  34. package/dist/{chunk-RK47IWO3.js → chunk-LRDIRJJL.js} +62 -30
  35. package/dist/{chunk-UC3RKRIV.js → chunk-NDIT3SBZ.js} +3 -3
  36. package/dist/{chunk-C3W3V54D.js → chunk-NSPNFOJ7.js} +57 -27
  37. package/dist/{chunk-TX3R5NVV.js → chunk-NU2IZ72J.js} +7 -7
  38. package/dist/{chunk-WHIIIREL.js → chunk-NYRLWDT5.js} +7 -3
  39. package/dist/{chunk-PMHVSHMU.js → chunk-OTPTQBYE.js} +80 -19
  40. package/dist/{chunk-ZHH3BEWU.js → chunk-OYP27E3I.js} +4 -4
  41. package/dist/{chunk-W4GAAI3M.js → chunk-PV6HIZQD.js} +2 -2
  42. package/dist/{chunk-NKNOBUBU.js → chunk-QGF2C7TF.js} +2 -2
  43. package/dist/{chunk-PQTONQ4P.js → chunk-QPNYDH5B.js} +19 -5
  44. package/dist/{chunk-VFF3Q5Z3.js → chunk-QSNVDHOZ.js} +37 -9
  45. package/dist/{chunk-45JPRMC2.js → chunk-QWKSKFKZ.js} +7 -3
  46. package/dist/{chunk-2VSZC6QM.js → chunk-SCMRM6NL.js} +6 -4
  47. package/dist/{chunk-RJZQWEMS.js → chunk-T77XOX7E.js} +3 -2
  48. package/dist/{chunk-QZDOQRF7.js → chunk-UZFY3CKD.js} +35 -14
  49. package/dist/{chunk-D4CXUZIF.js → chunk-VD2LV5WO.js} +82 -13
  50. package/dist/{chunk-DPKTXJQS.js → chunk-VPALDFYM.js} +2 -2
  51. package/dist/{chunk-IIF5HIVF.js → chunk-VVZPQPTY.js} +2 -2
  52. package/dist/{chunk-HWDB2G7J.js → chunk-W232EFEM.js} +49 -36
  53. package/dist/{chunk-FJEQZHZN.js → chunk-W2CECGTG.js} +24 -14
  54. package/dist/{chunk-4Q7LFU4T.js → chunk-WAQASTXB.js} +20 -9
  55. package/dist/{chunk-CWALNI6K.js → chunk-X6KXNHUO.js} +1 -1
  56. package/dist/{chunk-OCUXN6Y3.js → chunk-XRNN44QY.js} +1 -1
  57. package/dist/{chunk-N5O3FK5Q.js → chunk-XSSFBS5I.js} +70 -29
  58. package/dist/{chunk-Y7QXSE7N.js → chunk-YTKSNTT4.js} +44 -10
  59. package/dist/{chunk-MRKWZCHZ.js → chunk-Z5Q3TXRY.js} +3 -3
  60. package/dist/{chunk-PRXZOIEW.js → chunk-Z6IHMJMC.js} +104 -59
  61. package/dist/{chunk-NWZ6XGBJ.js → chunk-ZBMANLYH.js} +5 -3
  62. package/dist/{chunk-OJ4FIBAN.js → chunk-ZMW6WJFT.js} +30 -23
  63. package/dist/{chunk-QLF74MSV.js → chunk-ZUFHAHTE.js} +12 -6
  64. package/dist/codebase-index/index.js +12 -12
  65. package/dist/codebase-index/parser-worker-script.js +3 -3
  66. package/dist/codebase-index/project-server.js +5 -5
  67. package/dist/codebase-index/worker.js +4 -4
  68. package/dist/design.d.ts +1 -1
  69. package/dist/design.js +1 -1
  70. package/dist/diff.js +3 -2
  71. package/dist/document.js +2 -2
  72. package/dist/e2e.js +2 -2
  73. package/dist/edit.js +9 -9
  74. package/dist/exec.js +6 -6
  75. package/dist/fetch.js +2 -2
  76. package/dist/format.js +8 -8
  77. package/dist/git.js +2 -2
  78. package/dist/glob.js +3 -3
  79. package/dist/{go-parser-IKHW67WE.js → go-parser-D7ELWQ7F.js} +2 -2
  80. package/dist/grep.d.ts +2 -0
  81. package/dist/grep.js +5 -3
  82. package/dist/index.js +56 -56
  83. package/dist/install.js +8 -8
  84. package/dist/{json-parser-2EOVP5MR.js → json-parser-34DHYCPW.js} +1 -18
  85. package/dist/json.js +2 -2
  86. package/dist/kanban-tool-types.d.ts +2 -0
  87. package/dist/kanban.js +2 -2
  88. package/dist/languages/index.js +6 -6
  89. package/dist/legacy-bridge-FOV5ODDI.js +18 -0
  90. package/dist/lint.js +8 -8
  91. package/dist/logs.js +2 -2
  92. package/dist/memory.js +1 -1
  93. package/dist/next-steps.d.ts +7 -0
  94. package/dist/next-steps.js +82 -38
  95. package/dist/outdated.js +5 -3
  96. package/dist/pack.js +53 -53
  97. package/dist/{parser-dispatch-3PDBKIQ7.js → parser-dispatch-BDVCJXTV.js} +4 -4
  98. package/dist/patch.js +8 -8
  99. package/dist/plan.js +4 -4
  100. package/dist/process-registry.js +1 -1
  101. package/dist/ps-slash.js +3 -3
  102. package/dist/{py-parser-RFWG2HYI.js → py-parser-QC6MR4GB.js} +3 -3
  103. package/dist/read.js +8 -8
  104. package/dist/replace.js +9 -8
  105. package/dist/scaffold.js +2 -2
  106. package/dist/search.js +1 -1
  107. package/dist/session-kanban.js +1 -1
  108. package/dist/task.js +4 -4
  109. package/dist/test.js +8 -8
  110. package/dist/todo.js +3 -3
  111. package/dist/tool-search.js +1 -1
  112. package/dist/tool-tier.js +54 -54
  113. package/dist/{tree-sitter-parser-FGKHJGSX.js → tree-sitter-parser-GILYQLS4.js} +2 -2
  114. package/dist/tree.js +2 -2
  115. package/dist/typecheck.js +8 -8
  116. package/dist/win32.js +1 -1
  117. package/dist/write.js +9 -9
  118. package/package.json +5 -5
  119. package/dist/legacy-bridge-EC7GM2IW.js +0 -18
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseTreeSitterAst
3
- } from "./chunk-OCUXN6Y3.js";
3
+ } from "./chunk-XRNN44QY.js";
4
4
  import {
5
5
  spawnStream
6
- } from "./chunk-ZHH3BEWU.js";
6
+ } from "./chunk-OYP27E3I.js";
7
7
  import {
8
8
  codebaseIndexStats,
9
9
  enqueueReindex,
@@ -13,7 +13,7 @@ import {
13
13
  outgoingCallsService,
14
14
  runStartupIndex,
15
15
  searchCodebaseIndex
16
- } from "./chunk-MRKWZCHZ.js";
16
+ } from "./chunk-Z5Q3TXRY.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-J43O3BOX.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;
@@ -1618,12 +1643,13 @@ function collapseTsImports(code) {
1618
1643
  let importGroupCount = 0;
1619
1644
  for (let i = 0; i < lines.length; i++) {
1620
1645
  const line = lines[i] ?? "";
1621
- const trimmed = line.trim();
1646
+ let trimmed = line.trim();
1622
1647
  if (trimmed.startsWith("import ") || trimmed.startsWith("import{")) {
1623
1648
  inImportGroup = true;
1624
1649
  importGroupCount++;
1625
1650
  while (!trimmed.includes(";") && !trimmed.includes("from '") && !trimmed.includes('from "') && i + 1 < lines.length) {
1626
1651
  i++;
1652
+ trimmed = (lines[i] ?? "").trim();
1627
1653
  }
1628
1654
  } else {
1629
1655
  if (inImportGroup) {
@@ -2047,7 +2073,9 @@ var codebaseSkeletonTool = {
2047
2073
  required: ["path"],
2048
2074
  additionalProperties: false
2049
2075
  },
2050
- async execute(input, ctx) {
2076
+ async execute(input, ctx, execOpts) {
2077
+ const signal = execOpts?.signal ?? ctx?.signal;
2078
+ signal?.throwIfAborted();
2051
2079
  const targetPath = await safeResolveProjectPath(input.path, ctx);
2052
2080
  let stat3;
2053
2081
  try {
@@ -2103,6 +2131,7 @@ var codebaseSkeletonTool = {
2103
2131
  };
2104
2132
 
2105
2133
  // src/codebase-index/codebase-stats-tool.ts
2134
+ import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
2106
2135
  var codebaseStatsTool = {
2107
2136
  name: "codebase-stats",
2108
2137
  category: "Project",
@@ -2122,6 +2151,8 @@ var codebaseStatsTool = {
2122
2151
  additionalProperties: false
2123
2152
  },
2124
2153
  async execute(_input, ctx, execOpts) {
2154
+ const signal = execOpts?.signal ?? ctx?.signal;
2155
+ signal?.throwIfAborted();
2125
2156
  const idxState = getIndexState();
2126
2157
  const indexPath = resolveIndexDir(ctx.projectRoot, codebaseIndexDirOverride(ctx));
2127
2158
  if (idxState.indexing) {
@@ -2146,11 +2177,11 @@ var codebaseStatsTool = {
2146
2177
  try {
2147
2178
  stats = await codebaseIndexStats(
2148
2179
  { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
2149
- { signal: execOpts?.signal }
2180
+ { signal }
2150
2181
  );
2151
2182
  } catch (err) {
2152
- if (!(err instanceof IndexTimeoutError) && err?.name !== "IndexTimeoutError") {
2153
- throw err;
2183
+ if (signal?.aborted) {
2184
+ signal.throwIfAborted();
2154
2185
  }
2155
2186
  return {
2156
2187
  totalSymbols: 0,
@@ -2162,14 +2193,15 @@ var codebaseStatsTool = {
2162
2193
  indexPath,
2163
2194
  version: SCHEMA_VERSION,
2164
2195
  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."
2196
+ 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
2197
  };
2167
2198
  }
2168
2199
  const circuit = idxState.circuit;
2169
2200
  const hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
2170
2201
  let indexStatus;
2171
2202
  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.");
2203
+ const cooldownSec = Math.max(1, Math.ceil(circuit.cooldownRemainingMs / 1e3));
2204
+ 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
2205
  } else if (!hasPersistedIndex) {
2174
2206
  indexStatus = "No persisted index data found. Run codebase-index to build it before broad code exploration.";
2175
2207
  }
@@ -2191,7 +2223,7 @@ var codebaseStatsTool = {
2191
2223
  // src/codebase-index/codebase-targeted-test-tool.ts
2192
2224
  import * as fs6 from "node:fs/promises";
2193
2225
  import * as path6 from "node:path";
2194
- import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
2226
+ import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
2195
2227
  var TEST_FILE_REGEX2 = /(?:test|spec|mock|fixture|bench)[s]?[/\\._]/i;
2196
2228
  async function findConventionTestFiles(projectRoot, sourceRelPath) {
2197
2229
  const parsed = path6.parse(sourceRelPath);
@@ -2344,7 +2376,7 @@ ${streamRes.stderr}`.trim(),
2344
2376
  failed: 0,
2345
2377
  durationMs: 0,
2346
2378
  output: "",
2347
- error: toErrorMessage9(err)
2379
+ error: toErrorMessage10(err)
2348
2380
  };
2349
2381
  }
2350
2382
  }
@@ -2834,4 +2866,4 @@ export {
2834
2866
  deadCodeScanTool,
2835
2867
  runDeadCodeScan
2836
2868
  };
2837
- //# sourceMappingURL=chunk-RK47IWO3.js.map
2869
+ //# sourceMappingURL=chunk-LRDIRJJL.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  spawnStream
3
- } from "./chunk-ZHH3BEWU.js";
3
+ } from "./chunk-OYP27E3I.js";
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-NDIT3SBZ.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  getIndexState,
3
3
  searchCodebaseIndex
4
- } from "./chunk-MRKWZCHZ.js";
4
+ } from "./chunk-Z5Q3TXRY.js";
5
5
  import {
6
6
  codebaseIndexDirOverride
7
- } from "./chunk-TFMVHYJI.js";
7
+ } from "./chunk-J43O3BOX.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";
@@ -109,12 +109,14 @@ var readTool = {
109
109
  context: { size: stat2.size, limit: MAX_BYTES, reason: "too-large" }
110
110
  });
111
111
  }
112
- const offset = Math.max(1, input.offset ?? 1);
113
- const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
112
+ const rawOffset = typeof input.offset === "number" && Number.isFinite(input.offset) ? input.offset : 1;
113
+ const offset = Math.max(1, Math.floor(rawOffset));
114
+ const rawLimit = typeof input.limit === "number" && Number.isFinite(input.limit) ? input.limit : 2e3;
115
+ const limit = Math.max(0, Math.min(Math.floor(rawLimit), 5e3));
114
116
  const prior = getReadRangeRecord(ctx, absPath);
115
117
  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);
118
+ if (input.mode !== "summary" && limit > 0 && prior && offset <= requestedEnd && coversRange(prior, stat2.mtimeMs, stat2.size, offset, requestedEnd)) {
119
+ ctx.recordRead(absPath, stat2.mtimeMs, "user", ctx.lastReadHash?.(absPath));
118
120
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
119
121
  return {
120
122
  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 +144,14 @@ var readTool = {
142
144
  const total = allLines.length;
143
145
  if (input.mode === "summary") {
144
146
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
145
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, 1, Math.min(total, 200));
147
+ rememberReadRange(ctx, absPath, stat2.mtimeMs, stat2.size, total, 1, Math.min(total, 200));
146
148
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
149
+ const summary = summarizeFile(input.path, stat2.size, allLines);
147
150
  return {
148
- text: summarizeFile(input.path, stat2.size, allLines),
151
+ text: summary.text,
149
152
  total_lines: total,
150
153
  encoding: "utf8",
151
- truncated: total > 200,
154
+ truncated: summary.truncated,
152
155
  note: mergeSymbolNote(
153
156
  "Summary mode returned compact structure instead of full file content.",
154
157
  symResult2?.note
@@ -158,7 +161,7 @@ var readTool = {
158
161
  }
159
162
  if (limit === 0) {
160
163
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
161
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, 1, 0);
164
+ rememberReadRange(ctx, absPath, stat2.mtimeMs, stat2.size, total, 1, 0);
162
165
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
163
166
  return {
164
167
  text: "",
@@ -171,7 +174,6 @@ var readTool = {
171
174
  }
172
175
  if (offset > total) {
173
176
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
174
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, total + 1, total + 1);
175
177
  const symResult2 = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
176
178
  return {
177
179
  text: `[offset ${offset} is past end of file "${input.path}" \u2014 file has ${total} line(s). Do not retry this offset.]`,
@@ -184,10 +186,26 @@ var readTool = {
184
186
  }
185
187
  const slice = allLines.slice(offset - 1, offset - 1 + limit);
186
188
  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");
189
+ const sliceCount = slice.length;
190
+ const width = String(offset + sliceCount - 1).length;
191
+ const parts = new Array(sliceCount);
192
+ for (let i = 0; i < sliceCount; i++) {
193
+ const numStr = String(offset + i);
194
+ const padLen = width - numStr.length;
195
+ const pad = padLen > 0 ? " ".repeat(padLen) : "";
196
+ parts[i] = `${pad}${numStr}\u2192${slice[i]}`;
197
+ }
198
+ const numbered = parts.join("\n");
189
199
  ctx.recordRead(absPath, stat2.mtimeMs, "user", contentHash);
190
- rememberReadRange(ctx, absPath, stat2.mtimeMs, total, offset, offset + slice.length - 1);
200
+ rememberReadRange(
201
+ ctx,
202
+ absPath,
203
+ stat2.mtimeMs,
204
+ stat2.size,
205
+ total,
206
+ offset,
207
+ offset + slice.length - 1
208
+ );
191
209
  const symResult = shouldIncludeSymbols ? await fetchSymbolsForFile(absPath, ctx, signal) : void 0;
192
210
  return {
193
211
  text: numbered,
@@ -208,7 +226,7 @@ async function fetchSymbolsForFile(absPath, ctx, signal) {
208
226
  projectRoot: ctx.projectRoot,
209
227
  indexDir: codebaseIndexDirOverride(ctx),
210
228
  query: "",
211
- file: absPath,
229
+ file: absPath.replace(/\\/g, "/"),
212
230
  limit: 500
213
231
  },
214
232
  { signal }
@@ -248,20 +266,23 @@ function getReadRanges(ctx) {
248
266
  function getReadRangeRecord(ctx, absPath) {
249
267
  return getReadRanges(ctx)[absPath];
250
268
  }
251
- function rememberReadRange(ctx, absPath, mtimeMs, totalLines, start, end) {
269
+ var MTIME_TOLERANCE_MS = process.platform === "win32" ? 2e3 : 1;
270
+ function rememberReadRange(ctx, absPath, mtimeMs, size, totalLines, start, end) {
252
271
  if (end < start) return;
253
272
  const ranges = getReadRanges(ctx);
254
273
  const prior = ranges[absPath];
255
- const nextRanges = prior && Math.abs(prior.mtimeMs - mtimeMs) <= 1 ? prior.ranges.slice() : [];
274
+ const nextRanges = prior && prior.size === size && Math.abs(prior.mtimeMs - mtimeMs) <= MTIME_TOLERANCE_MS ? prior.ranges.slice() : [];
256
275
  nextRanges.push({ start, end });
257
276
  ranges[absPath] = {
258
277
  mtimeMs,
278
+ size,
259
279
  totalLines,
260
280
  ranges: mergeRanges(nextRanges)
261
281
  };
262
282
  }
263
- function coversRange(record, mtimeMs, start, end) {
264
- if (Math.abs(record.mtimeMs - mtimeMs) > 1) return false;
283
+ function coversRange(record, mtimeMs, size, start, end) {
284
+ if (record.size !== void 0 && record.size !== size) return false;
285
+ if (Math.abs(record.mtimeMs - mtimeMs) > MTIME_TOLERANCE_MS) return false;
265
286
  return record.ranges.some((range) => range.start <= start && range.end >= end);
266
287
  }
267
288
  function mergeRanges(ranges) {
@@ -278,21 +299,30 @@ function mergeRanges(ranges) {
278
299
  return merged;
279
300
  }
280
301
  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 [
302
+ const interesting = [];
303
+ 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+)/;
304
+ let truncated = false;
305
+ for (let i = 0; i < lines.length; i++) {
306
+ if (interesting.length >= 80) {
307
+ truncated = true;
308
+ break;
309
+ }
310
+ const trimmed = lines[i].trim();
311
+ if (symbolRegex.test(trimmed)) {
312
+ interesting.push(`${i + 1}: ${trimmed}`);
313
+ }
314
+ }
315
+ const text = [
287
316
  `summary: ${filePath}`,
288
317
  `bytes=${bytes}`,
289
318
  `total_lines=${lines.length}`,
290
319
  interesting.length > 0 ? `symbols/imports:
291
320
  ${interesting.join("\n")}` : "symbols/imports: (none detected)"
292
321
  ].join("\n");
322
+ return { text, truncated };
293
323
  }
294
324
 
295
325
  export {
296
326
  readTool
297
327
  };
298
- //# sourceMappingURL=chunk-C3W3V54D.js.map
328
+ //# sourceMappingURL=chunk-NSPNFOJ7.js.map
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  resolvePythonBinary
3
- } from "./chunk-IIF5HIVF.js";
3
+ } from "./chunk-VVZPQPTY.js";
4
4
  import {
5
5
  parseParserBatchOutput,
6
6
  withSpawnGate
7
7
  } from "./chunk-UCYQTZ7F.js";
8
8
  import {
9
9
  resolveWin32Command
10
- } from "./chunk-V43EQ6VR.js";
10
+ } from "./chunk-KE7A76VC.js";
11
11
 
12
12
  // src/codebase-index/import-extractor.ts
13
13
  var IMPORT_MAX_FILE_CHARS = 512 * 1024;
@@ -828,11 +828,11 @@ async function dispatch(file, content, lang) {
828
828
  return parseSymbols({ file, content, lang });
829
829
  }
830
830
  case "go": {
831
- const { parseSymbols } = await import("./go-parser-IKHW67WE.js");
831
+ const { parseSymbols } = await import("./go-parser-D7ELWQ7F.js");
832
832
  return parseSymbols({ file, content, lang: "go" });
833
833
  }
834
834
  case "py": {
835
- const { parseSymbols } = await import("./py-parser-RFWG2HYI.js");
835
+ const { parseSymbols } = await import("./py-parser-QC6MR4GB.js");
836
836
  return parseSymbols({ file, content, lang: "py" });
837
837
  }
838
838
  case "rs": {
@@ -840,7 +840,7 @@ async function dispatch(file, content, lang) {
840
840
  return parseSymbols({ file, content, lang: "rs" });
841
841
  }
842
842
  case "json": {
843
- const { parseSymbols } = await import("./json-parser-2EOVP5MR.js");
843
+ const { parseSymbols } = await import("./json-parser-34DHYCPW.js");
844
844
  return parseSymbols({ file, content, lang: "json" });
845
845
  }
846
846
  case "yaml": {
@@ -863,7 +863,7 @@ async function dispatch(file, content, lang) {
863
863
  case "shell":
864
864
  case "elixir": {
865
865
  try {
866
- const { parseSymbols: parseSymbols2 } = await import("./tree-sitter-parser-FGKHJGSX.js");
866
+ const { parseSymbols: parseSymbols2 } = await import("./tree-sitter-parser-GILYQLS4.js");
867
867
  const parsed = await parseSymbols2({ file, content, lang });
868
868
  if (parsed.symbols.length > 0) return parsed;
869
869
  } catch {
@@ -889,4 +889,4 @@ export {
889
889
  parseFileContent,
890
890
  parseFilesContent
891
891
  };
892
- //# sourceMappingURL=chunk-TX3R5NVV.js.map
892
+ //# sourceMappingURL=chunk-NU2IZ72J.js.map
@@ -109,6 +109,10 @@ function forgetTool(memory) {
109
109
  }
110
110
  };
111
111
  }
112
+ function normalizeMemoryLimit(value) {
113
+ const raw = typeof value === "number" && Number.isFinite(value) ? value : 5;
114
+ return Math.max(1, Math.min(Math.floor(raw), 20));
115
+ }
112
116
  function searchMemoryTool(memory) {
113
117
  return {
114
118
  name: "search_memory",
@@ -146,7 +150,7 @@ function searchMemoryTool(memory) {
146
150
  });
147
151
  }
148
152
  const scope = input.scope ?? "project-memory";
149
- const limit = Math.min(input.limit ?? 5, 20);
153
+ const limit = normalizeMemoryLimit(input.limit);
150
154
  const entries = await memory.search(input.query, scope, limit);
151
155
  return {
152
156
  results: entries.map((e) => ({
@@ -198,7 +202,7 @@ function relatedMemoryTool(memory) {
198
202
  });
199
203
  }
200
204
  const scope = input.scope ?? "project-memory";
201
- const limit = Math.min(input.limit ?? 5, 20);
205
+ const limit = normalizeMemoryLimit(input.limit);
202
206
  const entries = memory.findRelated ? await memory.findRelated(input.text, scope, limit) : await memory.search(input.text, scope, limit);
203
207
  return {
204
208
  results: entries.map((e) => ({
@@ -220,4 +224,4 @@ export {
220
224
  searchMemoryTool,
221
225
  relatedMemoryTool
222
226
  };
223
- //# sourceMappingURL=chunk-WHIIIREL.js.map
227
+ //# sourceMappingURL=chunk-NYRLWDT5.js.map