@wrongstack/tools 1.0.7 → 1.0.9

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 (135) hide show
  1. package/dist/_win32-resolve.d.ts +5 -0
  2. package/dist/audit.js +5 -5
  3. package/dist/bash.js +2 -2
  4. package/dist/browser/index.js +1 -1
  5. package/dist/builtin.js +41 -41
  6. package/dist/{chunk-N3OTTQNC.js → chunk-2OCAAFAT.js} +44 -25
  7. package/dist/{chunk-5MP76C72.js → chunk-2UVAIS5O.js} +24 -29
  8. package/dist/{chunk-SIIOHBNY.js → chunk-3RRK33YM.js} +2 -2
  9. package/dist/{chunk-M2W36JT7.js → chunk-4CE3RKEP.js} +6 -3
  10. package/dist/{chunk-QJARSK2G.js → chunk-4SUGLJLO.js} +11 -3
  11. package/dist/{chunk-AH7J5HWL.js → chunk-5RBUYFRV.js} +15 -7
  12. package/dist/{chunk-WKLUOXR4.js → chunk-7GOE2ZTZ.js} +2 -2
  13. package/dist/{chunk-O6AGFJBO.js → chunk-7V33PHT6.js} +49 -77
  14. package/dist/{chunk-CAF4L6KN.js → chunk-C3WP6ZOT.js} +36 -48
  15. package/dist/{chunk-KMXPP32M.js → chunk-C6ZOXNXV.js} +13 -3
  16. package/dist/{chunk-EGT5QLCB.js → chunk-D6RQZMZS.js} +123 -255
  17. package/dist/{chunk-S6CGSFLX.js → chunk-DBG7UQGK.js} +10 -3
  18. package/dist/{chunk-2W4XTULI.js → chunk-DLZCOEVG.js} +50 -8
  19. package/dist/{chunk-NL45OWEO.js → chunk-DOAHONOS.js} +7 -12
  20. package/dist/{chunk-3LQJXJDM.js → chunk-E7A4SLKR.js} +2 -2
  21. package/dist/{chunk-JB5JLY7V.js → chunk-ENE22I5W.js} +387 -394
  22. package/dist/{chunk-SEONDNLN.js → chunk-G7CYW5EP.js} +2 -2
  23. package/dist/{chunk-YPSIG23D.js → chunk-HV7RWDCL.js} +3 -3
  24. package/dist/{chunk-TU6UVATU.js → chunk-HYHE4VNA.js} +3 -3
  25. package/dist/{chunk-HIRVCXLP.js → chunk-IE4KDAOB.js} +3 -3
  26. package/dist/{chunk-YA2ZYCJY.js → chunk-JPK2HHBK.js} +1 -1
  27. package/dist/{chunk-7E55DAFW.js → chunk-KHUCKLOR.js} +12 -7
  28. package/dist/{chunk-R3HIZVFS.js → chunk-KPDCKSN5.js} +23 -3
  29. package/dist/{chunk-B3MGJBFI.js → chunk-KS2OEZWV.js} +23 -47
  30. package/dist/{chunk-UI3ZJIG3.js → chunk-LKLIQUCW.js} +14 -8
  31. package/dist/{chunk-62OAECEQ.js → chunk-LLSFOUZF.js} +8 -8
  32. package/dist/{chunk-YZQSUL5G.js → chunk-M4ZHHCCW.js} +14 -6
  33. package/dist/{chunk-IXIS7RSG.js → chunk-MRXWOOOR.js} +29 -47
  34. package/dist/{chunk-C4DSQBTT.js → chunk-NEYUPZL6.js} +3 -3
  35. package/dist/{chunk-6XYFYLN7.js → chunk-O2MHULBO.js} +13 -8
  36. package/dist/{chunk-B62LKMWQ.js → chunk-OKZJJDOG.js} +86 -188
  37. package/dist/{chunk-JKFIDWQG.js → chunk-ON6ET5ER.js} +52 -9
  38. package/dist/{chunk-KF7GMVNU.js → chunk-OUERRFEM.js} +32 -11
  39. package/dist/{chunk-4DK5WZGR.js → chunk-OXO773CW.js} +127 -196
  40. package/dist/{chunk-GF4JHLCO.js → chunk-PWZUHZP7.js} +2 -2
  41. package/dist/{chunk-CLJXKFVS.js → chunk-QA37PFYL.js} +29 -68
  42. package/dist/{chunk-A3LNNBYO.js → chunk-QAWI3RSK.js} +41 -83
  43. package/dist/{chunk-ZMCV3XOV.js → chunk-SPNXDTCJ.js} +29 -63
  44. package/dist/{chunk-H2VB7GCR.js → chunk-ULI2J6TX.js} +27 -14
  45. package/dist/{chunk-EMA2XENR.js → chunk-UVYRQKPZ.js} +4 -3
  46. package/dist/{chunk-2NTHMZV3.js → chunk-VMJY6FYJ.js} +693 -276
  47. package/dist/{chunk-KPR53VKZ.js → chunk-WEUEU53R.js} +2 -2
  48. package/dist/{chunk-ZOUG3KKS.js → chunk-XPVYZ2S6.js} +2 -2
  49. package/dist/{chunk-5U5ULQX2.js → chunk-XZLVJD56.js} +5 -1
  50. package/dist/{chunk-HCPSVK7J.js → chunk-YKSOSI2N.js} +47 -31
  51. package/dist/codebase-index/codebase-ast-replace-tool.d.ts +5 -6
  52. package/dist/codebase-index/codebase-context-tool.d.ts +4 -6
  53. package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +1 -2
  54. package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +0 -2
  55. package/dist/codebase-index/codebase-index-tool.d.ts +0 -2
  56. package/dist/codebase-index/codebase-invariant-check-tool.d.ts +1 -2
  57. package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +0 -2
  58. package/dist/codebase-index/codebase-repo-map-tool.d.ts +1 -2
  59. package/dist/codebase-index/codebase-search-tool.d.ts +1 -1
  60. package/dist/codebase-index/codebase-targeted-test-tool.d.ts +1 -2
  61. package/dist/codebase-index/index.js +8 -8
  62. package/dist/codebase-index/parser-worker-script.js +3 -3
  63. package/dist/codebase-index/project-server.js +5 -5
  64. package/dist/codebase-index/worker.js +4 -4
  65. package/dist/design.js +1 -1
  66. package/dist/diff.js +1 -1
  67. package/dist/edit.js +7 -7
  68. package/dist/exec.d.ts +7 -7
  69. package/dist/exec.js +2 -2
  70. package/dist/format.js +5 -5
  71. package/dist/git.js +1 -1
  72. package/dist/glob.js +1 -1
  73. package/dist/{go-parser-VDPRQEGM.js → go-parser-Z4H6SR3R.js} +6 -3
  74. package/dist/grep.js +1 -1
  75. package/dist/index.js +45 -45
  76. package/dist/install.js +5 -5
  77. package/dist/json.d.ts +0 -1
  78. package/dist/json.js +1 -1
  79. package/dist/kanban-presence.d.ts +6 -0
  80. package/dist/kanban-serializer.d.ts +9 -1
  81. package/dist/kanban-split-task-handler.d.ts +1 -1
  82. package/dist/kanban-tool-results.d.ts +57 -2
  83. package/dist/kanban-tool-types.d.ts +16 -5
  84. package/dist/kanban.d.ts +14 -0
  85. package/dist/kanban.js +9 -1
  86. package/dist/languages/index.js +3 -3
  87. package/dist/{legacy-bridge-4T4OTSQP.js → legacy-bridge-BPT2S2F4.js} +5 -5
  88. package/dist/lint.js +5 -5
  89. package/dist/logs.js +1 -1
  90. package/dist/mode.d.ts +3 -2
  91. package/dist/mode.js +1 -1
  92. package/dist/outdated.js +2 -2
  93. package/dist/pack.js +42 -42
  94. package/dist/{parser-dispatch-4E2SDQUO.js → parser-dispatch-WG4DNBHH.js} +4 -4
  95. package/dist/patch.js +7 -7
  96. package/dist/plan.d.ts +2 -1
  97. package/dist/plan.js +3 -3
  98. package/dist/{py-parser-MAW6DHFE.js → py-parser-4PFZYAJ7.js} +3 -3
  99. package/dist/read.js +7 -7
  100. package/dist/replace.js +7 -7
  101. package/dist/search.d.ts +0 -6
  102. package/dist/search.js +1 -1
  103. package/dist/security-ast-scan-tool.d.ts +1 -2
  104. package/dist/session-shell.js +2 -2
  105. package/dist/task.d.ts +2 -1
  106. package/dist/task.js +3 -3
  107. package/dist/test.d.ts +8 -1
  108. package/dist/test.js +5 -5
  109. package/dist/todo.js +2 -2
  110. package/dist/tool-search.d.ts +2 -0
  111. package/dist/tool-search.js +1 -1
  112. package/dist/tool-tier.js +43 -43
  113. package/dist/tool-use.d.ts +2 -2
  114. package/dist/tool-use.js +1 -1
  115. package/dist/tree.js +1 -1
  116. package/dist/typecheck.js +5 -5
  117. package/dist/wasm/bash/tree-sitter-bash.wasm +0 -0
  118. package/dist/wasm/c/tree-sitter-c.wasm +0 -0
  119. package/dist/wasm/c_sharp/tree-sitter-c_sharp.wasm +0 -0
  120. package/dist/wasm/checksums.json +16 -0
  121. package/dist/wasm/cpp/tree-sitter-cpp.wasm +0 -0
  122. package/dist/wasm/elixir/tree-sitter-elixir.wasm +0 -0
  123. package/dist/wasm/go/tree-sitter-go.wasm +0 -0
  124. package/dist/wasm/java/tree-sitter-java.wasm +0 -0
  125. package/dist/wasm/kotlin/tree-sitter-kotlin.wasm +0 -0
  126. package/dist/wasm/php/tree-sitter-php.wasm +0 -0
  127. package/dist/wasm/python/tree-sitter-python.wasm +0 -0
  128. package/dist/wasm/ruby/tree-sitter-ruby.wasm +0 -0
  129. package/dist/wasm/rust/tree-sitter-rust.wasm +0 -0
  130. package/dist/wasm/swift/tree-sitter-swift.wasm +0 -0
  131. package/dist/wasm/tree-sitter-runtime.wasm +0 -0
  132. package/dist/win32.d.ts +1 -1
  133. package/dist/win32.js +3 -1
  134. package/dist/write.js +7 -7
  135. package/package.json +10 -10
@@ -8,7 +8,8 @@ import {
8
8
 
9
9
  // src/json.ts
10
10
  import * as fs from "node:fs/promises";
11
- import { deepMerge as deepMergeCore } from "@wrongstack/core/utils";
11
+ import { deepMerge as deepMergeCore, toErrorMessage } from "@wrongstack/core/utils";
12
+ import { ToolValidationError } from "@wrongstack/core/types";
12
13
  var MAX_JSON_FILE_BYTES = 16 * 1024 * 1024;
13
14
  var MAX_JSON_FILE_BYTES_HUMAN = "16 MiB";
14
15
  var JsonFileTooLargeError = class extends Error {
@@ -39,8 +40,8 @@ async function readJsonFileBounded(filePath, ctx) {
39
40
  var jsonTool = {
40
41
  name: "json",
41
42
  category: "Data",
42
- description: "Parse, pretty-print, query, validate, transform, and merge JSON/JSON5/YAML (read-only \u2014 does not write files). Use `action` to select the operation: parse (default), query, validate, transform, or merge.",
43
- usageHint: 'VERY USEFUL FOR DATA INSPECTION:\n\n- `action: "parse"` (default): read/pretty-print/convert JSON, JSON5, or YAML from `file` or `data`.\n- `action: "query"`: JMESPath-like query (`a.b[0].c`, `items[*].name`, filters, functions).\n- `action: "validate"`: validate data against a JSON Schema (`schema` param).\n- `action: "transform"`: chain multiple JMESPath transforms (`transforms` param).\n- `action: "merge"`: deep merge `base` and `patch` objects (`conflictResolution` param).\nPrefer this over raw `read` + manual parsing when dealing with configuration or data files.',
43
+ description: "Parse, pretty-print, query, validate, transform, and merge JSON (read-only \u2014 does not write files). Input must be strict JSON; results can be rendered as JSON, JSON5-style, or YAML via `format`. Use `action` to select the operation: parse (default), query, validate, transform, or merge.",
44
+ usageHint: 'VERY USEFUL FOR DATA INSPECTION:\n\n- `action: "parse"` (default): read/pretty-print JSON from `file` or `data` (output as JSON, JSON5-style, or YAML via `format`).\n- `action: "query"`: JMESPath-like query (`a.b[0].c`, `items[*].name`, filters, functions).\n- `action: "validate"`: validate data against a JSON Schema (`schema` param).\n- `action: "transform"`: chain multiple JMESPath transforms (`transforms` param).\n- `action: "merge"`: deep merge `base` and `patch` objects (`conflictResolution` param).\nPrefer this over raw `read` + manual parsing when dealing with configuration or data files.',
44
45
  permission: "auto",
45
46
  mutating: false,
46
47
  maxOutputBytes: 262144,
@@ -55,10 +56,10 @@ var jsonTool = {
55
56
  enum: ["parse", "query", "validate", "transform", "merge"],
56
57
  description: "Operation (default: parse). parse=read/pretty-print, query=JMESPath, validate=schema, transform=chained queries, merge=deep merge."
57
58
  },
58
- file: { type: "string", description: "Path to JSON/JSON5/YAML file (parse/query/validate)" },
59
+ file: { type: "string", description: "Path to a JSON file (parse/query/validate/transform)" },
59
60
  data: {
60
61
  type: "string",
61
- description: "JSON/JSON5/YAML string (parse/query/validate, alternative to file)"
62
+ description: "JSON string (parse/query/validate/transform, alternative to file)"
62
63
  },
63
64
  format: {
64
65
  type: "string",
@@ -103,13 +104,10 @@ var jsonTool = {
103
104
  "merge"
104
105
  ]);
105
106
  if (input.action !== void 0 && !ALLOWED_ACTIONS.has(input.action)) {
106
- return {
107
- data: null,
108
- formatted: "",
109
- type: "unknown",
110
- action: String(input.action),
111
- error: `Unknown action "${input.action}". Allowed actions: parse, query, validate, transform, merge`
112
- };
107
+ throw new ToolValidationError({
108
+ message: `json: unknown action "${input.action}". Allowed actions: parse, query, validate, transform, merge`,
109
+ field: "action"
110
+ });
113
111
  }
114
112
  switch (action) {
115
113
  case "query":
@@ -125,54 +123,61 @@ var jsonTool = {
125
123
  }
126
124
  }
127
125
  };
126
+ async function readSource(input, ctx, action) {
127
+ if (input.file) {
128
+ try {
129
+ return await readJsonFileBounded(input.file, ctx);
130
+ } catch (error) {
131
+ if (error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError) {
132
+ throw error;
133
+ }
134
+ throw new Error(`json: could not read file "${input.file}": ${toErrorMessage(error)}`, {
135
+ cause: error
136
+ });
137
+ }
138
+ }
139
+ if (input.data) return input.data;
140
+ throw new ToolValidationError({
141
+ message: `json: provide \`file\` or \`data\` for action: ${action}`,
142
+ field: "file"
143
+ });
144
+ }
145
+ function parseSource(raw, input) {
146
+ try {
147
+ return JSON.parse(raw);
148
+ } catch (error) {
149
+ const message = `json: parse failed${input.file ? ` for "${input.file}"` : ""}: ${toErrorMessage(error)}`;
150
+ if (input.file) throw new Error(message, { cause: error });
151
+ throw new ToolValidationError({ message, field: "data", cause: error });
152
+ }
153
+ }
128
154
  async function executeParse(input, ctx) {
129
155
  const format = input.format ?? "json";
130
- let parsed;
131
- let raw;
132
- if (input.file) {
156
+ const raw = await readSource(input, ctx, "parse");
157
+ if (input.validate) {
158
+ let checked;
133
159
  try {
134
- raw = await readJsonFileBounded(input.file, ctx);
160
+ checked = JSON.parse(raw);
135
161
  } catch (error) {
136
162
  return {
137
163
  data: null,
138
- formatted: "",
164
+ formatted: "invalid",
139
165
  type: "unknown",
140
166
  action: "parse",
141
- error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read file"
167
+ valid: false,
168
+ errors: [`Parse failed: ${toErrorMessage(error)}`]
142
169
  };
143
170
  }
144
- } else if (input.data) {
145
- raw = input.data;
146
- } else {
147
171
  return {
148
- data: null,
149
- formatted: "",
150
- type: "unknown",
151
- action: "parse",
152
- error: "Provide file or data"
153
- };
154
- }
155
- try {
156
- parsed = JSON.parse(raw);
157
- } catch (e) {
158
- return {
159
- data: null,
160
- formatted: "",
161
- type: "unknown",
162
- action: "parse",
163
- /* v8 ignore next -- JSON.parse only throws SyntaxError (an Error); the String(e) side is defensive. */
164
- error: `Parse failed: ${e instanceof Error ? e.message : String(e)}`
165
- };
166
- }
167
- if (input.validate) {
168
- return {
169
- data: parsed,
172
+ data: checked,
170
173
  formatted: "valid",
171
- type: Array.isArray(parsed) ? "array" : typeof parsed,
174
+ type: Array.isArray(checked) ? "array" : typeof checked,
172
175
  action: "parse",
173
- keys: typeof parsed === "object" && parsed !== null ? Object.keys(parsed) : void 0
176
+ valid: true,
177
+ keys: typeof checked === "object" && checked !== null ? Object.keys(checked) : void 0
174
178
  };
175
179
  }
180
+ const parsed = parseSource(raw, input);
176
181
  if (input.query) {
177
182
  const queryResult = simpleQuery(parsed, input.query);
178
183
  const formatted2 = formatOutput(queryResult, format);
@@ -196,239 +201,102 @@ async function executeParse(input, ctx) {
196
201
  }
197
202
  async function executeQuery(input, ctx) {
198
203
  if (!input.query) {
199
- return {
200
- data: null,
201
- formatted: "",
202
- type: "unknown",
203
- action: "query",
204
- error: "query is required for action: query"
205
- };
206
- }
207
- let parsed;
208
- if (input.file) {
209
- try {
210
- const raw = await readJsonFileBounded(input.file, ctx);
211
- parsed = JSON.parse(raw);
212
- } catch (error) {
213
- return {
214
- data: null,
215
- formatted: "",
216
- type: "unknown",
217
- action: "query",
218
- error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
219
- };
220
- }
221
- } else if (input.data) {
222
- try {
223
- parsed = JSON.parse(input.data);
224
- } catch {
225
- return {
226
- data: null,
227
- formatted: "",
228
- type: "unknown",
229
- action: "query",
230
- error: "Could not parse data string"
231
- };
232
- }
233
- } else {
234
- return {
235
- data: null,
236
- formatted: "",
237
- type: "unknown",
238
- action: "query",
239
- error: "Provide file or data"
240
- };
204
+ throw new ToolValidationError({
205
+ message: "json: query is required for action: query",
206
+ field: "query"
207
+ });
241
208
  }
209
+ const parsed = parseSource(await readSource(input, ctx, "query"), input);
210
+ let result;
242
211
  try {
243
- const result = jmespathSearch(parsed, input.query);
244
- const format = input.format ?? "json";
245
- return {
246
- data: parsed,
247
- formatted: formatOutput(result, format),
248
- type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result,
249
- action: "query",
250
- query_result: result
251
- };
252
- } catch (e) {
253
- return {
254
- data: null,
255
- formatted: "",
256
- type: "unknown",
257
- action: "query",
258
- /* v8 ignore next -- defensive String(e) */
259
- error: `Query failed: ${e instanceof Error ? e.message : String(e)}`
260
- };
212
+ result = jmespathSearch(parsed, input.query);
213
+ } catch (error) {
214
+ throw new Error(`json: query failed: ${toErrorMessage(error)}`, { cause: error });
261
215
  }
216
+ const format = input.format ?? "json";
217
+ return {
218
+ data: parsed,
219
+ formatted: formatOutput(result, format),
220
+ type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result,
221
+ action: "query",
222
+ query_result: result
223
+ };
262
224
  }
263
225
  async function executeValidate(input, ctx) {
264
226
  if (!input.schema) {
265
- return {
266
- data: null,
267
- formatted: "",
268
- type: "unknown",
269
- action: "validate",
270
- error: "schema is required for action: validate"
271
- };
272
- }
273
- let parsed;
274
- if (input.file) {
275
- try {
276
- const raw = await readJsonFileBounded(input.file, ctx);
277
- parsed = JSON.parse(raw);
278
- } catch (error) {
279
- return {
280
- data: null,
281
- formatted: "",
282
- type: "unknown",
283
- action: "validate",
284
- error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
285
- };
286
- }
287
- } else if (input.data) {
288
- try {
289
- parsed = JSON.parse(input.data);
290
- } catch {
291
- return {
292
- data: null,
293
- formatted: "",
294
- type: "unknown",
295
- action: "validate",
296
- error: "Could not parse data string"
297
- };
298
- }
299
- } else {
300
- return {
301
- data: null,
302
- formatted: "",
303
- type: "unknown",
304
- action: "validate",
305
- error: "Provide file or data"
306
- };
227
+ throw new ToolValidationError({
228
+ message: "json: schema is required for action: validate",
229
+ field: "schema"
230
+ });
307
231
  }
232
+ const parsed = parseSource(await readSource(input, ctx, "validate"), input);
233
+ let outcome;
308
234
  try {
309
- const { valid, errors } = validateJsonSchema(parsed, input.schema);
310
- return {
311
- data: parsed,
312
- formatted: valid ? "valid" : "invalid",
313
- type: Array.isArray(parsed) ? "array" : typeof parsed,
314
- action: "validate",
315
- valid,
316
- errors
317
- };
318
- } catch (e) {
319
- return {
320
- data: null,
321
- formatted: "",
322
- type: "unknown",
323
- action: "validate",
324
- /* v8 ignore next -- defensive String(e) */
325
- error: `Validation failed: ${e instanceof Error ? e.message : String(e)}`
326
- };
235
+ outcome = validateJsonSchema(parsed, input.schema);
236
+ } catch (error) {
237
+ throw new Error(`json: schema validation failed to run: ${toErrorMessage(error)}`, {
238
+ cause: error
239
+ });
327
240
  }
241
+ return {
242
+ data: parsed,
243
+ formatted: outcome.valid ? "valid" : "invalid",
244
+ type: Array.isArray(parsed) ? "array" : typeof parsed,
245
+ action: "validate",
246
+ valid: outcome.valid,
247
+ errors: outcome.errors
248
+ };
328
249
  }
329
250
  async function executeTransform(input, ctx) {
330
251
  if (!input.transforms || input.transforms.length === 0) {
331
- return {
332
- data: null,
333
- formatted: "",
334
- type: "unknown",
335
- action: "transform",
336
- error: "transforms array is required for action: transform"
337
- };
252
+ throw new ToolValidationError({
253
+ message: "json: transforms array is required for action: transform",
254
+ field: "transforms"
255
+ });
338
256
  }
339
- let parsed;
340
- if (input.file) {
341
- try {
342
- const raw = await readJsonFileBounded(input.file, ctx);
343
- parsed = JSON.parse(raw);
344
- } catch (error) {
345
- return {
346
- data: null,
347
- formatted: "",
348
- type: "unknown",
349
- action: "transform",
350
- error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
351
- };
352
- }
353
- } else if (input.data) {
257
+ const parsed = parseSource(await readSource(input, ctx, "transform"), input);
258
+ let current = parsed;
259
+ const steps = [];
260
+ for (const t of input.transforms) {
354
261
  try {
355
- parsed = JSON.parse(input.data);
356
- } catch {
357
- return {
358
- data: null,
359
- formatted: "",
360
- type: "unknown",
361
- action: "transform",
362
- error: "Could not parse data string"
363
- };
364
- }
365
- } else {
366
- return {
367
- data: null,
368
- formatted: "",
369
- type: "unknown",
370
- action: "transform",
371
- error: "Provide file or data"
372
- };
373
- }
374
- try {
375
- let current = parsed;
376
- const steps = [];
377
- for (const t of input.transforms) {
378
262
  current = jmespathSearch(current, t);
379
- steps.push({ transform: t, result: current });
263
+ } catch (error) {
264
+ throw new Error(`json: transform "${t}" failed: ${toErrorMessage(error)}`, { cause: error });
380
265
  }
381
- const format = input.format ?? "json";
382
- return {
383
- data: parsed,
384
- formatted: formatOutput(current, format),
385
- type: current === null ? "null" : Array.isArray(current) ? "array" : typeof current,
386
- action: "transform",
387
- result: current,
388
- steps
389
- };
390
- } catch (e) {
391
- return {
392
- data: null,
393
- formatted: "",
394
- type: "unknown",
395
- action: "transform",
396
- /* v8 ignore next -- defensive String(e) */
397
- error: `Transform failed: ${e instanceof Error ? e.message : String(e)}`
398
- };
266
+ steps.push({ transform: t, result: current });
399
267
  }
268
+ const format = input.format ?? "json";
269
+ return {
270
+ data: parsed,
271
+ formatted: formatOutput(current, format),
272
+ type: current === null ? "null" : Array.isArray(current) ? "array" : typeof current,
273
+ action: "transform",
274
+ result: current,
275
+ steps
276
+ };
400
277
  }
401
278
  async function executeMerge(input) {
402
279
  if (input.base === void 0 || input.patch === void 0) {
403
- return {
404
- data: null,
405
- formatted: "",
406
- type: "unknown",
407
- action: "merge",
408
- error: "base and patch are required for action: merge"
409
- };
280
+ throw new ToolValidationError({
281
+ message: "json: base and patch are required for action: merge",
282
+ field: input.base === void 0 ? "base" : "patch"
283
+ });
410
284
  }
411
285
  const conflictResolution = input.conflictResolution ?? "prefer-patch";
286
+ let result;
412
287
  try {
413
- const result = deepMergeCore(input.base, input.patch, { conflictResolution });
414
- const format = input.format ?? "json";
415
- return {
416
- data: result,
417
- formatted: formatOutput(result, format),
418
- type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result,
419
- action: "merge",
420
- result
421
- };
422
- } catch (e) {
423
- return {
424
- data: null,
425
- formatted: "",
426
- type: "unknown",
427
- action: "merge",
428
- /* v8 ignore next -- defensive String(e) */
429
- error: `Merge failed: ${e instanceof Error ? e.message : String(e)}`
430
- };
288
+ result = deepMergeCore(input.base, input.patch, { conflictResolution });
289
+ } catch (error) {
290
+ throw new Error(`json: merge failed: ${toErrorMessage(error)}`, { cause: error });
431
291
  }
292
+ const format = input.format ?? "json";
293
+ return {
294
+ data: result,
295
+ formatted: formatOutput(result, format),
296
+ type: result === null ? "null" : Array.isArray(result) ? "array" : typeof result,
297
+ action: "merge",
298
+ result
299
+ };
432
300
  }
433
301
  function jmespathSearch(data, query) {
434
302
  if (!query || query === "@") return data;
@@ -704,4 +572,4 @@ ${toYaml(v, indent + 1)}`;
704
572
  export {
705
573
  jsonTool
706
574
  };
707
- //# sourceMappingURL=chunk-EGT5QLCB.js.map
575
+ //# sourceMappingURL=chunk-D6RQZMZS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  kanbanTool
3
- } from "./chunk-2NTHMZV3.js";
3
+ } from "./chunk-VMJY6FYJ.js";
4
4
  import {
5
5
  applyManagedKanbanBoardToTodos,
6
6
  blockingTitles,
@@ -118,7 +118,14 @@ async function synchronizeManagedKanban(items, board, ctx, signal) {
118
118
  const warnings = [];
119
119
  const actor = ctx.agentId?.trim() || ctx.agentName?.trim() || "kanban-agent";
120
120
  const execute = async (input) => {
121
- const result = await kanbanTool.execute(input, ctx, { signal });
121
+ let result;
122
+ try {
123
+ result = await kanbanTool.execute(input, ctx, { signal });
124
+ } catch (err) {
125
+ const message = err instanceof Error ? err.message : String(err);
126
+ warnings.push(message);
127
+ return { ok: false, message };
128
+ }
122
129
  if (!result.ok) warnings.push(result.message);
123
130
  else {
124
131
  synced++;
@@ -487,4 +494,4 @@ var todoTool = {
487
494
  export {
488
495
  todoTool
489
496
  };
490
- //# sourceMappingURL=chunk-S6CGSFLX.js.map
497
+ //# sourceMappingURL=chunk-DBG7UQGK.js.map
@@ -18,6 +18,7 @@ import {
18
18
  import { spawn } from "node:child_process";
19
19
  import * as fs from "node:fs/promises";
20
20
  import * as path from "node:path";
21
+ import { StringDecoder } from "node:string_decoder";
21
22
  import { ToolValidationError } from "@wrongstack/core/types";
22
23
  import {
23
24
  buildChildEnv,
@@ -31,6 +32,13 @@ var NATIVE_READ_CHUNK_BYTES = 64 * 1024;
31
32
  var NATIVE_MAX_FILE_BYTES = 1e6;
32
33
  var RG_MAX_QUEUE_CHUNKS = 128;
33
34
  var RG_MAX_QUEUE_CHARS = 8 * 1024 * 1024;
35
+ function isInsideDefaultIgnoredDirectory(target, root) {
36
+ const relative2 = path.relative(root, target);
37
+ if (!relative2 || relative2 === ".." || relative2.startsWith(`..${path.sep}`) || path.isAbsolute(relative2)) {
38
+ return false;
39
+ }
40
+ return relative2.split(path.sep).some((segment) => DEFAULT_IGNORE.has(segment));
41
+ }
34
42
  var grepTool = {
35
43
  name: "grep",
36
44
  category: "Search",
@@ -99,6 +107,14 @@ var grepTool = {
99
107
  });
100
108
  }
101
109
  const base = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
110
+ if (input.path && !await fs.stat(base).catch(() => null)) {
111
+ throw new ToolValidationError({
112
+ message: `grep: path "${input.path}" does not exist`,
113
+ field: "path"
114
+ });
115
+ }
116
+ const ignoreRoot = await fs.realpath(ctx.projectRoot ?? ctx.cwd).catch(() => path.resolve(ctx.projectRoot ?? ctx.cwd));
117
+ const ignoreTarget = await fs.realpath(base).catch(() => path.resolve(base));
102
118
  const mode = input.output_mode ?? "content";
103
119
  const rawLimit = typeof input.limit === "number" && Number.isFinite(input.limit) ? input.limit : 200;
104
120
  const limit = Math.max(1, Math.min(Math.floor(rawLimit), 2e3));
@@ -112,6 +128,18 @@ var grepTool = {
112
128
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
113
129
  const relativize = makeRootRelativizer(ctx.cwd);
114
130
  const rgAvailable = await detectRg();
131
+ if (isInsideDefaultIgnoredDirectory(ignoreTarget, ignoreRoot)) {
132
+ yield {
133
+ type: "final",
134
+ output: {
135
+ matches: [],
136
+ count: 0,
137
+ truncated: false,
138
+ used: rgAvailable ? "rg" : "native"
139
+ }
140
+ };
141
+ return;
142
+ }
115
143
  if (rgAvailable) {
116
144
  try {
117
145
  yield* runRgStream(input, base, mode, limit, signal, relativize);
@@ -132,7 +160,7 @@ function __setRgAvailableForTests(available) {
132
160
  rgAvailabilityCache = available !== void 0 ? Promise.resolve(available) : void 0;
133
161
  }
134
162
  function detectRg() {
135
- rgAvailabilityCache ??= new Promise((resolve) => {
163
+ rgAvailabilityCache ??= new Promise((resolve2) => {
136
164
  try {
137
165
  const p = spawn("rg", ["--version"], {
138
166
  env: buildChildEnv(),
@@ -140,10 +168,10 @@ function detectRg() {
140
168
  signal: AbortSignal.timeout(1e4),
141
169
  windowsHide: true
142
170
  });
143
- p.on("error", () => resolve(false));
144
- p.on("close", (code) => resolve(code === 0));
171
+ p.on("error", () => resolve2(false));
172
+ p.on("close", (code) => resolve2(code === 0));
145
173
  } catch {
146
- resolve(false);
174
+ resolve2(false);
147
175
  }
148
176
  });
149
177
  return rgAvailabilityCache;
@@ -206,6 +234,7 @@ async function* runRgStream(input, base, mode, limit, signal, relativize) {
206
234
  windowsHide: true
207
235
  });
208
236
  const queue = [];
237
+ const stdoutDecoder = new StringDecoder("utf8");
209
238
  let queuedChars = 0;
210
239
  let waiter;
211
240
  let paused = false;
@@ -227,7 +256,8 @@ async function* runRgStream(input, base, mode, limit, signal, relativize) {
227
256
  child.stdout?.resume();
228
257
  };
229
258
  const onStdoutData = (c) => {
230
- const data = c.toString();
259
+ const data = stdoutDecoder.write(c);
260
+ if (data.length === 0) return;
231
261
  queue.push({ kind: "out", data });
232
262
  queuedChars += data.length;
233
263
  wake();
@@ -237,7 +267,14 @@ async function* runRgStream(input, base, mode, limit, signal, relativize) {
237
267
  queue.push({ kind: "error", data: e.message });
238
268
  wake();
239
269
  };
240
- const onClose = () => {
270
+ let rgExitCode = null;
271
+ const onClose = (code) => {
272
+ rgExitCode = code;
273
+ const tail = stdoutDecoder.end();
274
+ if (tail.length > 0) {
275
+ queue.push({ kind: "out", data: tail });
276
+ queuedChars += tail.length;
277
+ }
241
278
  queue.push({ kind: "close", data: "" });
242
279
  wake();
243
280
  };
@@ -319,6 +356,9 @@ async function* runRgStream(input, base, mode, limit, signal, relativize) {
319
356
  };
320
357
  }
321
358
  if (errored) throw new Error("rg: spawn error");
359
+ if (rgExitCode === 2 && totalLines === 0 && !bufOverflow) {
360
+ throw new Error("rg: exited with an error and produced no matches");
361
+ }
322
362
  yield {
323
363
  type: "final",
324
364
  output: {
@@ -398,6 +438,7 @@ async function runNative(input, base, mode, limit, signal, relativize) {
398
438
  let lineNumber = 0;
399
439
  let fileHits = 0;
400
440
  let leftover = "";
441
+ const decoder = new StringDecoder("utf8");
401
442
  let binaryChecked = false;
402
443
  const buffer = Buffer.allocUnsafe(Math.min(NATIVE_READ_CHUNK_BYTES, maxBytes));
403
444
  while (!stopped && !signal.aborted && bytesReadTotal < maxBytes) {
@@ -415,7 +456,7 @@ async function runNative(input, base, mode, limit, signal, relativize) {
415
456
  if (isBinaryBuffer(chunk)) return;
416
457
  }
417
458
  bytesReadTotal += bytesRead;
418
- const text = leftover + chunk.toString("utf8");
459
+ const text = leftover + decoder.write(chunk);
419
460
  const lines = text.split(/\r?\n/);
420
461
  leftover = lines.pop() ?? "";
421
462
  for (const rawLine of lines) {
@@ -439,6 +480,7 @@ async function runNative(input, base, mode, limit, signal, relativize) {
439
480
  }
440
481
  if (mode === "files_with_matches" && fileHits > 0) break;
441
482
  }
483
+ leftover += decoder.end();
442
484
  if (!stopped && !signal.aborted && leftover.length > 0) {
443
485
  lineNumber++;
444
486
  const ln = leftover.length > 4096 ? capSubject(leftover) : leftover;
@@ -526,4 +568,4 @@ export {
526
568
  __resetRgDetectionForTests,
527
569
  __setRgAvailableForTests
528
570
  };
529
- //# sourceMappingURL=chunk-2W4XTULI.js.map
571
+ //# sourceMappingURL=chunk-DLZCOEVG.js.map