@wrongstack/tools 0.319.0 → 0.320.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/_edit-match.d.ts +1 -3
  2. package/dist/_util.d.ts +7 -0
  3. package/dist/audit.js +8 -8
  4. package/dist/bash.js +6 -6
  5. package/dist/browser/index.js +1 -1
  6. package/dist/builtin.d.ts +10 -0
  7. package/dist/builtin.js +54 -52
  8. package/dist/{chunk-65EMYN5G.js → chunk-23EX5WDO.js} +6 -3
  9. package/dist/{chunk-2472NT76.js → chunk-2OXHS3KZ.js} +17 -5
  10. package/dist/{chunk-7WV3D4S3.js → chunk-34VLZ35Z.js} +75 -36
  11. package/dist/{chunk-MYRMD6PZ.js → chunk-3GIKYHLA.js} +32 -8
  12. package/dist/{chunk-X5K3U6GI.js → chunk-3M7LLN7W.js} +71 -7
  13. package/dist/{chunk-VALQFAY2.js → chunk-4BABEJC2.js} +158 -45
  14. package/dist/{chunk-42BHI7TN.js → chunk-4CQWNZ62.js} +2 -2
  15. package/dist/{chunk-WNUJR3H4.js → chunk-5UFVCIUB.js} +40 -16
  16. package/dist/{chunk-UMXVT7ST.js → chunk-6AAOT4PW.js} +58 -10
  17. package/dist/{chunk-H6AWAZ2Y.js → chunk-B4HTSV6Z.js} +57 -6
  18. package/dist/{chunk-5ZYEGP2P.js → chunk-BK732J7I.js} +72 -27
  19. package/dist/{chunk-KD5EHYA2.js → chunk-C2DQKVLO.js} +61 -28
  20. package/dist/{chunk-Z4VBTVUM.js → chunk-DFQDJT22.js} +99 -12
  21. package/dist/{chunk-6RNIFLG4.js → chunk-ELRRTWNP.js} +38 -6
  22. package/dist/{chunk-3PVYQFRQ.js → chunk-ESLPBT5A.js} +2 -1
  23. package/dist/{chunk-YZHK4TLF.js → chunk-FQNXYA6W.js} +45 -6
  24. package/dist/{chunk-OYVPBPCL.js → chunk-HR67OWEP.js} +72 -33
  25. package/dist/{chunk-ZDUGI5BC.js → chunk-I7FBZOEP.js} +2 -2
  26. package/dist/{chunk-HBM4QNXU.js → chunk-IU4DV2PA.js} +18 -8
  27. package/dist/{chunk-TFMVHYJI.js → chunk-J43O3BOX.js} +9 -15
  28. package/dist/{chunk-ODFVG54U.js → chunk-J74TMK4M.js} +37 -13
  29. package/dist/{chunk-COUNXG4X.js → chunk-JLYA4X7K.js} +75 -8
  30. package/dist/{chunk-FQZOHLG3.js → chunk-JSNSMG5R.js} +3 -2
  31. package/dist/{chunk-SYXW3Y5Z.js → chunk-JVRLTEO7.js} +2 -2
  32. package/dist/{chunk-V43EQ6VR.js → chunk-KE7A76VC.js} +3 -2
  33. package/dist/{chunk-ZKSBHXVB.js → chunk-KWXIKGE3.js} +64 -15
  34. package/dist/{chunk-RK47IWO3.js → chunk-LRDIRJJL.js} +62 -30
  35. package/dist/{chunk-UC3RKRIV.js → chunk-NDIT3SBZ.js} +3 -3
  36. package/dist/{chunk-C3W3V54D.js → chunk-NSPNFOJ7.js} +57 -27
  37. package/dist/{chunk-TX3R5NVV.js → chunk-NU2IZ72J.js} +7 -7
  38. package/dist/{chunk-WHIIIREL.js → chunk-NYRLWDT5.js} +7 -3
  39. package/dist/{chunk-PMHVSHMU.js → chunk-OTPTQBYE.js} +80 -19
  40. package/dist/{chunk-ZHH3BEWU.js → chunk-OYP27E3I.js} +4 -4
  41. package/dist/{chunk-W4GAAI3M.js → chunk-PV6HIZQD.js} +2 -2
  42. package/dist/{chunk-NKNOBUBU.js → chunk-QGF2C7TF.js} +2 -2
  43. package/dist/{chunk-PQTONQ4P.js → chunk-QPNYDH5B.js} +19 -5
  44. package/dist/{chunk-VFF3Q5Z3.js → chunk-QSNVDHOZ.js} +37 -9
  45. package/dist/{chunk-45JPRMC2.js → chunk-QWKSKFKZ.js} +7 -3
  46. package/dist/{chunk-2VSZC6QM.js → chunk-SCMRM6NL.js} +6 -4
  47. package/dist/{chunk-RJZQWEMS.js → chunk-T77XOX7E.js} +3 -2
  48. package/dist/{chunk-QZDOQRF7.js → chunk-UZFY3CKD.js} +35 -14
  49. package/dist/{chunk-D4CXUZIF.js → chunk-VD2LV5WO.js} +82 -13
  50. package/dist/{chunk-DPKTXJQS.js → chunk-VPALDFYM.js} +2 -2
  51. package/dist/{chunk-IIF5HIVF.js → chunk-VVZPQPTY.js} +2 -2
  52. package/dist/{chunk-HWDB2G7J.js → chunk-W232EFEM.js} +49 -36
  53. package/dist/{chunk-FJEQZHZN.js → chunk-W2CECGTG.js} +24 -14
  54. package/dist/{chunk-4Q7LFU4T.js → chunk-WAQASTXB.js} +20 -9
  55. package/dist/{chunk-CWALNI6K.js → chunk-X6KXNHUO.js} +1 -1
  56. package/dist/{chunk-OCUXN6Y3.js → chunk-XRNN44QY.js} +1 -1
  57. package/dist/{chunk-N5O3FK5Q.js → chunk-XSSFBS5I.js} +70 -29
  58. package/dist/{chunk-Y7QXSE7N.js → chunk-YTKSNTT4.js} +44 -10
  59. package/dist/{chunk-MRKWZCHZ.js → chunk-Z5Q3TXRY.js} +3 -3
  60. package/dist/{chunk-PRXZOIEW.js → chunk-Z6IHMJMC.js} +104 -59
  61. package/dist/{chunk-NWZ6XGBJ.js → chunk-ZBMANLYH.js} +5 -3
  62. package/dist/{chunk-OJ4FIBAN.js → chunk-ZMW6WJFT.js} +30 -23
  63. package/dist/{chunk-QLF74MSV.js → chunk-ZUFHAHTE.js} +12 -6
  64. package/dist/codebase-index/index.js +12 -12
  65. package/dist/codebase-index/parser-worker-script.js +3 -3
  66. package/dist/codebase-index/project-server.js +5 -5
  67. package/dist/codebase-index/worker.js +4 -4
  68. package/dist/design.d.ts +1 -1
  69. package/dist/design.js +1 -1
  70. package/dist/diff.js +3 -2
  71. package/dist/document.js +2 -2
  72. package/dist/e2e.js +2 -2
  73. package/dist/edit.js +9 -9
  74. package/dist/exec.js +6 -6
  75. package/dist/fetch.js +2 -2
  76. package/dist/format.js +8 -8
  77. package/dist/git.js +2 -2
  78. package/dist/glob.js +3 -3
  79. package/dist/{go-parser-IKHW67WE.js → go-parser-D7ELWQ7F.js} +2 -2
  80. package/dist/grep.d.ts +2 -0
  81. package/dist/grep.js +5 -3
  82. package/dist/index.js +56 -56
  83. package/dist/install.js +8 -8
  84. package/dist/{json-parser-2EOVP5MR.js → json-parser-34DHYCPW.js} +1 -18
  85. package/dist/json.js +2 -2
  86. package/dist/kanban-tool-types.d.ts +2 -0
  87. package/dist/kanban.js +2 -2
  88. package/dist/languages/index.js +6 -6
  89. package/dist/legacy-bridge-FOV5ODDI.js +18 -0
  90. package/dist/lint.js +8 -8
  91. package/dist/logs.js +2 -2
  92. package/dist/memory.js +1 -1
  93. package/dist/next-steps.d.ts +7 -0
  94. package/dist/next-steps.js +82 -38
  95. package/dist/outdated.js +5 -3
  96. package/dist/pack.js +53 -53
  97. package/dist/{parser-dispatch-3PDBKIQ7.js → parser-dispatch-BDVCJXTV.js} +4 -4
  98. package/dist/patch.js +8 -8
  99. package/dist/plan.js +4 -4
  100. package/dist/process-registry.js +1 -1
  101. package/dist/ps-slash.js +3 -3
  102. package/dist/{py-parser-RFWG2HYI.js → py-parser-QC6MR4GB.js} +3 -3
  103. package/dist/read.js +8 -8
  104. package/dist/replace.js +9 -8
  105. package/dist/scaffold.js +2 -2
  106. package/dist/search.js +1 -1
  107. package/dist/session-kanban.js +1 -1
  108. package/dist/task.js +4 -4
  109. package/dist/test.js +8 -8
  110. package/dist/todo.js +3 -3
  111. package/dist/tool-search.js +1 -1
  112. package/dist/tool-tier.js +54 -54
  113. package/dist/{tree-sitter-parser-FGKHJGSX.js → tree-sitter-parser-GILYQLS4.js} +2 -2
  114. package/dist/tree.js +2 -2
  115. package/dist/typecheck.js +8 -8
  116. package/dist/win32.js +1 -1
  117. package/dist/write.js +9 -9
  118. package/package.json +5 -5
  119. package/dist/legacy-bridge-EC7GM2IW.js +0 -18
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-Q5JPPCZC.js";
5
5
  import {
6
6
  safeResolveReal
7
- } from "./chunk-QLF74MSV.js";
7
+ } from "./chunk-ZUFHAHTE.js";
8
8
 
9
9
  // src/json.ts
10
10
  import * as fs from "node:fs/promises";
@@ -19,9 +19,18 @@ var JsonFileTooLargeError = class extends Error {
19
19
  this.name = "JsonFileTooLargeError";
20
20
  }
21
21
  };
22
+ var JsonFileIsDirectoryError = class extends Error {
23
+ constructor(filePath) {
24
+ super(`json: "${filePath}" is a directory, not a file`);
25
+ this.name = "JsonFileIsDirectoryError";
26
+ }
27
+ };
22
28
  async function readJsonFileBounded(filePath, ctx) {
23
29
  const resolved = await safeResolveReal(filePath, ctx);
24
30
  const stat2 = await fs.stat(resolved);
31
+ if (stat2.isDirectory()) {
32
+ throw new JsonFileIsDirectoryError(filePath);
33
+ }
25
34
  if (stat2.size > MAX_JSON_FILE_BYTES) {
26
35
  throw new JsonFileTooLargeError(filePath, stat2.size);
27
36
  }
@@ -82,8 +91,26 @@ var jsonTool = {
82
91
  }
83
92
  }
84
93
  },
85
- async execute(input, ctx) {
94
+ async execute(input, ctx, opts) {
95
+ const signal = opts?.signal ?? ctx?.signal;
96
+ signal?.throwIfAborted();
86
97
  const action = input.action ?? "parse";
98
+ const ALLOWED_ACTIONS = /* @__PURE__ */ new Set([
99
+ "parse",
100
+ "query",
101
+ "validate",
102
+ "transform",
103
+ "merge"
104
+ ]);
105
+ 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
+ };
113
+ }
87
114
  switch (action) {
88
115
  case "query":
89
116
  return executeQuery(input, ctx);
@@ -111,7 +138,7 @@ async function executeParse(input, ctx) {
111
138
  formatted: "",
112
139
  type: "unknown",
113
140
  action: "parse",
114
- error: error instanceof JsonFileTooLargeError ? error.message : "Could not read file"
141
+ error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read file"
115
142
  };
116
143
  }
117
144
  } else if (input.data) {
@@ -188,7 +215,7 @@ async function executeQuery(input, ctx) {
188
215
  formatted: "",
189
216
  type: "unknown",
190
217
  action: "query",
191
- error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
218
+ error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
192
219
  };
193
220
  }
194
221
  } else if (input.data) {
@@ -254,7 +281,7 @@ async function executeValidate(input, ctx) {
254
281
  formatted: "",
255
282
  type: "unknown",
256
283
  action: "validate",
257
- error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
284
+ error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
258
285
  };
259
286
  }
260
287
  } else if (input.data) {
@@ -320,7 +347,7 @@ async function executeTransform(input, ctx) {
320
347
  formatted: "",
321
348
  type: "unknown",
322
349
  action: "transform",
323
- error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
350
+ error: error instanceof JsonFileTooLargeError || error instanceof JsonFileIsDirectoryError ? error.message : "Could not read/parse file"
324
351
  };
325
352
  }
326
353
  } else if (input.data) {
@@ -409,6 +436,9 @@ function jmespathSearch(data, query) {
409
436
  const dotMatch = query.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(?:\.(.+))?$/);
410
437
  if (dotMatch) {
411
438
  const key = dotMatch[1];
439
+ if (key === "__proto__" || key === "prototype" || key === "constructor") {
440
+ return void 0;
441
+ }
412
442
  const rest = dotMatch[2];
413
443
  const val = data?.[key];
414
444
  if (rest === void 0) return val;
@@ -539,9 +569,23 @@ function validateJsonSchema(data, schema) {
539
569
  if (typeof value === "number" && s["maximum"] !== void 0 && value > s["maximum"]) {
540
570
  errors.push(`${path}: above maximum ${s["maximum"]}`);
541
571
  }
542
- if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
543
- for (let i = 0; i < value.length; i++) {
544
- check(value[i], s["items"], `${path}[${i}]`);
572
+ if (Array.isArray(value) && s["items"]) {
573
+ if (Array.isArray(s["items"])) {
574
+ for (let i = 0; i < Math.min(value.length, s["items"].length); i++) {
575
+ check(value[i], s["items"][i], `${path}[${i}]`);
576
+ }
577
+ } else if (typeof s["items"] === "object" && s["items"] !== null) {
578
+ for (let i = 0; i < value.length; i++) {
579
+ check(value[i], s["items"], `${path}[${i}]`);
580
+ }
581
+ }
582
+ }
583
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && Array.isArray(s["required"])) {
584
+ const obj = value;
585
+ for (const req of s["required"]) {
586
+ if (typeof req === "string" && !(req in obj)) {
587
+ errors.push(`${path}: missing required property "${req}"`);
588
+ }
545
589
  }
546
590
  }
547
591
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
@@ -559,6 +603,9 @@ function simpleQuery(data, path) {
559
603
  let current = data;
560
604
  for (const part of parts) {
561
605
  if (current === null || current === void 0) return void 0;
606
+ if (part === "__proto__" || part === "prototype" || part === "constructor") {
607
+ return void 0;
608
+ }
562
609
  const idx = Number(part);
563
610
  if (!Number.isNaN(idx) && Array.isArray(current)) {
564
611
  current = current[idx];
@@ -585,7 +632,7 @@ function toYaml(data, indent = 0) {
585
632
  if (typeof data === "boolean") return String(data) + "\n";
586
633
  if (typeof data === "number") return String(data) + "\n";
587
634
  if (typeof data === "string") {
588
- if (data.includes("\n") || data.includes(":") || data.includes("#")) {
635
+ if (data.includes("\n") || data.includes(":") || data.includes("#") || data.startsWith("-")) {
589
636
  return `"${data.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"
590
637
  `;
591
638
  }
@@ -594,12 +641,34 @@ function toYaml(data, indent = 0) {
594
641
  if (Array.isArray(data)) {
595
642
  if (data.length === 0) return "[]\n";
596
643
  const prefix = " ".repeat(indent);
597
- return data.map((item) => `${prefix}- ${toYaml(item, indent + 1).trimStart()}`).join("");
644
+ return data.map((item) => {
645
+ if (typeof item === "object" && item !== null && !Array.isArray(item)) {
646
+ const itemEntries = Object.entries(item);
647
+ if (itemEntries.length === 0) return `${prefix}- {}
648
+ `;
649
+ const [firstK, firstV] = itemEntries[0];
650
+ const rest = itemEntries.slice(1);
651
+ let itemYaml = `${prefix}- ${firstK}: ${toYaml(firstV, indent + 2).trimStart()}`;
652
+ for (const [k, v] of rest) {
653
+ itemYaml += `${prefix} ${k}: ${toYaml(v, indent + 2)}`;
654
+ }
655
+ return itemYaml;
656
+ }
657
+ return `${prefix}- ${toYaml(item, indent + 1).trimStart()}`;
658
+ }).join("");
598
659
  }
599
660
  if (typeof data === "object") {
600
661
  const prefix = " ".repeat(indent);
601
662
  const entries = Object.entries(data);
602
- return entries.map(([k, v]) => `${prefix}${k}: ${toYaml(v, indent + 1)}`).join("");
663
+ if (entries.length === 0) return "{}\n";
664
+ return entries.map(([k, v]) => {
665
+ const safeKey = /[:#\s]/.test(k) ? `"${k.replace(/"/g, '\\"')}"` : k;
666
+ if (typeof v === "object" && v !== null && (Array.isArray(v) ? v.length > 0 : Object.keys(v).length > 0)) {
667
+ return `${prefix}${safeKey}:
668
+ ${toYaml(v, indent + 1)}`;
669
+ }
670
+ return `${prefix}${safeKey}: ${toYaml(v, indent + 1)}`;
671
+ }).join("");
603
672
  }
604
673
  return String(data) + "\n";
605
674
  }
@@ -607,4 +676,4 @@ function toYaml(data, indent = 0) {
607
676
  export {
608
677
  jsonTool
609
678
  };
610
- //# sourceMappingURL=chunk-D4CXUZIF.js.map
679
+ //# sourceMappingURL=chunk-VD2LV5WO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  safeResolveReal
3
- } from "./chunk-QLF74MSV.js";
3
+ } from "./chunk-ZUFHAHTE.js";
4
4
 
5
5
  // src/e2e.ts
6
6
  import { open, readdir } from "node:fs/promises";
@@ -422,4 +422,4 @@ export {
422
422
  discoverE2EProjects,
423
423
  e2ePlanTool
424
424
  };
425
- //# sourceMappingURL=chunk-DPKTXJQS.js.map
425
+ //# sourceMappingURL=chunk-VPALDFYM.js.map
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-UCYQTZ7F.js";
8
8
  import {
9
9
  resolveWin32Command
10
- } from "./chunk-V43EQ6VR.js";
10
+ } from "./chunk-KE7A76VC.js";
11
11
 
12
12
  // src/codebase-index/py-parser.ts
13
13
  import { spawn } from "node:child_process";
@@ -382,4 +382,4 @@ export {
382
382
  parseSymbols,
383
383
  resolvePythonBinary
384
384
  };
385
- //# sourceMappingURL=chunk-IIF5HIVF.js.map
385
+ //# sourceMappingURL=chunk-VVZPQPTY.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  safeResolveReal
3
- } from "./chunk-QLF74MSV.js";
3
+ } from "./chunk-ZUFHAHTE.js";
4
4
 
5
5
  // src/tree.ts
6
6
  import * as fs from "node:fs/promises";
7
7
  import * as path from "node:path";
8
- import { DEFAULT_WALK_IGNORE_DIRS, compileGlob, expectDefined } from "@wrongstack/core/utils";
8
+ import { compileGlob, DEFAULT_WALK_IGNORE_DIRS, expectDefined } from "@wrongstack/core/utils";
9
9
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
10
10
  ...DEFAULT_WALK_IGNORE_DIRS,
11
11
  ".wrongstack",
@@ -80,14 +80,21 @@ var treeTool = {
80
80
  },
81
81
  async *executeStream(input, ctx, opts) {
82
82
  const basePath = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
83
- const maxDepth = input.depth ?? 3;
83
+ const maxDepth = typeof input.depth === "number" && Number.isFinite(input.depth) && input.depth >= 0 ? Math.floor(input.depth) : 3;
84
84
  const showFiles = input.show_files ?? true;
85
85
  const showDirs = input.show_dirs ?? true;
86
86
  const showHidden = input.show_hidden ?? false;
87
- const exclude = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...input.exclude ?? []]);
87
+ const rawExclude = Array.isArray(input.exclude) ? input.exclude : typeof input.exclude === "string" ? [input.exclude] : [];
88
+ const exclude = /* @__PURE__ */ new Set([
89
+ ...DEFAULT_IGNORE,
90
+ ...rawExclude.filter((s) => typeof s === "string").map(
91
+ (s) => s.trim().replace(/[\\/]+$/, "").replace(/^\.\//, "")
92
+ ).filter(Boolean)
93
+ ]);
88
94
  const globRe = input.glob ? compileGlob(input.glob) : void 0;
89
- const maxEntries = input.max_entries ?? DEFAULT_MAX_ENTRIES;
95
+ const maxEntries = typeof input.max_entries === "number" && Number.isFinite(input.max_entries) && input.max_entries > 0 ? Math.floor(input.max_entries) : DEFAULT_MAX_ENTRIES;
90
96
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
97
+ signal.throwIfAborted();
91
98
  const lines = [basePath];
92
99
  const totals = { totalFiles: { value: 0 }, totalDirs: { value: 0 } };
93
100
  const retention = {
@@ -100,6 +107,7 @@ var treeTool = {
100
107
  const queue = [];
101
108
  const FLUSH_EVERY = 200;
102
109
  let lastEmittedTotal = 0;
110
+ let wakeWaiter;
103
111
  const tickProgress = () => {
104
112
  const seen = totals.totalFiles.value + totals.totalDirs.value;
105
113
  if (seen - lastEmittedTotal >= FLUSH_EVERY) {
@@ -109,6 +117,7 @@ var treeTool = {
109
117
  data: { files: totals.totalFiles.value, dirs: totals.totalDirs.value }
110
118
  });
111
119
  lastEmittedTotal = seen;
120
+ wakeWaiter?.();
112
121
  }
113
122
  };
114
123
  const walkPromise = walkDir(basePath, 0, {
@@ -129,27 +138,20 @@ var treeTool = {
129
138
  signal
130
139
  });
131
140
  let walkDone = false;
132
- void walkPromise.then(
133
- () => {
134
- walkDone = true;
135
- },
136
- () => {
137
- walkDone = true;
138
- }
139
- );
141
+ void walkPromise.catch(() => {
142
+ }).finally(() => {
143
+ walkDone = true;
144
+ wakeWaiter?.();
145
+ });
140
146
  while (!walkDone || queue.length > 0) {
141
147
  if (queue.length > 0) {
142
148
  yield expectDefined(queue.shift());
143
149
  } else {
144
- let pollTimer;
145
- const poll = new Promise((r) => {
146
- pollTimer = setTimeout(r, 50);
150
+ await new Promise((resolve) => {
151
+ wakeWaiter = resolve;
152
+ if (walkDone || queue.length > 0) resolve();
147
153
  });
148
- try {
149
- await Promise.race([walkPromise, poll]).catch(() => void 0);
150
- } finally {
151
- if (pollTimer) clearTimeout(pollTimer);
152
- }
154
+ wakeWaiter = void 0;
153
155
  }
154
156
  }
155
157
  await walkPromise;
@@ -166,10 +168,17 @@ var treeTool = {
166
168
  }
167
169
  };
168
170
  function matchesTreeGlob(globRe, fileName, absPath, basePath) {
169
- if (globRe.test(fileName)) return true;
171
+ globRe.lastIndex = 0;
172
+ if (globRe.test(fileName)) {
173
+ globRe.lastIndex = 0;
174
+ return true;
175
+ }
176
+ globRe.lastIndex = 0;
170
177
  const rel = path.relative(basePath, absPath);
171
178
  const posixRel = !rel || rel.startsWith("..") || path.isAbsolute(rel) ? absPath.split(path.sep).join("/") : rel.split(path.sep).join("/");
172
- return globRe.test(posixRel);
179
+ const matches = globRe.test(posixRel);
180
+ globRe.lastIndex = 0;
181
+ return matches;
173
182
  }
174
183
  async function walkDir(dir, depth, opts) {
175
184
  opts.signal.throwIfAborted();
@@ -178,6 +187,8 @@ async function walkDir(dir, depth, opts) {
178
187
  const filtered = entries.filter((e) => {
179
188
  if (!opts.showHidden && e.name.startsWith(".")) return false;
180
189
  if (opts.exclude.has(e.name)) return false;
190
+ const rel = path.relative(opts.basePath, path.join(dir, e.name)).split(path.sep).join("/");
191
+ if (opts.exclude.has(rel)) return false;
181
192
  if (e.isFile() && opts.globRe) {
182
193
  const abs = path.join(dir, e.name);
183
194
  if (!matchesTreeGlob(opts.globRe, e.name, abs, opts.basePath)) return false;
@@ -204,22 +215,24 @@ async function walkDir(dir, depth, opts) {
204
215
  const entry = items[i];
205
216
  if (!entry) continue;
206
217
  const isLast = i === items.length - 1;
207
- const connector = opts.isLast ? " " : "\u2502 ";
218
+ const connector = isLast ? " " : "\u2502 ";
208
219
  const branch = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
209
220
  const displayName = entry.name + (entry.isDirectory() ? "/" : "");
210
- if (!opts.showDirs && entry.isDirectory()) continue;
211
- if (!opts.showFiles && entry.isFile()) continue;
212
- const line = opts.prefix + branch + displayName;
213
- const lineBytes = Buffer.byteLength(line, "utf8") + 1;
214
- if (opts.retention.entries >= opts.retention.maxEntries || opts.retention.outputBytes + lineBytes > opts.retention.maxOutputBytes) {
215
- opts.retention.truncated = true;
216
- return;
221
+ const isDir = entry.isDirectory();
222
+ const showEntry = isDir && opts.showDirs || !isDir && opts.showFiles;
223
+ if (showEntry) {
224
+ const line = opts.prefix + branch + displayName;
225
+ const lineBytes = Buffer.byteLength(line, "utf8") + 1;
226
+ if (opts.retention.entries >= opts.retention.maxEntries || opts.retention.outputBytes + lineBytes > opts.retention.maxOutputBytes) {
227
+ opts.retention.truncated = true;
228
+ return;
229
+ }
230
+ opts.lines.push(line);
231
+ opts.retention.entries++;
232
+ opts.retention.outputBytes += lineBytes;
217
233
  }
218
- opts.lines.push(line);
219
- opts.retention.entries++;
220
- opts.retention.outputBytes += lineBytes;
221
234
  if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {
222
- const childPrefix = opts.prefix + connector;
235
+ const childPrefix = showEntry ? opts.prefix + connector : opts.prefix;
223
236
  await walkDir(path.join(dir, entry.name), depth + 1, {
224
237
  ...opts,
225
238
  prefix: childPrefix,
@@ -232,4 +245,4 @@ async function walkDir(dir, depth, opts) {
232
245
  export {
233
246
  treeTool
234
247
  };
235
- //# sourceMappingURL=chunk-HWDB2G7J.js.map
248
+ //# sourceMappingURL=chunk-W232EFEM.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  applySessionKanbanTaskToSource,
3
3
  taskFileToSerializedGraph
4
- } from "./chunk-CWALNI6K.js";
4
+ } from "./chunk-X6KXNHUO.js";
5
5
 
6
6
  // src/kanban.ts
7
7
  import { stripLifecycleIssues as stripLifecycleIssues2 } from "@wrongstack/kanban";
@@ -349,7 +349,7 @@ async function handleKanbanBoardAction(projectRoot, input, ctx) {
349
349
  assignedAgent: input.agentId,
350
350
  status: input.status,
351
351
  priority: input.priority,
352
- label: input.labels?.[0],
352
+ label: input.label ?? input.labels?.[0],
353
353
  chainId: input.chainId
354
354
  });
355
355
  return { ok: true, message: `${tasks.length} task(s) matched.`, tasks };
@@ -360,7 +360,7 @@ async function handleKanbanBoardAction(projectRoot, input, ctx) {
360
360
  boardId: input.boardId,
361
361
  assignedAgent: input.agentId,
362
362
  priority: input.priority,
363
- label: input.labels?.[0],
363
+ label: input.label ?? input.labels?.[0],
364
364
  chainId: input.chainId,
365
365
  limit: input.limit
366
366
  });
@@ -373,7 +373,7 @@ async function handleKanbanBoardAction(projectRoot, input, ctx) {
373
373
  assignedAgent: input.agentId,
374
374
  status: input.status,
375
375
  priority: input.priority,
376
- label: input.labels?.[0],
376
+ label: input.label ?? input.labels?.[0],
377
377
  chainId: input.chainId
378
378
  });
379
379
  return {
@@ -2070,29 +2070,39 @@ var kanbanTool = {
2070
2070
  timeoutMs: 3e4,
2071
2071
  inputSchema: KANBAN_INPUT_SCHEMA,
2072
2072
  async execute(input, ctx, _opts) {
2073
+ const signal = _opts?.signal ?? ctx.signal ?? new AbortController().signal;
2074
+ if (signal.aborted) return fail("Operation aborted.");
2075
+ if (!input || typeof input !== "object" || typeof input.action !== "string" || !input.action.trim()) {
2076
+ return fail("kanban: action is required and must be a non-empty string.");
2077
+ }
2078
+ const normalizedInput = {
2079
+ ...input,
2080
+ action: input.action.trim()
2081
+ };
2073
2082
  const projectRoot = ctx.projectRoot;
2074
2083
  if (!projectRoot) return fail("No project root is available.");
2075
- const withPresence = createKanbanPresenceWrapper(projectRoot, input, ctx);
2084
+ const withPresence = createKanbanPresenceWrapper(projectRoot, normalizedInput, ctx);
2076
2085
  try {
2077
2086
  const result = await (async () => {
2078
- const decompositionResult = await handleKanbanDecompositionAction(projectRoot, input, ctx);
2087
+ const decompositionResult = await handleKanbanDecompositionAction(projectRoot, normalizedInput, ctx);
2079
2088
  if (decompositionResult !== void 0) return decompositionResult;
2080
- const boardResult = await handleKanbanBoardAction(projectRoot, input, ctx);
2089
+ const boardResult = await handleKanbanBoardAction(projectRoot, normalizedInput, ctx);
2081
2090
  if (boardResult !== void 0) return boardResult;
2082
- const lifecycleResult = await handleKanbanLifecycleAction(projectRoot, input, ctx);
2091
+ const lifecycleResult = await handleKanbanLifecycleAction(projectRoot, normalizedInput, ctx);
2083
2092
  if (lifecycleResult !== void 0) return lifecycleResult;
2084
2093
  const contractResult = await handleKanbanContractAction(
2085
2094
  projectRoot,
2086
- input,
2087
- input.author ?? input.agentId,
2095
+ normalizedInput,
2096
+ normalizedInput.author ?? normalizedInput.agentId,
2088
2097
  ctx.eventSessionId?.() ?? ctx.session?.id ?? "default-session"
2089
2098
  );
2090
2099
  if (contractResult !== void 0) return contractResult;
2091
- const detailResult = await handleKanbanDetailAction(projectRoot, input, ctx);
2100
+ const detailResult = await handleKanbanDetailAction(projectRoot, normalizedInput, ctx);
2092
2101
  if (detailResult !== void 0) return detailResult;
2093
- return fail(`Unknown kanban action: ${input.action}`);
2102
+ return fail(`Unknown kanban action: ${normalizedInput.action}`);
2094
2103
  })();
2095
- return withPresence(result);
2104
+ if (signal.aborted) return fail("Operation aborted.");
2105
+ return await withPresence(result);
2096
2106
  } catch (err) {
2097
2107
  return fail(stripLifecycleIssues2(err instanceof Error ? err.message : String(err)));
2098
2108
  }
@@ -2108,4 +2118,4 @@ export {
2108
2118
  recordKanbanVerificationEvidence,
2109
2119
  kanbanTool
2110
2120
  };
2111
- //# sourceMappingURL=chunk-FJEQZHZN.js.map
2121
+ //# sourceMappingURL=chunk-W2CECGTG.js.map
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  tryLegacyPackageOperation
3
- } from "./chunk-ZDUGI5BC.js";
3
+ } from "./chunk-I7FBZOEP.js";
4
4
  import {
5
5
  spawnStream
6
- } from "./chunk-ZHH3BEWU.js";
6
+ } from "./chunk-OYP27E3I.js";
7
7
  import {
8
8
  detectPackageManager,
9
9
  normalizeCommandOutput,
10
10
  safeResolveReal
11
- } from "./chunk-QLF74MSV.js";
11
+ } from "./chunk-ZUFHAHTE.js";
12
12
 
13
13
  // src/install.ts
14
14
  import { join } from "node:path";
@@ -16,6 +16,7 @@ import {
16
16
  detectEcosystem as detectPackageEcosystem,
17
17
  recordPackageAction
18
18
  } from "@wrongstack/core/coordination";
19
+ import { ToolValidationError } from "@wrongstack/core/types";
19
20
  var installTool = {
20
21
  name: "install",
21
22
  category: "Package Management",
@@ -74,8 +75,17 @@ var installTool = {
74
75
  async *executeStream(input, ctx, opts) {
75
76
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
76
77
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
78
+ signal.throwIfAborted();
79
+ const VALID_SAVES = /* @__PURE__ */ new Set(["dependency", "dev", "optional"]);
80
+ if (input.save !== void 0 && !VALID_SAVES.has(input.save)) {
81
+ throw new ToolValidationError({
82
+ message: `install: invalid save option "${input.save}". Allowed: dependency, dev, optional`,
83
+ field: "save"
84
+ });
85
+ }
77
86
  if (!input.global) {
78
- const pkgList2 = input.packages ? Array.isArray(input.packages) ? input.packages : input.packages.split(",").map((p) => p.trim()) : [];
87
+ const rawList2 = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()).filter(Boolean) : [];
88
+ const pkgList2 = Array.from(new Set(rawList2));
79
89
  const bridge = await tryLegacyPackageOperation(
80
90
  "package-install",
81
91
  {
@@ -107,9 +117,8 @@ var installTool = {
107
117
  yield { type: "log", text: `Resolving with ${pkgManager}\u2026`, data: { phase: "resolve" } };
108
118
  const globalFlag = input.global ? ["-g"] : [];
109
119
  const ignoreScripts = input.lifecycleScripts !== true;
110
- const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
111
- (p) => p.trim()
112
- ) : [];
120
+ const rawList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()).filter(Boolean) : [];
121
+ const pkgList = Array.from(new Set(rawList));
113
122
  const PKG_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9^~><=*.+-]+)?$/i;
114
123
  for (const pkg of pkgList) {
115
124
  if (!PKG_NAME_RE.test(pkg) || pkg.startsWith("-") || pkg.length > 200) {
@@ -166,10 +175,12 @@ var installTool = {
166
175
  signal,
167
176
  maxBytes: 1e5
168
177
  });
178
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
179
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
169
180
  const output = {
170
181
  packages: pkgList,
171
182
  exit_code: result.exitCode,
172
- output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
183
+ output: normalizeCommandOutput(rawOutput),
173
184
  dry_run: args.includes("--dry-run"),
174
185
  truncated: result.truncated
175
186
  };
@@ -221,4 +232,4 @@ function resolveManifestPath(cwd, pkgManager) {
221
232
  export {
222
233
  installTool
223
234
  };
224
- //# sourceMappingURL=chunk-4Q7LFU4T.js.map
235
+ //# sourceMappingURL=chunk-WAQASTXB.js.map
@@ -997,4 +997,4 @@ export {
997
997
  applyManagedKanbanBoardToTodos2 as applyManagedKanbanBoardToTodos,
998
998
  applySessionKanbanTaskToSource2 as applySessionKanbanTaskToSource
999
999
  };
1000
- //# sourceMappingURL=chunk-CWALNI6K.js.map
1000
+ //# sourceMappingURL=chunk-X6KXNHUO.js.map
@@ -939,4 +939,4 @@ export {
939
939
  __smokeRootType,
940
940
  parseTreeSitterAst
941
941
  };
942
- //# sourceMappingURL=chunk-OCUXN6Y3.js.map
942
+ //# sourceMappingURL=chunk-XRNN44QY.js.map