@wrongstack/tools 1.0.8 → 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-FI6SOIVE.js → chunk-DLZCOEVG.js} +21 -3
  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-MF6ZUBI7.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-HOFVYIPI.js → chunk-ON6ET5ER.js} +16 -1
  38. package/dist/{chunk-KF7GMVNU.js → chunk-OUERRFEM.js} +32 -11
  39. package/dist/{chunk-GNLHBDFO.js → chunk-OXO773CW.js} +127 -196
  40. package/dist/{chunk-GF4JHLCO.js → chunk-PWZUHZP7.js} +2 -2
  41. package/dist/{chunk-5KRKL3WD.js → chunk-QA37PFYL.js} +28 -67
  42. package/dist/{chunk-OGQMPX3J.js → chunk-QAWI3RSK.js} +37 -81
  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-YUK3WLHJ.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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  todoTool
3
- } from "./chunk-S6CGSFLX.js";
3
+ } from "./chunk-DBG7UQGK.js";
4
4
  import {
5
5
  mirrorSessionTasksToKanban
6
6
  } from "./chunk-YZECCW3F.js";
@@ -138,13 +138,10 @@ var taskTool = {
138
138
  "planify"
139
139
  ]);
140
140
  if (!input?.action || !VALID_ACTIONS.has(input.action)) {
141
- return {
142
- ok: false,
143
- message: `Unknown action "${input?.action}". Use replace | add | status | show | promote | planify.`,
144
- count: 0,
145
- completed: 0,
146
- inProgress: 0
147
- };
141
+ throw new ToolValidationError({
142
+ message: `task: Unknown action "${input?.action}". Use replace | add | status | show | promote | planify.`,
143
+ field: "action"
144
+ });
148
145
  }
149
146
  if (input.scope !== void 0 && input.scope !== "session" && input.scope !== "project") {
150
147
  throw new ToolValidationError({
@@ -167,13 +164,7 @@ var taskTool = {
167
164
  taskPath = sessionTaskPath;
168
165
  }
169
166
  if (typeof taskPath !== "string" || !taskPath) {
170
- return {
171
- ok: false,
172
- message: "Task storage path not configured.",
173
- count: 0,
174
- completed: 0,
175
- inProgress: 0
176
- };
167
+ throw new Error("task: Task storage path not configured.");
177
168
  }
178
169
  const sessionId = ctx.session?.id ?? "unknown";
179
170
  if (input.action === "show") {
@@ -189,7 +180,10 @@ var taskTool = {
189
180
  inProgress: p2.inProgress
190
181
  };
191
182
  }
192
- let early = null;
183
+ let failure;
184
+ const refuse = (message, field) => {
185
+ failure = field ? new ToolValidationError({ message: `task: ${message}`, field }) : new Error(`task: ${message}`);
186
+ };
193
187
  const promoteMeta = { count: 0, title: "" };
194
188
  const planifyMeta = { title: "", details: "" };
195
189
  let didPlanify = false;
@@ -202,13 +196,7 @@ var taskTool = {
202
196
  break;
203
197
  case "replace": {
204
198
  if (!Array.isArray(input.tasks)) {
205
- early = {
206
- ok: false,
207
- message: "action=replace requires `tasks` array.",
208
- count: 0,
209
- completed: 0,
210
- inProgress: 0
211
- };
199
+ refuse("action=replace requires `tasks` array.", "tasks");
212
200
  return f;
213
201
  }
214
202
  const newIds = new Set(input.tasks.map((t) => t.id));
@@ -223,25 +211,20 @@ var taskTool = {
223
211
  })
224
212
  )
225
213
  ];
226
- early = {
227
- ok: false,
228
- message: `action=replace has duplicate task IDs: ${dupes.join(", ")}. Each task id must be unique.`,
229
- count: 0,
230
- completed: 0,
231
- inProgress: 0
232
- };
214
+ refuse(
215
+ `action=replace has duplicate task IDs: ${dupes.join(", ")}. Each task id must be unique.`,
216
+ "tasks"
217
+ );
233
218
  return f;
234
219
  }
235
220
  const omittedUnfinished = f.tasks.filter(
236
221
  (task) => task.status !== "completed" && !newIds.has(task.id)
237
222
  );
238
223
  if (omittedUnfinished.length > 0) {
239
- early = {
240
- ok: false,
241
- message: `action=replace cannot omit unfinished tasks: ${omittedUnfinished.map((task) => task.id).join(", ")}. Complete them first.`,
242
- count: f.tasks.length,
243
- ...computeTaskItemProgress(f.tasks)
244
- };
224
+ refuse(
225
+ `action=replace cannot omit unfinished tasks: ${omittedUnfinished.map((task) => task.id).join(", ")}. Complete them first.`,
226
+ "tasks"
227
+ );
245
228
  return f;
246
229
  }
247
230
  const taskStatusById = new Map(input.tasks.map((t) => [t.id, t.status]));
@@ -249,13 +232,10 @@ var taskTool = {
249
232
  if (t.dependsOn && t.dependsOn.length > 0) {
250
233
  const missing = t.dependsOn.filter((d) => !newIds.has(d));
251
234
  if (missing.length > 0) {
252
- early = {
253
- ok: false,
254
- message: `dependsOn validation failed: task "${t.id}" references unknown IDs: ${missing.join(", ")}`,
255
- count: 0,
256
- completed: 0,
257
- inProgress: 0
258
- };
235
+ refuse(
236
+ `dependsOn validation failed: task "${t.id}" references unknown IDs: ${missing.join(", ")}`,
237
+ "tasks"
238
+ );
259
239
  return f;
260
240
  }
261
241
  }
@@ -264,12 +244,9 @@ var taskTool = {
264
244
  (dependencyId) => taskStatusById.get(dependencyId) !== "completed"
265
245
  );
266
246
  if (unmet.length > 0) {
267
- early = {
268
- ok: false,
269
- message: `dependency status validation failed: task "${t.id}" cannot be ${t.status} before completion of ${unmet.join(", ")}.`,
270
- count: f.tasks.length,
271
- ...computeTaskItemProgress(f.tasks)
272
- };
247
+ refuse(
248
+ `dependency status validation failed: task "${t.id}" cannot be ${t.status} before completion of ${unmet.join(", ")}.`
249
+ );
273
250
  return f;
274
251
  }
275
252
  }
@@ -285,26 +262,17 @@ var taskTool = {
285
262
  case "add": {
286
263
  const t = input.task;
287
264
  if (!t?.title) {
288
- early = {
289
- ok: false,
290
- message: "action=add requires `task` with at least `title`.",
291
- count: 0,
292
- completed: 0,
293
- inProgress: 0
294
- };
265
+ refuse("action=add requires `task` with at least `title`.", "task");
295
266
  return f;
296
267
  }
297
268
  if (t.dependsOn && t.dependsOn.length > 0) {
298
269
  const existingIds = new Set(f.tasks.map((e) => e.id));
299
270
  const missing = t.dependsOn.filter((d) => !existingIds.has(d));
300
271
  if (missing.length > 0) {
301
- early = {
302
- ok: false,
303
- message: `dependsOn validation failed: unknown task IDs: ${missing.join(", ")}`,
304
- count: 0,
305
- completed: 0,
306
- inProgress: 0
307
- };
272
+ refuse(
273
+ `dependsOn validation failed: unknown task IDs: ${missing.join(", ")}`,
274
+ "task"
275
+ );
308
276
  return f;
309
277
  }
310
278
  }
@@ -336,33 +304,19 @@ var taskTool = {
336
304
  "completed"
337
305
  ]);
338
306
  if (!input.id || !input.status) {
339
- early = {
340
- ok: false,
341
- message: "action=status requires `id` and `status`.",
342
- count: 0,
343
- completed: 0,
344
- inProgress: 0
345
- };
307
+ refuse("action=status requires `id` and `status`.", input.id ? "status" : "id");
346
308
  return f;
347
309
  }
348
310
  if (!VALID_TASK_STATUSES.has(input.status)) {
349
- early = {
350
- ok: false,
351
- message: `action=status requires valid status ('pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed'), got "${input.status}".`,
352
- count: f.tasks.length,
353
- ...computeTaskItemProgress(f.tasks)
354
- };
311
+ refuse(
312
+ `action=status requires valid status ('pending' | 'in_progress' | 'blocked' | 'failed' | 'review' | 'completed'), got "${input.status}".`,
313
+ "status"
314
+ );
355
315
  return f;
356
316
  }
357
317
  const task = f.tasks.find((t) => t.id === input.id);
358
318
  if (!task) {
359
- early = {
360
- ok: false,
361
- message: `Task "${input.id}" not found.`,
362
- count: 0,
363
- completed: 0,
364
- inProgress: 0
365
- };
319
+ refuse(`Task "${input.id}" not found.`, "id");
366
320
  return f;
367
321
  }
368
322
  if (input.status === "in_progress" || input.status === "completed") {
@@ -370,12 +324,9 @@ var taskTool = {
370
324
  (dependencyId) => f.tasks.find((candidate) => candidate.id === dependencyId)?.status !== "completed"
371
325
  );
372
326
  if (unmet.length > 0) {
373
- early = {
374
- ok: false,
375
- message: `Task "${task.id}" cannot be ${input.status} before dependencies complete: ${unmet.join(", ")}.`,
376
- count: f.tasks.length,
377
- ...computeTaskItemProgress(f.tasks)
378
- };
327
+ refuse(
328
+ `Task "${task.id}" cannot be ${input.status} before dependencies complete: ${unmet.join(", ")}.`
329
+ );
379
330
  return f;
380
331
  }
381
332
  }
@@ -386,35 +337,16 @@ var taskTool = {
386
337
  case "promote": {
387
338
  const target = (input.target ?? input.id)?.trim();
388
339
  if (!target) {
389
- early = {
390
- ok: false,
391
- message: "action=promote requires `target` (task id, index, or title substring).",
392
- count: 0,
393
- completed: 0,
394
- inProgress: 0
395
- };
340
+ refuse(
341
+ "action=promote requires `target` (task id, index, or title substring).",
342
+ "target"
343
+ );
396
344
  return f;
397
345
  }
398
346
  const idx = findTaskIndex(f.tasks, target);
399
- if (idx === -1) {
400
- early = {
401
- ok: false,
402
- message: `No task matched "${target}".`,
403
- count: 0,
404
- completed: 0,
405
- inProgress: 0
406
- };
407
- return f;
408
- }
409
- const match = f.tasks[idx];
347
+ const match = idx === -1 ? void 0 : f.tasks[idx];
410
348
  if (!match) {
411
- early = {
412
- ok: false,
413
- message: `No task matched "${target}".`,
414
- count: 0,
415
- completed: 0,
416
- inProgress: 0
417
- };
349
+ refuse(`No task matched "${target}".`, "target");
418
350
  return f;
419
351
  }
420
352
  if (match.status !== "completed" && match.status !== "failed") {
@@ -457,35 +389,16 @@ var taskTool = {
457
389
  case "planify": {
458
390
  const target = (input.target ?? input.id)?.trim();
459
391
  if (!target) {
460
- early = {
461
- ok: false,
462
- message: "action=planify requires `target` (task id, index, or title substring).",
463
- count: 0,
464
- completed: 0,
465
- inProgress: 0
466
- };
392
+ refuse(
393
+ "action=planify requires `target` (task id, index, or title substring).",
394
+ "target"
395
+ );
467
396
  return f;
468
397
  }
469
398
  const idx = findTaskIndex(f.tasks, target);
470
- if (idx === -1) {
471
- early = {
472
- ok: false,
473
- message: `No task matched "${target}".`,
474
- count: 0,
475
- completed: 0,
476
- inProgress: 0
477
- };
478
- return f;
479
- }
480
- const match = f.tasks[idx];
399
+ const match = idx === -1 ? void 0 : f.tasks[idx];
481
400
  if (!match) {
482
- early = {
483
- ok: false,
484
- message: `No task matched "${target}".`,
485
- count: 0,
486
- completed: 0,
487
- inProgress: 0
488
- };
401
+ refuse(`No task matched "${target}".`, "target");
489
402
  return f;
490
403
  }
491
404
  planifyMeta.title = match.title;
@@ -494,26 +407,21 @@ var taskTool = {
494
407
  break;
495
408
  }
496
409
  default:
497
- early = {
498
- ok: false,
499
- message: `Unknown action "${input.action}". Use replace | add | status | show | promote | planify.`,
500
- count: 0,
501
- completed: 0,
502
- inProgress: 0
503
- };
410
+ refuse(
411
+ `Unknown action "${input.action}". Use replace | add | status | show | promote | planify.`,
412
+ "action"
413
+ );
504
414
  return f;
505
415
  }
506
416
  return f;
507
417
  });
508
418
  } catch (err) {
509
- return {
510
- ok: false,
511
- message: `Task change not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
512
- count: 0,
513
- completed: 0,
514
- inProgress: 0
515
- };
419
+ throw new Error(
420
+ `task: change not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
421
+ { cause: err }
422
+ );
516
423
  }
424
+ if (failure) throw failure;
517
425
  ctx.meta["task.path.resolved"] = taskPath;
518
426
  if (todosToReplace) {
519
427
  await todoTool.execute({ todos: todosToReplace }, ctx, {
@@ -521,46 +429,36 @@ var taskTool = {
521
429
  });
522
430
  }
523
431
  mirrorSessionTasksToKanban(ctx.projectRoot, file.tasks, sessionId);
524
- if (early) return early;
525
432
  if (didPlanify) {
526
433
  const { title, details } = planifyMeta;
527
434
  const planPathRaw = ctx.meta["plan.path"];
528
435
  const prog = computeTaskItemProgress(file.tasks);
529
- if (typeof planPathRaw === "string" && planPathRaw) {
530
- let planPath = planPathRaw;
531
- if (input.scope === "project") {
532
- const lastSep = Math.max(planPath.lastIndexOf("/"), planPath.lastIndexOf("\\"));
533
- planPath = lastSep >= 0 ? planPath.slice(0, lastSep + 1) + "backlog.plan.json" : "backlog.plan.json";
534
- }
535
- let formatted = "";
536
- try {
537
- await mutatePlan(planPath, sessionId, (pf) => {
538
- const { plan: updated } = addPlanItem(pf, title, details || void 0);
539
- formatted = formatPlan(updated);
540
- return updated;
541
- });
542
- ctx.meta["plan.path.resolved"] = planPath;
543
- } catch (err) {
544
- return {
545
- ok: false,
546
- message: `planify: plan not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
547
- count: file.tasks.length,
548
- completed: prog.completed,
549
- inProgress: prog.inProgress
550
- };
551
- }
552
- return {
553
- ok: true,
554
- message: `planify ok \u2014 added "${title}" to plan.
555
- ${formatted}`,
556
- count: file.tasks.length,
557
- completed: prog.completed,
558
- inProgress: prog.inProgress
559
- };
436
+ if (typeof planPathRaw !== "string" || !planPathRaw) {
437
+ throw new Error("task: Plan storage path not configured \u2014 cannot planify.");
560
438
  }
439
+ let planPath = planPathRaw;
440
+ if (input.scope === "project") {
441
+ const lastSep = Math.max(planPath.lastIndexOf("/"), planPath.lastIndexOf("\\"));
442
+ planPath = lastSep >= 0 ? planPath.slice(0, lastSep + 1) + "backlog.plan.json" : "backlog.plan.json";
443
+ }
444
+ let formatted = "";
445
+ try {
446
+ await mutatePlan(planPath, sessionId, (pf) => {
447
+ const { plan: updated } = addPlanItem(pf, title, details || void 0);
448
+ formatted = formatPlan(updated);
449
+ return updated;
450
+ });
451
+ } catch (err) {
452
+ throw new Error(
453
+ `task: planify: plan not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
454
+ { cause: err }
455
+ );
456
+ }
457
+ ctx.meta["plan.path.resolved"] = planPath;
561
458
  return {
562
- ok: false,
563
- message: "Plan storage path not configured \u2014 cannot planify.",
459
+ ok: true,
460
+ message: `planify ok \u2014 added "${title}" to plan.
461
+ ${formatted}`,
564
462
  count: file.tasks.length,
565
463
  completed: prog.completed,
566
464
  inProgress: prog.inProgress
@@ -582,4 +480,4 @@ ${formatTaskList(file.tasks)}` : file.tasks.length > 0 ? formatTaskList(file.tas
582
480
  export {
583
481
  taskTool
584
482
  };
585
- //# sourceMappingURL=chunk-B62LKMWQ.js.map
483
+ //# sourceMappingURL=chunk-OKZJJDOG.js.map
@@ -64,6 +64,21 @@ var globTool = {
64
64
  return { files: [], truncated: true };
65
65
  }
66
66
  const base = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
67
+ if (input.path) {
68
+ const baseStat = await fs.stat(base).catch(() => null);
69
+ if (!baseStat) {
70
+ throw new ToolValidationError({
71
+ message: `glob: path "${input.path}" does not exist`,
72
+ field: "path"
73
+ });
74
+ }
75
+ if (!baseStat.isDirectory()) {
76
+ throw new ToolValidationError({
77
+ message: `glob: path "${input.path}" is not a directory`,
78
+ field: "path"
79
+ });
80
+ }
81
+ }
67
82
  const rawLimit = typeof input.limit === "number" && !Number.isNaN(input.limit) ? input.limit : 1e3;
68
83
  const limit = Math.max(1, Math.min(Math.floor(rawLimit), 5e3));
69
84
  const isGitIgnored = await loadGitignoreMatcher(base);
@@ -188,4 +203,4 @@ var globTool = {
188
203
  export {
189
204
  globTool
190
205
  };
191
- //# sourceMappingURL=chunk-HOFVYIPI.js.map
206
+ //# sourceMappingURL=chunk-ON6ET5ER.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  tryLegacyCodeOperation
3
- } from "./chunk-3LQJXJDM.js";
3
+ } from "./chunk-E7A4SLKR.js";
4
4
  import {
5
5
  spawnStream
6
- } from "./chunk-EMA2XENR.js";
6
+ } from "./chunk-UVYRQKPZ.js";
7
7
  import {
8
8
  normalizeCommandOutput,
9
9
  safeResolveReal
@@ -16,7 +16,7 @@ var testTool = {
16
16
  name: "test",
17
17
  category: "Code Quality",
18
18
  description: "Execute the project's test suite. This is one of the most critical tools for validating that your changes are correct.",
19
- usageHint: "ESSENTIAL BEFORE CONSIDERING WORK DONE:\n\n- Use `files` or `grep` to run only relevant tests during development.\n- `coverage: true` is useful when working on critical paths.\nRun tests frequently. A clean test run is usually required before the task can be considered complete.",
19
+ usageHint: "ESSENTIAL BEFORE CONSIDERING WORK DONE:\n\n- Use `files` or `grep` to run only relevant tests during development.\n- `coverage: true` is useful when working on critical paths.\n- Check `status`: `passed`, `failed`, or `no_tests` (no runner configured / no test files matched). `no_tests` is NOT a pass.\nRun tests frequently. A clean test run is usually required before the task can be considered complete.",
20
20
  permission: "confirm",
21
21
  mutating: false,
22
22
  icon: "test",
@@ -75,16 +75,26 @@ var testTool = {
75
75
  });
76
76
  if (bridge?.run) {
77
77
  const run = bridge.run;
78
+ if (run.status === "unavailable") {
79
+ throw new Error(
80
+ `test: ${bridge.language} test runner unavailable: ${run.error || run.output || "no detail"}`
81
+ );
82
+ }
83
+ const passed = run.summary.passed ?? 0;
84
+ const failed = run.summary.failed ?? 0;
85
+ const exitCode = run.exitCode ?? 0;
86
+ const rawOutput = run.output || run.error || "";
78
87
  yield {
79
88
  type: "final",
80
89
  output: {
81
90
  runner: bridge.language,
82
- exit_code: run.exitCode ?? 0,
83
- tests_run: (run.summary.passed ?? 0) + (run.summary.failed ?? 0),
84
- passed: run.summary.passed ?? 0,
85
- failed: run.summary.failed ?? 0,
91
+ status: resolveStatus(exitCode, passed + failed, rawOutput),
92
+ exit_code: exitCode,
93
+ tests_run: passed + failed,
94
+ passed,
95
+ failed,
86
96
  duration_ms: run.durationMs,
87
- output: normalizeCommandOutput(run.output || run.error || ""),
97
+ output: normalizeCommandOutput(rawOutput),
88
98
  truncated: run.truncated
89
99
  }
90
100
  };
@@ -97,12 +107,14 @@ var testTool = {
97
107
  type: "final",
98
108
  output: {
99
109
  runner: "none",
100
- exit_code: 0,
110
+ // Nothing ran: not a pass, and no exit code to report.
111
+ status: "no_tests",
112
+ exit_code: null,
101
113
  tests_run: 0,
102
114
  passed: 0,
103
115
  failed: 0,
104
116
  duration_ms: 0,
105
- output: "No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)",
117
+ output: "No tests: no test runner configured (looked for vitest.config.*, jest.config.*, .mocharc.*).",
106
118
  truncated: false
107
119
  }
108
120
  };
@@ -119,6 +131,9 @@ var testTool = {
119
131
  maxBytes: 2e5
120
132
  });
121
133
  const duration = Date.now() - start;
134
+ if (result.error) {
135
+ throw new Error(`test: failed to start ${detected}: ${result.error}`);
136
+ }
122
137
  yield { type: "final", output: parseResult(detected, result, duration) };
123
138
  }
124
139
  };
@@ -183,6 +198,11 @@ function buildArgs(runner, input) {
183
198
  }
184
199
  return args;
185
200
  }
201
+ var NO_TESTS_PATTERN = /No test files found|No tests found|no test files|running 0 tests|^\s*0 passing/im;
202
+ function resolveStatus(exitCode, testsRun, output) {
203
+ if (testsRun === 0 && NO_TESTS_PATTERN.test(output)) return "no_tests";
204
+ return exitCode === 0 ? "passed" : "failed";
205
+ }
186
206
  function parseResult(runner, result, duration) {
187
207
  const out = result.stdout + result.stderr;
188
208
  let tests_run = 0;
@@ -212,6 +232,7 @@ function parseResult(runner, result, duration) {
212
232
  ${result.stderr}` : result.stdout || result.stderr || result.error || "";
213
233
  return {
214
234
  runner,
235
+ status: resolveStatus(result.exitCode, tests_run, out),
215
236
  exit_code: result.exitCode,
216
237
  tests_run,
217
238
  passed,
@@ -231,4 +252,4 @@ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
231
252
  export {
232
253
  testTool
233
254
  };
234
- //# sourceMappingURL=chunk-KF7GMVNU.js.map
255
+ //# sourceMappingURL=chunk-OUERRFEM.js.map