@wrongstack/tools 0.320.1 → 1.0.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 (192) hide show
  1. package/dist/_danger-detect.d.ts +8 -0
  2. package/dist/_syntax-check.d.ts +1 -2
  3. package/dist/audit.d.ts +4 -5
  4. package/dist/audit.js +6 -6
  5. package/dist/auto-proceed-loop-guard.d.ts +1 -1
  6. package/dist/auto-proceed-loop-guard.js +9 -160
  7. package/dist/bash-kill-guard.d.ts +2 -3
  8. package/dist/bash.d.ts +2 -3
  9. package/dist/bash.js +4 -4
  10. package/dist/batch-tool-use.d.ts +2 -3
  11. package/dist/batch-tool-use.js +1 -1
  12. package/dist/browser/index.d.ts +1 -1
  13. package/dist/browser/index.js +1 -1
  14. package/dist/browser/tools.d.ts +56 -45
  15. package/dist/builtin.js +50 -50
  16. package/dist/{chunk-T77XOX7E.js → chunk-24T5XIJU.js} +1 -1
  17. package/dist/{chunk-DTQ75VBC.js → chunk-2CTZW6JQ.js} +2 -2
  18. package/dist/{chunk-KWXIKGE3.js → chunk-3D5DJWOE.js} +3 -3
  19. package/dist/{chunk-XSSFBS5I.js → chunk-4ERMPDHV.js} +11 -6
  20. package/dist/{chunk-QSNVDHOZ.js → chunk-4WPBS45A.js} +4 -5
  21. package/dist/{chunk-4CQWNZ62.js → chunk-5ITQPCLC.js} +2 -2
  22. package/dist/{chunk-B4HTSV6Z.js → chunk-5KS24LTS.js} +11 -11
  23. package/dist/{chunk-QPNYDH5B.js → chunk-62OAECEQ.js} +29 -7
  24. package/dist/{chunk-DFQDJT22.js → chunk-6GOZTS6F.js} +3 -3
  25. package/dist/{chunk-5UFVCIUB.js → chunk-6H4ERJP7.js} +140 -24
  26. package/dist/{chunk-34VLZ35Z.js → chunk-7HL23T3T.js} +63 -10
  27. package/dist/{chunk-HFXSNKCV.js → chunk-7JJFC6CT.js} +1 -1
  28. package/dist/{chunk-23EX5WDO.js → chunk-7LQCT3LT.js} +3 -3
  29. package/dist/{chunk-NDIT3SBZ.js → chunk-7N5U2RAU.js} +2 -2
  30. package/dist/{chunk-QBV725YG.js → chunk-7ODZTPCU.js} +2 -2
  31. package/dist/{chunk-6AAOT4PW.js → chunk-AQ5YBURX.js} +16 -4
  32. package/dist/{chunk-ZMW6WJFT.js → chunk-ASEXQRA7.js} +3 -3
  33. package/dist/{chunk-FRN7TIAA.js → chunk-B3MGJBFI.js} +1 -1
  34. package/dist/{chunk-JLYA4X7K.js → chunk-CGXTR34H.js} +21 -6
  35. package/dist/{chunk-IU4DV2PA.js → chunk-CHEK3ASV.js} +6 -3
  36. package/dist/{chunk-GOIZNNOA.js → chunk-DJYLI7EW.js} +1 -1
  37. package/dist/{chunk-Z6IHMJMC.js → chunk-DK4J2WMH.js} +11 -11
  38. package/dist/{chunk-OYP27E3I.js → chunk-DRRY6SCM.js} +47 -20
  39. package/dist/{chunk-JVRLTEO7.js → chunk-E7JE3UEX.js} +2 -2
  40. package/dist/{chunk-BK732J7I.js → chunk-FBUP524S.js} +4 -4
  41. package/dist/{chunk-NYRLWDT5.js → chunk-FVM3O4JQ.js} +1 -1
  42. package/dist/chunk-FYDBUVQD.js +244 -0
  43. package/dist/{chunk-PV6HIZQD.js → chunk-GDDXX27P.js} +50 -50
  44. package/dist/{chunk-3GIKYHLA.js → chunk-GKSG5CPH.js} +6 -6
  45. package/dist/{chunk-ESLPBT5A.js → chunk-HDYUBRMF.js} +4 -3
  46. package/dist/{chunk-4BABEJC2.js → chunk-IAYMTTR2.js} +236 -79
  47. package/dist/{chunk-ELRRTWNP.js → chunk-IXIS7RSG.js} +2 -2
  48. package/dist/{chunk-HR67OWEP.js → chunk-IYH4OHBP.js} +5 -5
  49. package/dist/{chunk-W232EFEM.js → chunk-JDOO4P6Z.js} +7 -4
  50. package/dist/{chunk-2OXHS3KZ.js → chunk-KLJZ4WV4.js} +4 -3
  51. package/dist/{chunk-3M7LLN7W.js → chunk-LQSOU63Z.js} +12 -11
  52. package/dist/{chunk-OTPTQBYE.js → chunk-MGPR7VG3.js} +279 -265
  53. package/dist/{chunk-C2DQKVLO.js → chunk-MJSJWXBF.js} +1 -1
  54. package/dist/{chunk-NSPNFOJ7.js → chunk-MK2JK35J.js} +25 -10
  55. package/dist/{chunk-JSNSMG5R.js → chunk-MSOAX3GC.js} +1 -1
  56. package/dist/{chunk-J43O3BOX.js → chunk-N3K2SFEI.js} +532 -492
  57. package/dist/{chunk-J74TMK4M.js → chunk-NJX2RXKJ.js} +50 -7
  58. package/dist/chunk-NL2S2CXD.js +171 -0
  59. package/dist/{chunk-WAQASTXB.js → chunk-O6BY43ZB.js} +29 -8
  60. package/dist/{chunk-QGF2C7TF.js → chunk-PVCB7RUZ.js} +2 -2
  61. package/dist/{chunk-YTKSNTT4.js → chunk-QA6IASYX.js} +8 -8
  62. package/dist/{chunk-VPALDFYM.js → chunk-RV7BVTDG.js} +1 -1
  63. package/dist/chunk-S57CDR2H.js +288 -0
  64. package/dist/{chunk-W2CECGTG.js → chunk-SMARDKAC.js} +1 -1
  65. package/dist/{chunk-QWKSKFKZ.js → chunk-SOR7OCYP.js} +1 -1
  66. package/dist/{chunk-I7FBZOEP.js → chunk-T3SMFF5Q.js} +11 -5
  67. package/dist/{chunk-4BHUSMNL.js → chunk-UI3ZJIG3.js} +1 -1
  68. package/dist/{chunk-WXJY3BKV.js → chunk-VBF2YXUW.js} +2 -2
  69. package/dist/{chunk-FQNXYA6W.js → chunk-VU3QBYL4.js} +36 -5
  70. package/dist/{chunk-VD2LV5WO.js → chunk-VZGPASGD.js} +37 -12
  71. package/dist/{chunk-UZFY3CKD.js → chunk-WEEVSWQB.js} +17 -6
  72. package/dist/chunk-XXRMF2BC.js +186 -0
  73. package/dist/{chunk-LRDIRJJL.js → chunk-YEISRT4S.js} +5 -11
  74. package/dist/{chunk-Q5JPPCZC.js → chunk-YJTXRH5Y.js} +3 -2
  75. package/dist/{chunk-Z5Q3TXRY.js → chunk-ZTCJCYLN.js} +3 -3
  76. package/dist/circuit-breaker.d.ts +1 -0
  77. package/dist/circuit-breaker.js +1 -1
  78. package/dist/clarify.d.ts +1 -0
  79. package/dist/codebase-index/codebase-ast-replace-tool.d.ts +2 -3
  80. package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +5 -4
  81. package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +4 -3
  82. package/dist/codebase-index/codebase-index-tool.d.ts +2 -3
  83. package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -3
  84. package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +4 -3
  85. package/dist/codebase-index/codebase-repo-map-tool.d.ts +2 -3
  86. package/dist/codebase-index/codebase-search-tool.d.ts +2 -3
  87. package/dist/codebase-index/codebase-skeleton-tool.d.ts +2 -3
  88. package/dist/codebase-index/codebase-stats-tool.d.ts +9 -3
  89. package/dist/codebase-index/codebase-targeted-test-tool.d.ts +4 -3
  90. package/dist/codebase-index/index.d.ts +11 -11
  91. package/dist/codebase-index/index.js +8 -8
  92. package/dist/codebase-index/project-server-framing.d.ts +29 -0
  93. package/dist/codebase-index/project-server-lifecycle.d.ts +20 -0
  94. package/dist/codebase-index/project-server-operations.d.ts +37 -0
  95. package/dist/codebase-index/project-server-types.d.ts +30 -0
  96. package/dist/codebase-index/project-server-watcher.d.ts +25 -0
  97. package/dist/codebase-index/project-server.js +448 -391
  98. package/dist/codebase-index/worker.js +2 -2
  99. package/dist/codebase-index/writer-init.d.ts +13 -0
  100. package/dist/codebase-index/writer-maintenance.d.ts +14 -0
  101. package/dist/codebase-index/writer.d.ts +0 -15
  102. package/dist/design.d.ts +2 -2
  103. package/dist/design.js +1 -1
  104. package/dist/diff.d.ts +4 -4
  105. package/dist/diff.js +1 -1
  106. package/dist/document.d.ts +3 -4
  107. package/dist/document.js +1 -1
  108. package/dist/e2e.d.ts +1 -1
  109. package/dist/e2e.js +1 -1
  110. package/dist/edit.d.ts +3 -3
  111. package/dist/edit.js +6 -6
  112. package/dist/exec.d.ts +2 -3
  113. package/dist/exec.js +4 -4
  114. package/dist/fetch.d.ts +4 -3
  115. package/dist/fetch.js +1 -1
  116. package/dist/format.d.ts +17 -45
  117. package/dist/format.js +12 -8
  118. package/dist/git.d.ts +3 -3
  119. package/dist/git.js +1 -1
  120. package/dist/glob.d.ts +2 -3
  121. package/dist/glob.js +2 -2
  122. package/dist/grep.d.ts +7 -5
  123. package/dist/grep.js +3 -3
  124. package/dist/index.d.ts +54 -45
  125. package/dist/index.js +118 -59
  126. package/dist/install.d.ts +7 -55
  127. package/dist/install.js +6 -6
  128. package/dist/json.d.ts +3 -4
  129. package/dist/json.js +2 -2
  130. package/dist/kanban-tool-types.d.ts +1 -2
  131. package/dist/kanban.d.ts +3 -19
  132. package/dist/kanban.js +1 -1
  133. package/dist/languages/execute-tool.d.ts +2 -3
  134. package/dist/languages/index.d.ts +2 -2
  135. package/dist/languages/index.js +4 -4
  136. package/dist/languages/tool.d.ts +3 -4
  137. package/dist/{legacy-bridge-FOV5ODDI.js → legacy-bridge-YXANP5QB.js} +6 -6
  138. package/dist/lint.d.ts +5 -4
  139. package/dist/lint.js +6 -6
  140. package/dist/logs.d.ts +3 -4
  141. package/dist/logs.js +2 -2
  142. package/dist/memory.d.ts +7 -8
  143. package/dist/memory.js +1 -1
  144. package/dist/mode.d.ts +3 -4
  145. package/dist/mode.js +1 -1
  146. package/dist/next-steps-tool.d.ts +2 -3
  147. package/dist/next-steps.d.ts +4 -0
  148. package/dist/next-steps.js +9 -175
  149. package/dist/outdated.d.ts +5 -29
  150. package/dist/outdated.js +3 -3
  151. package/dist/pack.js +51 -51
  152. package/dist/patch.d.ts +2 -3
  153. package/dist/patch.js +5 -5
  154. package/dist/plan.d.ts +4 -4
  155. package/dist/plan.js +3 -3
  156. package/dist/process-registry.js +2 -2
  157. package/dist/ps-slash.js +4 -4
  158. package/dist/pwsh.d.ts +1 -0
  159. package/dist/read.d.ts +5 -5
  160. package/dist/read.js +5 -5
  161. package/dist/replace.d.ts +2 -3
  162. package/dist/replace.js +6 -6
  163. package/dist/scaffold.d.ts +7 -3
  164. package/dist/scaffold.js +3 -1
  165. package/dist/search.d.ts +17 -3
  166. package/dist/search.js +5 -1
  167. package/dist/set-working-dir.d.ts +2 -3
  168. package/dist/skill.d.ts +4 -5
  169. package/dist/skill.js +1 -1
  170. package/dist/task.d.ts +6 -6
  171. package/dist/task.js +3 -3
  172. package/dist/test.d.ts +5 -4
  173. package/dist/test.js +6 -6
  174. package/dist/todo.d.ts +8 -8
  175. package/dist/todo.js +2 -2
  176. package/dist/tool-diff.js +11 -230
  177. package/dist/tool-help.d.ts +2 -3
  178. package/dist/tool-help.js +1 -1
  179. package/dist/tool-search.d.ts +2 -3
  180. package/dist/tool-search.js +1 -1
  181. package/dist/tool-summary.js +5 -281
  182. package/dist/tool-tier.js +52 -52
  183. package/dist/tool-use.d.ts +2 -3
  184. package/dist/tool-use.js +1 -1
  185. package/dist/tree.d.ts +5 -3
  186. package/dist/tree.js +5 -1
  187. package/dist/typecheck.d.ts +3 -3
  188. package/dist/typecheck.js +6 -6
  189. package/dist/write.d.ts +2 -3
  190. package/dist/write.js +6 -6
  191. package/package.json +5 -5
  192. package/dist/spawn-background.d.ts +0 -80
@@ -117,7 +117,7 @@ function formatAllToolsMarkdown(tools) {
117
117
  lines.push("| Tool | Description |");
118
118
  lines.push("|------|-------------|");
119
119
  for (const t of tools) {
120
- lines.push(`| \`${t.name}\` | ${t.description} |`);
120
+ lines.push(`| \`${t.name}\` | ${t.description.replaceAll("|", "\\|")} |`);
121
121
  }
122
122
  return lines.join("\n");
123
123
  }
@@ -125,4 +125,4 @@ function formatAllToolsMarkdown(tools) {
125
125
  export {
126
126
  toolHelpTool
127
127
  };
128
- //# sourceMappingURL=chunk-QBV725YG.js.map
128
+ //# sourceMappingURL=chunk-7ODZTPCU.js.map
@@ -103,6 +103,12 @@ var gitTool = {
103
103
  field: "command"
104
104
  });
105
105
  }
106
+ if (input.limit !== void 0 && (typeof input.limit !== "number" || !Number.isFinite(input.limit) || input.limit <= 0)) {
107
+ throw new ToolValidationError({
108
+ message: "git: limit must be a positive integer",
109
+ field: "limit"
110
+ });
111
+ }
106
112
  if (input.command === "commit" && !input.message) {
107
113
  return {
108
114
  command: "commit",
@@ -214,7 +220,8 @@ function findGitDir(cwd, projectRoot) {
214
220
  function buildArgs(input) {
215
221
  const rawLimit = typeof input.limit === "number" && Number.isFinite(input.limit) && input.limit > 0 ? Math.floor(input.limit) : 20;
216
222
  const limit = Math.max(1, rawLimit);
217
- const files = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((s) => s.trim().replace(/\\/g, "/")).filter(Boolean) : [];
223
+ const rawFiles = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).filter((s) => typeof s === "string").map((s) => s.trim().replace(/\\/g, "/")).filter(Boolean) : [];
224
+ const files = [...new Set(rawFiles)];
218
225
  switch (input.command) {
219
226
  case "status":
220
227
  return ["status", ...files.length ? ["--", ...files] : []];
@@ -247,9 +254,14 @@ function buildArgs(input) {
247
254
  case "stash":
248
255
  return input.message ? ["stash", "push", "-m", input.message] : ["stash", "push"];
249
256
  case "push":
250
- return ["push"];
257
+ return [
258
+ "push",
259
+ ...input.dry_run ? ["--dry-run"] : [],
260
+ ...input.force ? ["--force"] : [],
261
+ ...input.branch ? ["origin", input.branch] : []
262
+ ];
251
263
  case "pull":
252
- return ["pull"];
264
+ return ["pull", ...input.branch ? ["origin", input.branch] : []];
253
265
  case "fetch":
254
266
  return ["fetch", ...input.branch ? [input.branch] : ["--all"]];
255
267
  case "reset":
@@ -361,4 +373,4 @@ function runGit(args, cwd, signal) {
361
373
  export {
362
374
  gitTool
363
375
  };
364
- //# sourceMappingURL=chunk-6AAOT4PW.js.map
376
+ //# sourceMappingURL=chunk-AQ5YBURX.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-ZBMANLYH.js";
4
4
  import {
5
5
  loadGitignoreMatcher
6
- } from "./chunk-DTQ75VBC.js";
6
+ } from "./chunk-2CTZW6JQ.js";
7
7
  import {
8
8
  assertRealInsideRoot,
9
9
  safeResolveReal
@@ -144,7 +144,7 @@ var globTool = {
144
144
  } catch {
145
145
  }
146
146
  }
147
- if (truncated) return;
147
+ if (signal?.aborted) return;
148
148
  }
149
149
  await mapWithConcurrency(matchedFiles, WALK_CONCURRENCY, pushResult);
150
150
  await mapWithConcurrency(subdirs, WALK_CONCURRENCY, ({ full, rel }) => walk(full, rel));
@@ -158,4 +158,4 @@ var globTool = {
158
158
  export {
159
159
  globTool
160
160
  };
161
- //# sourceMappingURL=chunk-ZMW6WJFT.js.map
161
+ //# sourceMappingURL=chunk-ASEXQRA7.js.map
@@ -96,4 +96,4 @@ var toolUseTool = {
96
96
  export {
97
97
  toolUseTool
98
98
  };
99
- //# sourceMappingURL=chunk-FRN7TIAA.js.map
99
+ //# sourceMappingURL=chunk-B3MGJBFI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  enqueueReindex
3
- } from "./chunk-Z5Q3TXRY.js";
3
+ } from "./chunk-ZTCJCYLN.js";
4
4
  import {
5
5
  resolveRealInsideRoot,
6
6
  safeResolveReal,
@@ -50,12 +50,12 @@ var patchTool = {
50
50
  writeTargets(input) {
51
51
  if (!input?.patch || typeof input.patch !== "string" || !input.patch.trim()) return [];
52
52
  const strip = typeof input.strip === "number" && Number.isFinite(input.strip) && input.strip >= 0 ? Math.floor(input.strip) : 1;
53
- const out = [];
53
+ const out = /* @__PURE__ */ new Set();
54
54
  for (const target of extractDiffTargets(input.patch)) {
55
55
  const stripped = stripPathComponents(target.raw, strip);
56
- if (stripped) out.push(stripped);
56
+ if (stripped) out.add(stripped);
57
57
  }
58
- return out;
58
+ return [...out];
59
59
  },
60
60
  async execute(input, ctx, opts) {
61
61
  if (!input?.patch || typeof input.patch !== "string" || !input.patch.trim()) {
@@ -66,6 +66,12 @@ var patchTool = {
66
66
  }
67
67
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
68
68
  signal.throwIfAborted();
69
+ if (input.strip !== void 0 && (typeof input.strip !== "number" || !Number.isFinite(input.strip) || input.strip < 0)) {
70
+ throw new ToolValidationError({
71
+ message: "patch: strip must be a non-negative integer",
72
+ field: "strip"
73
+ });
74
+ }
69
75
  const strip = typeof input.strip === "number" && Number.isFinite(input.strip) && input.strip >= 0 ? Math.floor(input.strip) : 1;
70
76
  const dryRun = input.dry_run ?? false;
71
77
  const refuse = (message) => ({
@@ -108,6 +114,11 @@ var patchTool = {
108
114
  }
109
115
  resolvedTargets.push({ raw: t.raw, deleted: t.deleted, abs: real });
110
116
  }
117
+ if (resolvedTargets.length === 0) {
118
+ return refuse(
119
+ "patch refused: no file target could be resolved from the patch (fail-closed containment \u2014 target paths stripped to empty)"
120
+ );
121
+ }
111
122
  const beforeContents = /* @__PURE__ */ new Map();
112
123
  const beforeExisted = /* @__PURE__ */ new Set();
113
124
  if (!dryRun) {
@@ -180,7 +191,11 @@ var patchTool = {
180
191
  message: `patch failed: ${result.stderr || result.stdout}${partial}`
181
192
  };
182
193
  }
183
- const wouldPatch = extractPatchedFiles(result.stdout);
194
+ const wouldPatch = result.engine === "git" ? [
195
+ ...new Set(
196
+ resolvedTargets.map((target) => path.relative(dir, target.abs) || target.abs)
197
+ )
198
+ ] : extractPatchedFiles(result.stdout);
184
199
  return {
185
200
  applied: wouldPatch.length,
186
201
  rejected: 1,
@@ -380,4 +395,4 @@ function extractPatchedFiles(output) {
380
395
  export {
381
396
  patchTool
382
397
  };
383
- //# sourceMappingURL=chunk-JLYA4X7K.js.map
398
+ //# sourceMappingURL=chunk-CGXTR34H.js.map
@@ -29,7 +29,10 @@ var documentTool = {
29
29
  description: "Specific file path to document"
30
30
  },
31
31
  files: {
32
- type: "string",
32
+ oneOf: [
33
+ { type: "string" },
34
+ { type: "array", items: { type: "string" } }
35
+ ],
33
36
  description: "File(s) to process: single path, comma-separated list, or glob"
34
37
  },
35
38
  style: {
@@ -111,7 +114,7 @@ async function resolveFiles(filesInput, cwd, ctx) {
111
114
  } catch {
112
115
  }
113
116
  }
114
- return resolved;
117
+ return [...new Set(resolved)];
115
118
  }
116
119
  function processFile(content, absPath, _style, _overwrite, target) {
117
120
  const results = [];
@@ -177,4 +180,4 @@ function processFile(content, absPath, _style, _overwrite, target) {
177
180
  export {
178
181
  documentTool
179
182
  };
180
- //# sourceMappingURL=chunk-IU4DV2PA.js.map
183
+ //# sourceMappingURL=chunk-CHEK3ASV.js.map
@@ -218,4 +218,4 @@ async function walk(root, dir, out) {
218
218
  export {
219
219
  makeSkillTool
220
220
  };
221
- //# sourceMappingURL=chunk-GOIZNNOA.js.map
221
+ //# sourceMappingURL=chunk-DJYLI7EW.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  checkSyntax
3
- } from "./chunk-JSNSMG5R.js";
3
+ } from "./chunk-MSOAX3GC.js";
4
4
  import {
5
5
  enqueueReindex
6
- } from "./chunk-Z5Q3TXRY.js";
6
+ } from "./chunk-ZTCJCYLN.js";
7
7
  import {
8
8
  safeResolveReal,
9
9
  sha256hex,
@@ -140,6 +140,10 @@ function fuzzyScan(fileLines, needleLines, offsets, includeTrailingNewline = fal
140
140
  if (fileTrimmed[i + n - 1] !== lastNeedle) continue;
141
141
  const windowInterior = fileTrimmed.slice(i + 1, i + n - 1).join("\n");
142
142
  if (windowInterior.length > FUZZY_MAX_INTERIOR_CHARS) continue;
143
+ const maxLen = Math.max(needleInterior.length, windowInterior.length);
144
+ if (maxLen > 0 && Math.abs(needleInterior.length - windowInterior.length) / maxLen > 1 - FUZZY_MIN_SIMILARITY) {
145
+ continue;
146
+ }
143
147
  const score = similarity(needleInterior, windowInterior);
144
148
  if (score >= FUZZY_MIN_SIMILARITY) {
145
149
  candidates.push({
@@ -166,10 +170,6 @@ function similarity(a, b) {
166
170
  if (a === b) return 1;
167
171
  const max = Math.max(a.length, b.length);
168
172
  if (max === 0) return 1;
169
- const lenDiff = Math.abs(a.length - b.length);
170
- if (lenDiff / max > 1 - FUZZY_MIN_SIMILARITY) {
171
- return 1 - lenDiff / max;
172
- }
173
173
  return 1 - levenshtein(a, b) / max;
174
174
  }
175
175
  function levenshtein(a, b) {
@@ -362,7 +362,7 @@ var editTool = {
362
362
  field: "path"
363
363
  });
364
364
  }
365
- const autoRead = !ctx.hasRead(absPath);
365
+ const autoRead = ctx.hasRead ? !ctx.hasRead(absPath) : false;
366
366
  const original = await fs.readFile(absPath, "utf8");
367
367
  const updated = await fs.stat(absPath);
368
368
  const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
@@ -377,7 +377,7 @@ var editTool = {
377
377
  });
378
378
  }
379
379
  } else {
380
- const lastReadMtime = ctx.lastReadMtime(absPath);
380
+ const lastReadMtime = ctx.lastReadMtime?.(absPath);
381
381
  if (lastReadMtime !== void 0 && updated.mtimeMs > lastReadMtime + mtimeTolerance) {
382
382
  throw new ToolValidationError({
383
383
  message: `edit: file "${input.path}" was modified externally. Re-read it first.`,
@@ -402,7 +402,7 @@ var editTool = {
402
402
  if (!fileLf.includes(oldLf)) {
403
403
  throw noMatchError(input.path, fileLf, oldLf);
404
404
  }
405
- if (autoRead) ctx.recordRead(absPath, updated.mtimeMs, "user", originalHash);
405
+ if (autoRead) ctx.recordRead?.(absPath, updated.mtimeMs, "user", originalHash);
406
406
  return {
407
407
  path: absPath,
408
408
  replacements: 0,
@@ -473,7 +473,7 @@ var editTool = {
473
473
  } catch {
474
474
  }
475
475
  const written = await fs.stat(absPath);
476
- ctx.recordRead(absPath, written.mtimeMs, "write", sha256hex(newFile));
476
+ ctx.recordRead?.(absPath, written.mtimeMs, "write", sha256hex(newFile));
477
477
  ctx.session?.recordFileChange?.({
478
478
  path: absPath,
479
479
  action: "modified",
@@ -518,4 +518,4 @@ Compare this against your old_string and retry with the file's actual text.` : "
518
518
  export {
519
519
  editTool
520
520
  };
521
- //# sourceMappingURL=chunk-Z6IHMJMC.js.map
521
+ //# sourceMappingURL=chunk-DK4J2WMH.js.map
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  getProcessRegistry,
7
7
  redactCommand
8
- } from "./chunk-ESLPBT5A.js";
8
+ } from "./chunk-HDYUBRMF.js";
9
9
  import {
10
10
  buildWin32CmdShimInvocation,
11
11
  resolveWin32Command
@@ -13,6 +13,7 @@ import {
13
13
 
14
14
  // src/_spawn-stream.ts
15
15
  import { spawn } from "node:child_process";
16
+ import { StringDecoder } from "node:string_decoder";
16
17
  import {
17
18
  emitProcessCompleted,
18
19
  emitProcessOutput,
@@ -88,19 +89,23 @@ async function* spawnStream(opts) {
88
89
  child.stderr?.resume();
89
90
  }
90
91
  };
92
+ const stdoutDecoder = new StringDecoder("utf8");
93
+ const stderrDecoder = new StringDecoder("utf8");
91
94
  const onOut = (c) => {
92
- const s = c.toString();
95
+ const s = stdoutDecoder.write(c);
93
96
  stdoutBytes += c.byteLength;
94
97
  emitProcessOutput({ pid, stream: "stdout", chunk: c });
95
- if (stdoutRetainedBytes < max) {
96
- const retained = utf8Prefix(s, max - stdoutRetainedBytes);
97
- stdout += retained;
98
- stdoutRetainedBytes += Buffer.byteLength(retained, "utf8");
98
+ if (s.length > 0) {
99
+ if (stdoutRetainedBytes < max) {
100
+ const retained = utf8Prefix(s, max - stdoutRetainedBytes);
101
+ stdout += retained;
102
+ stdoutRetainedBytes += Buffer.byteLength(retained, "utf8");
103
+ }
104
+ spool.write(s);
105
+ queue.push({ kind: "out", data: s, bytes: c.byteLength });
106
+ queuedBytes += c.byteLength;
107
+ wake();
99
108
  }
100
- spool.write(s);
101
- queue.push({ kind: "out", data: s, bytes: c.byteLength });
102
- queuedBytes += c.byteLength;
103
- wake();
104
109
  if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
105
110
  paused = true;
106
111
  child.stdout?.pause();
@@ -108,18 +113,20 @@ async function* spawnStream(opts) {
108
113
  }
109
114
  };
110
115
  const onErr = (c) => {
111
- const s = c.toString();
116
+ const s = stderrDecoder.write(c);
112
117
  stderrBytes += c.byteLength;
113
118
  emitProcessOutput({ pid, stream: "stderr", chunk: c });
114
- if (stderrRetainedBytes < max) {
115
- const retained = utf8Prefix(s, max - stderrRetainedBytes);
116
- stderr += retained;
117
- stderrRetainedBytes += Buffer.byteLength(retained, "utf8");
119
+ if (s.length > 0) {
120
+ if (stderrRetainedBytes < max) {
121
+ const retained = utf8Prefix(s, max - stderrRetainedBytes);
122
+ stderr += retained;
123
+ stderrRetainedBytes += Buffer.byteLength(retained, "utf8");
124
+ }
125
+ spool.write(s);
126
+ queue.push({ kind: "err", data: s, bytes: c.byteLength });
127
+ queuedBytes += c.byteLength;
128
+ wake();
118
129
  }
119
- spool.write(s);
120
- queue.push({ kind: "err", data: s, bytes: c.byteLength });
121
- queuedBytes += c.byteLength;
122
- wake();
123
130
  if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
124
131
  paused = true;
125
132
  child.stdout?.pause();
@@ -151,6 +158,26 @@ async function* spawnStream(opts) {
151
158
  };
152
159
  child.on("close", (code, signal2) => {
153
160
  if (typeof pid === "number") registry.unregister(pid);
161
+ const restOut = stdoutDecoder.end();
162
+ if (restOut) {
163
+ if (stdoutRetainedBytes < max) {
164
+ const retained = utf8Prefix(restOut, max - stdoutRetainedBytes);
165
+ stdout += retained;
166
+ stdoutRetainedBytes += Buffer.byteLength(retained, "utf8");
167
+ }
168
+ spool.write(restOut);
169
+ queue.push({ kind: "out", data: restOut, bytes: 0 });
170
+ }
171
+ const restErr = stderrDecoder.end();
172
+ if (restErr) {
173
+ if (stderrRetainedBytes < max) {
174
+ const retained = utf8Prefix(restErr, max - stderrRetainedBytes);
175
+ stderr += retained;
176
+ stderrRetainedBytes += Buffer.byteLength(retained, "utf8");
177
+ }
178
+ spool.write(restErr);
179
+ queue.push({ kind: "err", data: restErr, bytes: 0 });
180
+ }
154
181
  const exitCode2 = code ?? (signal2 ? 1 : 0);
155
182
  completeTelemetry(exitCode2, signal2 ?? void 0);
156
183
  queue.push({
@@ -258,4 +285,4 @@ function utf8Prefix(text, maxBytes) {
258
285
  export {
259
286
  spawnStream
260
287
  };
261
- //# sourceMappingURL=chunk-OYP27E3I.js.map
288
+ //# sourceMappingURL=chunk-DRRY6SCM.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtinTools
3
- } from "./chunk-4BABEJC2.js";
3
+ } from "./chunk-IAYMTTR2.js";
4
4
 
5
5
  // src/pack.ts
6
6
  var builtinToolsPack = {
@@ -12,4 +12,4 @@ var builtinToolsPack = {
12
12
  export {
13
13
  builtinToolsPack
14
14
  };
15
- //# sourceMappingURL=chunk-JVRLTEO7.js.map
15
+ //# sourceMappingURL=chunk-E7JE3UEX.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  kanbanTool
3
- } from "./chunk-W2CECGTG.js";
3
+ } from "./chunk-SMARDKAC.js";
4
4
  import {
5
5
  applyManagedKanbanBoardToTodos,
6
6
  blockingTitles,
@@ -323,7 +323,7 @@ var todoTool = {
323
323
  field: "todos"
324
324
  });
325
325
  }
326
- if (item.status !== void 0 && !VALID_STATUSES.has(item.status)) {
326
+ if (!item.status || !VALID_STATUSES.has(item.status)) {
327
327
  throw new ToolValidationError({
328
328
  message: `todo: item "${item.id}" has invalid status "${item.status}". Allowed: pending, in_progress, completed`,
329
329
  field: "todos"
@@ -331,7 +331,7 @@ var todoTool = {
331
331
  }
332
332
  }
333
333
  const items = input.todos.filter((t) => Boolean(t?.id && t.content));
334
- const todoIdentity = (item) => item.kanbanBoardId && item.kanbanTaskId ? `kanban:${item.kanbanBoardId}:${item.kanbanTaskId}` : item.promotedFromTask ? `task:${item.promotedFromTask}` : item.promotedFromPlan ? `plan:${item.promotedFromPlan}` : `todo:${item.id}`;
334
+ const todoIdentity = (item) => item.kanbanBoardId && item.kanbanTaskId ? `kanban:${item.kanbanBoardId}:${item.kanbanTaskId}` : item.promotedFromTask ? `task:${item.promotedFromTask}:${item.id}` : item.promotedFromPlan ? `plan:${item.promotedFromPlan}:${item.id}` : `todo:${item.id}`;
335
335
  const requestedIdentities = new Set(items.map(todoIdentity));
336
336
  for (const previous of ctx.todos ?? []) {
337
337
  const identity = todoIdentity(previous);
@@ -476,4 +476,4 @@ var todoTool = {
476
476
  export {
477
477
  todoTool
478
478
  };
479
- //# sourceMappingURL=chunk-BK732J7I.js.map
479
+ //# sourceMappingURL=chunk-FBUP524S.js.map
@@ -224,4 +224,4 @@ export {
224
224
  searchMemoryTool,
225
225
  relatedMemoryTool
226
226
  };
227
- //# sourceMappingURL=chunk-NYRLWDT5.js.map
227
+ //# sourceMappingURL=chunk-FVM3O4JQ.js.map
@@ -0,0 +1,244 @@
1
+ // src/tool-diff.ts
2
+ var DIFF_MAX_LINES = 5e3;
3
+ function asObject(input) {
4
+ if (typeof input === "string") {
5
+ const s = input.trim();
6
+ if (s.startsWith("{") || s.startsWith("[")) {
7
+ try {
8
+ const parsed = JSON.parse(s);
9
+ if (parsed && typeof parsed === "object") return parsed;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ return null;
15
+ }
16
+ if (input && typeof input === "object") return input;
17
+ return null;
18
+ }
19
+ function diffFromToolInput(toolName, input) {
20
+ if (!toolName) return null;
21
+ const obj = asObject(input);
22
+ if (!obj) return null;
23
+ const name = toolName.toLowerCase();
24
+ const filePath = String(obj.file_path ?? obj.path ?? "");
25
+ switch (name) {
26
+ case "edit":
27
+ case "str_replace":
28
+ case "edit_file":
29
+ case "multi_edit": {
30
+ const oldText = typeof obj.old_string === "string" ? obj.old_string : "";
31
+ const newText = typeof obj.new_string === "string" ? obj.new_string : "";
32
+ if (!oldText && !newText) return null;
33
+ return { mode: "lcs", oldText, newText, caption: `edit ${filePath}`.trim() };
34
+ }
35
+ case "write":
36
+ case "write_file":
37
+ case "create_file": {
38
+ const content = typeof obj.content === "string" ? obj.content : "";
39
+ if (!content) return null;
40
+ return {
41
+ mode: "lcs",
42
+ oldText: "",
43
+ newText: content,
44
+ caption: `write ${filePath} (new)`.trim()
45
+ };
46
+ }
47
+ case "patch": {
48
+ const patchText = typeof obj.patch === "string" ? obj.patch : "";
49
+ if (!patchText.trim()) return null;
50
+ return { mode: "unified", patchText, caption: filePath ? `patch ${filePath}` : "patch" };
51
+ }
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+ function computeLineDiff(oldText, newText) {
57
+ const a = oldText.split("\n").map((l) => l.replace(/\r$/, ""));
58
+ const b = newText.split("\n").map((l) => l.replace(/\r$/, ""));
59
+ if (a.length > DIFF_MAX_LINES || b.length > DIFF_MAX_LINES) return null;
60
+ const n = a.length;
61
+ const m = b.length;
62
+ const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
63
+ for (let i2 = n - 1; i2 >= 0; i2--) {
64
+ for (let j2 = m - 1; j2 >= 0; j2--) {
65
+ dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
66
+ }
67
+ }
68
+ const rows = [];
69
+ let i = 0;
70
+ let j = 0;
71
+ while (i < n && j < m) {
72
+ if (a[i] === b[j]) {
73
+ rows.push({ kind: "ctx", text: a[i] });
74
+ i++;
75
+ j++;
76
+ } else if (dp[i + 1][j] >= dp[i][j + 1]) {
77
+ rows.push({ kind: "del", text: a[i] });
78
+ i++;
79
+ } else {
80
+ rows.push({ kind: "add", text: b[j] });
81
+ j++;
82
+ }
83
+ }
84
+ while (i < n) rows.push({ kind: "del", text: a[i++] });
85
+ while (j < m) rows.push({ kind: "add", text: b[j++] });
86
+ return rows;
87
+ }
88
+ function parseUnifiedDiff(patchText) {
89
+ const rows = [];
90
+ const lines = patchText.split("\n");
91
+ for (const r of lines) {
92
+ const raw = r.replace(/\r$/, "");
93
+ if (raw.startsWith("@@") || raw.startsWith("--- ") || raw.startsWith("+++ ") || raw.startsWith("diff ") || raw.startsWith("index ") || raw.startsWith("\\ ")) {
94
+ rows.push({ kind: "meta", text: raw });
95
+ } else if (raw.startsWith("+")) {
96
+ rows.push({ kind: "add", text: raw.slice(1) });
97
+ } else if (raw.startsWith("-")) {
98
+ rows.push({ kind: "del", text: raw.slice(1) });
99
+ } else {
100
+ rows.push({ kind: "ctx", text: raw.startsWith(" ") ? raw.slice(1) : raw });
101
+ }
102
+ }
103
+ if (rows.length > 0 && rows[rows.length - 1].kind === "ctx" && rows[rows.length - 1].text === "") {
104
+ rows.pop();
105
+ }
106
+ return rows;
107
+ }
108
+ function diffRowsFromToolInput(toolName, input) {
109
+ const d = diffFromToolInput(toolName, input);
110
+ if (!d) return null;
111
+ if (d.mode === "unified") return { caption: d.caption, rows: parseUnifiedDiff(d.patchText) };
112
+ return { caption: d.caption, rows: computeLineDiff(d.oldText, d.newText) };
113
+ }
114
+ var DIFF_LINE_SAFETY_CAP = 4e3;
115
+ function truncMid(s, max) {
116
+ if (s.length <= max) return s;
117
+ return `${s.slice(0, max - 1)}\u2026`;
118
+ }
119
+ function parseUnifiedDiffPreview(diff, maxLines, lineCap = DIFF_LINE_SAFETY_CAP) {
120
+ const all = [];
121
+ let oldLn = 0;
122
+ let newLn = 0;
123
+ for (const raw of diff.split("\n")) {
124
+ const line = raw.replace(/\r$/, "");
125
+ if (line.startsWith("+++") || line.startsWith("---")) continue;
126
+ if (line.startsWith("diff --git") || line.startsWith("index ")) continue;
127
+ if (line.startsWith("@@")) {
128
+ const m = line.match(/^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
129
+ if (m) {
130
+ oldLn = Number.parseInt(m[1] ?? "0", 10) || 0;
131
+ newLn = Number.parseInt(m[2] ?? "0", 10) || 0;
132
+ }
133
+ all.push({ kind: "hunk", text: truncMid(line, 60) });
134
+ continue;
135
+ }
136
+ if (line.startsWith("+")) {
137
+ all.push({ kind: "add", text: truncMid(line, lineCap), newLine: newLn });
138
+ newLn++;
139
+ continue;
140
+ }
141
+ if (line.startsWith("-")) {
142
+ all.push({ kind: "del", text: truncMid(line, lineCap), oldLine: oldLn });
143
+ oldLn++;
144
+ continue;
145
+ }
146
+ if (line.startsWith("\\ No newline")) {
147
+ all.push({ kind: "meta", text: line });
148
+ continue;
149
+ }
150
+ if (line.length === 0) continue;
151
+ all.push({ kind: "ctx", text: truncMid(line, lineCap), oldLine: oldLn, newLine: newLn });
152
+ oldLn++;
153
+ newLn++;
154
+ }
155
+ const added = all.filter((row) => row.kind === "add").length;
156
+ const removed = all.filter((row) => row.kind === "del").length;
157
+ if (all.length === 0) {
158
+ return { rows: [], hidden: 0, added: 0, removed: 0, hiddenAdded: 0, hiddenRemoved: 0 };
159
+ }
160
+ if (all.length <= maxLines) {
161
+ return { rows: all, hidden: 0, added, removed, hiddenAdded: 0, hiddenRemoved: 0 };
162
+ }
163
+ const rows = all.slice(0, maxLines);
164
+ const hiddenRows = all.slice(maxLines);
165
+ return {
166
+ rows,
167
+ hidden: hiddenRows.length,
168
+ added,
169
+ removed,
170
+ hiddenAdded: hiddenRows.filter((row) => row.kind === "add").length,
171
+ hiddenRemoved: hiddenRows.filter((row) => row.kind === "del").length
172
+ };
173
+ }
174
+ var TOOL_DIFF_BROWSER_SRC = [
175
+ "var DIFF_MAX_LINES = 5000;",
176
+ "function __diffObj(input){",
177
+ ' if(typeof input==="string"){ var s=input.trim(); if(s.charAt(0)==="{"||s.charAt(0)==="["){ try{ var p=JSON.parse(s); if(p&&typeof p==="object") return p; }catch(_e){} } return null; }',
178
+ ' if(input&&typeof input==="object") return input; return null;',
179
+ "}",
180
+ "function diffFromToolInput(name, input){",
181
+ " if(!name) return null;",
182
+ " var obj=__diffObj(input); if(!obj) return null;",
183
+ " var n=String(name).toLowerCase();",
184
+ ' var fp=String(obj.file_path!=null?obj.file_path:(obj.path!=null?obj.path:""));',
185
+ ' if(n==="edit"||n==="str_replace"||n==="edit_file"||n==="multi_edit"){',
186
+ ' var oldT=typeof obj.old_string==="string"?obj.old_string:"";',
187
+ ' var newT=typeof obj.new_string==="string"?obj.new_string:"";',
188
+ " if(!oldT&&!newT) return null;",
189
+ ' return { mode:"lcs", oldText:oldT, newText:newT, caption:("edit "+fp).replace(/\\s+$/,"") };',
190
+ " }",
191
+ ' if(n==="write"||n==="write_file"||n==="create_file"){',
192
+ ' var c=typeof obj.content==="string"?obj.content:"";',
193
+ " if(!c) return null;",
194
+ ' return { mode:"lcs", oldText:"", newText:c, caption:("write "+fp+" (new)").replace(/^\\s+/,"").replace(/\\s+$/,"") };',
195
+ " }",
196
+ ' if(n==="patch"){',
197
+ ' var pt=typeof obj.patch==="string"?obj.patch:"";',
198
+ ' if(!pt.replace(/\\s/g,"")) return null;',
199
+ ' return { mode:"unified", patchText:pt, caption: fp ? ("patch "+fp) : "patch" };',
200
+ " }",
201
+ " return null;",
202
+ "}",
203
+ "function computeLineDiff(oldText, newText){",
204
+ ' var a=oldText.split("\\n").map(function(l){return l.replace(/\\r$/,"");}), b=newText.split("\\n").map(function(l){return l.replace(/\\r$/,"");});',
205
+ " if(a.length>DIFF_MAX_LINES||b.length>DIFF_MAX_LINES) return null;",
206
+ " var nn=a.length, mm=b.length, i, j;",
207
+ " var dp=new Array(nn+1); for(i=0;i<=nn;i++){ dp[i]=new Array(mm+1); for(j=0;j<=mm;j++) dp[i][j]=0; }",
208
+ " for(i=nn-1;i>=0;i--){ for(j=mm-1;j>=0;j--){ dp[i][j]= a[i]===b[j] ? dp[i+1][j+1]+1 : Math.max(dp[i+1][j], dp[i][j+1]); } }",
209
+ " var rows=[]; i=0; j=0;",
210
+ ' while(i<nn&&j<mm){ if(a[i]===b[j]){ rows.push({kind:"ctx",text:a[i]}); i++; j++; } else if(dp[i+1][j]>=dp[i][j+1]){ rows.push({kind:"del",text:a[i]}); i++; } else { rows.push({kind:"add",text:b[j]}); j++; } }',
211
+ ' while(i<nn){ rows.push({kind:"del",text:a[i]}); i++; }',
212
+ ' while(j<mm){ rows.push({kind:"add",text:b[j]}); j++; }',
213
+ " return rows;",
214
+ "}",
215
+ "function parseUnifiedDiff(patchText){",
216
+ ' var rows=[], lines=patchText.split("\\n"), k, raw;',
217
+ ' for(k=0;k<lines.length;k++){ raw=lines[k].replace(/\\r$/,"");',
218
+ ' if(raw.indexOf("@@")===0||raw.indexOf("--- ")===0||raw.indexOf("+++ ")===0||raw.indexOf("diff ")===0||raw.indexOf("index ")===0||raw.indexOf("\\\\ ")===0){ rows.push({kind:"meta",text:raw}); }',
219
+ ' else if(raw.charAt(0)==="+"){ rows.push({kind:"add",text:raw.slice(1)}); }',
220
+ ' else if(raw.charAt(0)==="-"){ rows.push({kind:"del",text:raw.slice(1)}); }',
221
+ ' else { rows.push({kind:"ctx",text: raw.charAt(0)===" " ? raw.slice(1) : raw}); }',
222
+ " }",
223
+ ' if(rows.length>0 && rows[rows.length-1].kind==="ctx" && rows[rows.length-1].text===""){ rows.pop(); }',
224
+ " return rows;",
225
+ "}",
226
+ "function diffRowsFromToolInput(name, input){",
227
+ " var d=diffFromToolInput(name, input); if(!d) return null;",
228
+ ' if(d.mode==="unified") return { caption:d.caption, rows:parseUnifiedDiff(d.patchText) };',
229
+ " return { caption:d.caption, rows:computeLineDiff(d.oldText, d.newText) };",
230
+ "}"
231
+ ].join("\n");
232
+
233
+ export {
234
+ DIFF_MAX_LINES,
235
+ diffFromToolInput,
236
+ computeLineDiff,
237
+ parseUnifiedDiff,
238
+ diffRowsFromToolInput,
239
+ DIFF_LINE_SAFETY_CAP,
240
+ truncMid,
241
+ parseUnifiedDiffPreview,
242
+ TOOL_DIFF_BROWSER_SRC
243
+ };
244
+ //# sourceMappingURL=chunk-FYDBUVQD.js.map