@wrongstack/tools 0.297.0 → 0.298.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 (254) hide show
  1. package/dist/_spawn-stream.d.ts +2 -0
  2. package/dist/_util.d.ts +29 -1
  3. package/dist/audit.js +113 -65
  4. package/dist/bash.js +5 -4
  5. package/dist/batch-tool-use.js +70 -7
  6. package/dist/browser/index.js +31 -1
  7. package/dist/builtin.js +908 -390
  8. package/dist/codebase-index/index.js +68 -16
  9. package/dist/codebase-index/project-server-protocol.d.ts +26 -0
  10. package/dist/codebase-index/project-server.js +60 -16
  11. package/dist/codebase-index/worker.js +36 -13
  12. package/dist/codebase-index/writer-search-helpers.d.ts +16 -0
  13. package/dist/codebase-index/writer.d.ts +13 -1
  14. package/dist/design.d.ts +1 -1
  15. package/dist/design.js +4 -3
  16. package/dist/diff.js +11 -1
  17. package/dist/e2e.js +9 -6
  18. package/dist/edit.js +9 -6
  19. package/dist/exec-allowlist.d.ts +15 -0
  20. package/dist/exec.js +627 -568
  21. package/dist/format.js +116 -65
  22. package/dist/git.js +22 -2
  23. package/dist/glob.js +11 -5
  24. package/dist/grep.js +160 -104
  25. package/dist/index.js +859 -329
  26. package/dist/install.js +120 -68
  27. package/dist/json.js +64 -18
  28. package/dist/kanban-board-inputs.d.ts +21 -4
  29. package/dist/kanban.js +20 -5
  30. package/dist/languages/index.js +128 -71
  31. package/dist/lint.js +113 -65
  32. package/dist/outdated.js +86 -26
  33. package/dist/pack.js +908 -390
  34. package/dist/patch.js +11 -1
  35. package/dist/plan.js +32 -10
  36. package/dist/read.js +77 -23
  37. package/dist/replace.js +11 -3
  38. package/dist/scaffold.js +9 -6
  39. package/dist/session-kanban.js +21 -5
  40. package/dist/task.js +3 -1
  41. package/dist/test.js +113 -65
  42. package/dist/todo.js +3 -1
  43. package/dist/tool-tier.js +908 -390
  44. package/dist/tool-use.d.ts +1 -1
  45. package/dist/tool-use.js +28 -4
  46. package/dist/tree.d.ts +1 -0
  47. package/dist/tree.js +44 -8
  48. package/dist/typecheck.js +113 -65
  49. package/dist/write.js +9 -6
  50. package/package.json +7 -6
  51. package/dist/_concurrency.d.ts.map +0 -1
  52. package/dist/_danger-detect.d.ts.map +0 -1
  53. package/dist/_edit-match.d.ts.map +0 -1
  54. package/dist/_env.d.ts.map +0 -1
  55. package/dist/_fetch-guard.d.ts.map +0 -1
  56. package/dist/_output-spool.d.ts.map +0 -1
  57. package/dist/_redact-command.d.ts.map +0 -1
  58. package/dist/_regex.d.ts.map +0 -1
  59. package/dist/_session-shell.d.ts.map +0 -1
  60. package/dist/_shell-pick.d.ts.map +0 -1
  61. package/dist/_spawn-stream.d.ts.map +0 -1
  62. package/dist/_syntax-check.d.ts.map +0 -1
  63. package/dist/_util.d.ts.map +0 -1
  64. package/dist/_win32-resolve.d.ts.map +0 -1
  65. package/dist/audit.d.ts.map +0 -1
  66. package/dist/audit.js.map +0 -7
  67. package/dist/auto-proceed-loop-guard.d.ts.map +0 -1
  68. package/dist/auto-proceed-loop-guard.js.map +0 -7
  69. package/dist/bash-kill-guard.d.ts.map +0 -1
  70. package/dist/bash.d.ts.map +0 -1
  71. package/dist/bash.js.map +0 -7
  72. package/dist/batch-tool-use.d.ts.map +0 -1
  73. package/dist/batch-tool-use.js.map +0 -7
  74. package/dist/browser/artifacts.d.ts.map +0 -1
  75. package/dist/browser/index.d.ts.map +0 -1
  76. package/dist/browser/index.js.map +0 -7
  77. package/dist/browser/manager.d.ts.map +0 -1
  78. package/dist/browser/network-guard-proxy.d.ts.map +0 -1
  79. package/dist/browser/security.d.ts.map +0 -1
  80. package/dist/browser/tools.d.ts.map +0 -1
  81. package/dist/browser/types.d.ts.map +0 -1
  82. package/dist/builtin.d.ts.map +0 -1
  83. package/dist/builtin.js.map +0 -7
  84. package/dist/circuit-breaker.d.ts.map +0 -1
  85. package/dist/circuit-breaker.js.map +0 -7
  86. package/dist/codebase-index/background-indexer.d.ts.map +0 -1
  87. package/dist/codebase-index/bm25.d.ts.map +0 -1
  88. package/dist/codebase-index/circuit-breaker.d.ts.map +0 -1
  89. package/dist/codebase-index/codebase-index-tool.d.ts.map +0 -1
  90. package/dist/codebase-index/codebase-search-tool.d.ts.map +0 -1
  91. package/dist/codebase-index/codebase-stats-tool.d.ts.map +0 -1
  92. package/dist/codebase-index/dead-code-scan.d.ts.map +0 -1
  93. package/dist/codebase-index/generic-parser.d.ts.map +0 -1
  94. package/dist/codebase-index/gitignore.d.ts.map +0 -1
  95. package/dist/codebase-index/go-parser.d.ts.map +0 -1
  96. package/dist/codebase-index/index-service.d.ts.map +0 -1
  97. package/dist/codebase-index/index.d.ts.map +0 -1
  98. package/dist/codebase-index/index.js.map +0 -7
  99. package/dist/codebase-index/indexer.d.ts.map +0 -1
  100. package/dist/codebase-index/json-parser.d.ts.map +0 -1
  101. package/dist/codebase-index/languages.d.ts.map +0 -1
  102. package/dist/codebase-index/lsp-kind.d.ts.map +0 -1
  103. package/dist/codebase-index/parser-dispatch.d.ts.map +0 -1
  104. package/dist/codebase-index/project-server-cache.d.ts.map +0 -1
  105. package/dist/codebase-index/project-server-client.d.ts.map +0 -1
  106. package/dist/codebase-index/project-server-endpoint.d.ts.map +0 -1
  107. package/dist/codebase-index/project-server-protocol.d.ts.map +0 -1
  108. package/dist/codebase-index/project-server.d.ts.map +0 -1
  109. package/dist/codebase-index/project-server.js.map +0 -7
  110. package/dist/codebase-index/py-parser.d.ts.map +0 -1
  111. package/dist/codebase-index/refs-extractor.d.ts.map +0 -1
  112. package/dist/codebase-index/rs-parser.d.ts.map +0 -1
  113. package/dist/codebase-index/schema.d.ts.map +0 -1
  114. package/dist/codebase-index/spawn-gate.d.ts.map +0 -1
  115. package/dist/codebase-index/sqlite-runtime.d.ts.map +0 -1
  116. package/dist/codebase-index/ts-parser.d.ts.map +0 -1
  117. package/dist/codebase-index/worker-protocol.d.ts.map +0 -1
  118. package/dist/codebase-index/worker.d.ts.map +0 -1
  119. package/dist/codebase-index/worker.js.map +0 -7
  120. package/dist/codebase-index/writer-admin.d.ts.map +0 -1
  121. package/dist/codebase-index/writer-bulk-insert.d.ts.map +0 -1
  122. package/dist/codebase-index/writer-graph-helpers.d.ts.map +0 -1
  123. package/dist/codebase-index/writer-graph-reader.d.ts.map +0 -1
  124. package/dist/codebase-index/writer-helpers.d.ts.map +0 -1
  125. package/dist/codebase-index/writer-pragmas.d.ts.map +0 -1
  126. package/dist/codebase-index/writer-ref-mapper.d.ts.map +0 -1
  127. package/dist/codebase-index/writer-schema.d.ts.map +0 -1
  128. package/dist/codebase-index/writer-search-helpers.d.ts.map +0 -1
  129. package/dist/codebase-index/writer-store-pool.d.ts.map +0 -1
  130. package/dist/codebase-index/writer.d.ts.map +0 -1
  131. package/dist/codebase-index/yaml-parser.d.ts.map +0 -1
  132. package/dist/design.d.ts.map +0 -1
  133. package/dist/design.js.map +0 -7
  134. package/dist/diff.d.ts.map +0 -1
  135. package/dist/diff.js.map +0 -7
  136. package/dist/document.d.ts.map +0 -1
  137. package/dist/document.js.map +0 -7
  138. package/dist/e2e.d.ts.map +0 -1
  139. package/dist/e2e.js.map +0 -7
  140. package/dist/edit.d.ts.map +0 -1
  141. package/dist/edit.js.map +0 -7
  142. package/dist/exec-kill-guard.d.ts.map +0 -1
  143. package/dist/exec.d.ts.map +0 -1
  144. package/dist/exec.js.map +0 -7
  145. package/dist/fetch.d.ts.map +0 -1
  146. package/dist/fetch.js.map +0 -7
  147. package/dist/format.d.ts.map +0 -1
  148. package/dist/format.js.map +0 -7
  149. package/dist/git.d.ts.map +0 -1
  150. package/dist/git.js.map +0 -7
  151. package/dist/glob.d.ts.map +0 -1
  152. package/dist/glob.js.map +0 -7
  153. package/dist/grep.d.ts.map +0 -1
  154. package/dist/grep.js.map +0 -7
  155. package/dist/index.d.ts.map +0 -1
  156. package/dist/index.js.map +0 -7
  157. package/dist/install.d.ts.map +0 -1
  158. package/dist/install.js.map +0 -7
  159. package/dist/json.d.ts.map +0 -1
  160. package/dist/json.js.map +0 -7
  161. package/dist/kanban-board-inputs.d.ts.map +0 -1
  162. package/dist/kanban-decomposition-actions.d.ts.map +0 -1
  163. package/dist/kanban-detail-actions.d.ts.map +0 -1
  164. package/dist/kanban-evidence-bridge.d.ts.map +0 -1
  165. package/dist/kanban-presence.d.ts.map +0 -1
  166. package/dist/kanban-split-task-handler.d.ts.map +0 -1
  167. package/dist/kanban-task-inputs.d.ts.map +0 -1
  168. package/dist/kanban-tool-results.d.ts.map +0 -1
  169. package/dist/kanban-tool-schema.d.ts.map +0 -1
  170. package/dist/kanban-tool-types.d.ts.map +0 -1
  171. package/dist/kanban.d.ts.map +0 -1
  172. package/dist/kanban.js.map +0 -7
  173. package/dist/languages/detect.d.ts.map +0 -1
  174. package/dist/languages/diagnostics.d.ts.map +0 -1
  175. package/dist/languages/execute-tool.d.ts.map +0 -1
  176. package/dist/languages/execute.d.ts.map +0 -1
  177. package/dist/languages/index.d.ts.map +0 -1
  178. package/dist/languages/index.js.map +0 -7
  179. package/dist/languages/legacy-bridge.d.ts.map +0 -1
  180. package/dist/languages/package-tool.d.ts.map +0 -1
  181. package/dist/languages/plan.d.ts.map +0 -1
  182. package/dist/languages/profile-helpers.d.ts.map +0 -1
  183. package/dist/languages/profiles/additional.d.ts.map +0 -1
  184. package/dist/languages/profiles/primary.d.ts.map +0 -1
  185. package/dist/languages/registry.d.ts.map +0 -1
  186. package/dist/languages/tool.d.ts.map +0 -1
  187. package/dist/languages/types.d.ts.map +0 -1
  188. package/dist/lint.d.ts.map +0 -1
  189. package/dist/lint.js.map +0 -7
  190. package/dist/logs.d.ts.map +0 -1
  191. package/dist/logs.js.map +0 -7
  192. package/dist/memory.d.ts.map +0 -1
  193. package/dist/memory.js.map +0 -7
  194. package/dist/mode.d.ts.map +0 -1
  195. package/dist/mode.js.map +0 -7
  196. package/dist/next-steps.d.ts.map +0 -1
  197. package/dist/next-steps.js.map +0 -7
  198. package/dist/outdated.d.ts.map +0 -1
  199. package/dist/outdated.js.map +0 -7
  200. package/dist/pack.d.ts.map +0 -1
  201. package/dist/pack.js.map +0 -7
  202. package/dist/patch.d.ts.map +0 -1
  203. package/dist/patch.js.map +0 -7
  204. package/dist/plan.d.ts.map +0 -1
  205. package/dist/plan.js.map +0 -7
  206. package/dist/process-guardian.d.ts.map +0 -1
  207. package/dist/process-registry-persistent.d.ts.map +0 -1
  208. package/dist/process-registry.d.ts.map +0 -1
  209. package/dist/process-registry.js.map +0 -7
  210. package/dist/ps-slash.d.ts.map +0 -1
  211. package/dist/ps-slash.js.map +0 -7
  212. package/dist/read.d.ts.map +0 -1
  213. package/dist/read.js.map +0 -7
  214. package/dist/replace.d.ts.map +0 -1
  215. package/dist/replace.js.map +0 -7
  216. package/dist/scaffold.d.ts.map +0 -1
  217. package/dist/scaffold.js.map +0 -7
  218. package/dist/search.d.ts.map +0 -1
  219. package/dist/search.js.map +0 -7
  220. package/dist/session-kanban.d.ts.map +0 -1
  221. package/dist/session-kanban.js.map +0 -7
  222. package/dist/set-working-dir.d.ts.map +0 -1
  223. package/dist/skill.d.ts.map +0 -1
  224. package/dist/skill.js.map +0 -7
  225. package/dist/spawn-background.d.ts.map +0 -1
  226. package/dist/task.d.ts.map +0 -1
  227. package/dist/task.js.map +0 -7
  228. package/dist/test.d.ts.map +0 -1
  229. package/dist/test.js.map +0 -7
  230. package/dist/todo.d.ts.map +0 -1
  231. package/dist/todo.js.map +0 -7
  232. package/dist/tool-diff.d.ts.map +0 -1
  233. package/dist/tool-diff.js.map +0 -7
  234. package/dist/tool-help.d.ts.map +0 -1
  235. package/dist/tool-help.js.map +0 -7
  236. package/dist/tool-icon-map.d.ts.map +0 -1
  237. package/dist/tool-icons.d.ts.map +0 -1
  238. package/dist/tool-icons.js.map +0 -7
  239. package/dist/tool-search.d.ts.map +0 -1
  240. package/dist/tool-search.js.map +0 -7
  241. package/dist/tool-summary.d.ts.map +0 -1
  242. package/dist/tool-summary.js.map +0 -7
  243. package/dist/tool-tier.d.ts.map +0 -1
  244. package/dist/tool-tier.js.map +0 -7
  245. package/dist/tool-use.d.ts.map +0 -1
  246. package/dist/tool-use.js.map +0 -7
  247. package/dist/tree.d.ts.map +0 -1
  248. package/dist/tree.js.map +0 -7
  249. package/dist/typecheck.d.ts.map +0 -1
  250. package/dist/typecheck.js.map +0 -7
  251. package/dist/win32.d.ts.map +0 -1
  252. package/dist/win32.js.map +0 -7
  253. package/dist/write.d.ts.map +0 -1
  254. package/dist/write.js.map +0 -7
package/dist/outdated.js CHANGED
@@ -52,12 +52,13 @@ function ensureInsideRoot(absPath, ctx) {
52
52
  function safeResolve(input, ctx) {
53
53
  return ensureInsideRoot(resolvePath(input, ctx), ctx);
54
54
  }
55
- async function assertRealInsideRoot(absPath, ctx) {
56
- if (ctx.allowOutsideProjectRoot) return;
55
+ async function resolveRealInsideRoot(absPath, ctx) {
56
+ if (ctx.allowOutsideProjectRoot) return absPath;
57
57
  const realRoots = await Promise.all(
58
58
  allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r)))
59
59
  );
60
60
  let probe = absPath;
61
+ const pendingTail = [];
61
62
  for (; ; ) {
62
63
  let real;
63
64
  try {
@@ -65,13 +66,16 @@ async function assertRealInsideRoot(absPath, ctx) {
65
66
  } catch (err) {
66
67
  if (err.code === "ENOENT") {
67
68
  const parent = path.dirname(probe);
68
- if (parent === probe) return;
69
+ if (parent === probe) return absPath;
70
+ pendingTail.unshift(path.basename(probe));
69
71
  probe = parent;
70
72
  continue;
71
73
  }
72
74
  throw err;
73
75
  }
74
- if (isInsideAny(real, realRoots)) return;
76
+ if (isInsideAny(real, realRoots)) {
77
+ return pendingTail.length > 0 ? path.join(real, ...pendingTail) : real;
78
+ }
75
79
  throw new Error(
76
80
  `Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
77
81
  );
@@ -79,8 +83,7 @@ async function assertRealInsideRoot(absPath, ctx) {
79
83
  }
80
84
  async function safeResolveReal(input, ctx) {
81
85
  const abs = safeResolve(input, ctx);
82
- await assertRealInsideRoot(abs, ctx);
83
- return abs;
86
+ return await resolveRealInsideRoot(abs, ctx);
84
87
  }
85
88
  function collapseCarriageReturns(text) {
86
89
  const lf = text.replace(/\r\n/g, "\n");
@@ -1682,8 +1685,8 @@ async function scanDirectory(directory, depth, profiles, limits, state, extraIgn
1682
1685
  collectFileEvidence(directory, fullPath, entry.name, profiles, state);
1683
1686
  }
1684
1687
  }
1685
- function collectFileEvidence(directory, fullPath, basename4, profiles, state) {
1686
- const lower = basename4.toLowerCase();
1688
+ function collectFileEvidence(directory, fullPath, basename5, profiles, state) {
1689
+ const lower = basename5.toLowerCase();
1687
1690
  const extension = path3.extname(lower);
1688
1691
  for (const profile of profiles) {
1689
1692
  const detector = profile.detectors.find(
@@ -1694,7 +1697,7 @@ function collectFileEvidence(directory, fullPath, basename4, profiles, state) {
1694
1697
  candidate.evidence.push({
1695
1698
  kind: detector.kind,
1696
1699
  path: fullPath,
1697
- value: basename4,
1700
+ value: basename5,
1698
1701
  weight: detector.weight
1699
1702
  });
1700
1703
  if (detector.kind === "manifest" || detector.kind === "config") {
@@ -2442,11 +2445,12 @@ function createOutputSpool(opts) {
2442
2445
  return {
2443
2446
  write(text) {
2444
2447
  if (finalized || !text) return;
2445
- totalBytes += Buffer.byteLength(text, "utf8");
2448
+ const textBytes = Buffer.byteLength(text, "utf8");
2449
+ totalBytes += textBytes;
2446
2450
  if (!stream && !failed) {
2447
- if (headBytes + text.length <= threshold) {
2451
+ if (headBytes + textBytes <= threshold) {
2448
2452
  head += text;
2449
- headBytes += text.length;
2453
+ headBytes += textBytes;
2450
2454
  return;
2451
2455
  }
2452
2456
  head += text;
@@ -2456,7 +2460,7 @@ function createOutputSpool(opts) {
2456
2460
  }
2457
2461
  if (stream) {
2458
2462
  if (stream.writableLength > SPOOL_WRITE_HWM_BYTES) {
2459
- droppedBytes += Buffer.byteLength(text, "utf8");
2463
+ droppedBytes += textBytes;
2460
2464
  return;
2461
2465
  }
2462
2466
  stream.write(text);
@@ -3140,8 +3144,11 @@ async function* spawnStream(opts) {
3140
3144
  const max = opts.maxBytes ?? 2e5;
3141
3145
  const flushAt = opts.flushBytes ?? 4 * 1024;
3142
3146
  const maxQueue = opts.maxQueueSize ?? 500;
3147
+ const maxQueueBytes = opts.maxQueueBytes ?? 1024 * 1024;
3143
3148
  let stdout = "";
3144
3149
  let stderr = "";
3150
+ let stdoutRetainedBytes = 0;
3151
+ let stderrRetainedBytes = 0;
3145
3152
  let pending = "";
3146
3153
  let error;
3147
3154
  const spool = createOutputSpool({ tool: opts.cmd, thresholdBytes: max });
@@ -3183,6 +3190,7 @@ async function* spawnStream(opts) {
3183
3190
  });
3184
3191
  }
3185
3192
  const queue = [];
3193
+ let queuedBytes = 0;
3186
3194
  let waiter;
3187
3195
  let paused = false;
3188
3196
  const wake = () => {
@@ -3193,7 +3201,7 @@ async function* spawnStream(opts) {
3193
3201
  }
3194
3202
  };
3195
3203
  const resume = () => {
3196
- if (paused && queue.length < maxQueue) {
3204
+ if (paused && queue.length <= Math.floor(maxQueue / 2) && queuedBytes <= Math.floor(maxQueueBytes / 2)) {
3197
3205
  paused = false;
3198
3206
  child.stdout?.resume();
3199
3207
  child.stderr?.resume();
@@ -3203,11 +3211,16 @@ async function* spawnStream(opts) {
3203
3211
  const s = c.toString();
3204
3212
  stdoutBytes += c.byteLength;
3205
3213
  emitProcessOutput({ pid, stream: "stdout", chunk: c });
3206
- if (stdout.length < max) stdout += s;
3214
+ if (stdoutRetainedBytes < max) {
3215
+ const retained = utf8Prefix(s, max - stdoutRetainedBytes);
3216
+ stdout += retained;
3217
+ stdoutRetainedBytes += Buffer.byteLength(retained, "utf8");
3218
+ }
3207
3219
  spool.write(s);
3208
- queue.push({ kind: "out", data: s });
3220
+ queue.push({ kind: "out", data: s, bytes: c.byteLength });
3221
+ queuedBytes += c.byteLength;
3209
3222
  wake();
3210
- if (!paused && queue.length >= maxQueue) {
3223
+ if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
3211
3224
  paused = true;
3212
3225
  child.stdout?.pause();
3213
3226
  child.stderr?.pause();
@@ -3217,11 +3230,16 @@ async function* spawnStream(opts) {
3217
3230
  const s = c.toString();
3218
3231
  stderrBytes += c.byteLength;
3219
3232
  emitProcessOutput({ pid, stream: "stderr", chunk: c });
3220
- if (stderr.length < max) stderr += s;
3233
+ if (stderrRetainedBytes < max) {
3234
+ const retained = utf8Prefix(s, max - stderrRetainedBytes);
3235
+ stderr += retained;
3236
+ stderrRetainedBytes += Buffer.byteLength(retained, "utf8");
3237
+ }
3221
3238
  spool.write(s);
3222
- queue.push({ kind: "err", data: s });
3239
+ queue.push({ kind: "err", data: s, bytes: c.byteLength });
3240
+ queuedBytes += c.byteLength;
3223
3241
  wake();
3224
- if (!paused && queue.length >= maxQueue) {
3242
+ if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
3225
3243
  paused = true;
3226
3244
  child.stdout?.pause();
3227
3245
  child.stderr?.pause();
@@ -3231,7 +3249,9 @@ async function* spawnStream(opts) {
3231
3249
  child.stderr?.on("data", onErr);
3232
3250
  child.on("error", (e) => {
3233
3251
  error = e.message;
3234
- queue.push({ kind: "error", data: e.message });
3252
+ const bytes = Buffer.byteLength(e.message, "utf8");
3253
+ queue.push({ kind: "error", data: e.message, bytes });
3254
+ queuedBytes += bytes;
3235
3255
  wake();
3236
3256
  });
3237
3257
  const completeTelemetry = (code, signal, timedOut = false) => {
@@ -3252,7 +3272,13 @@ async function* spawnStream(opts) {
3252
3272
  if (typeof pid === "number") registry.unregister(pid);
3253
3273
  const exitCode2 = code ?? (signal ? 1 : 0);
3254
3274
  completeTelemetry(exitCode2, signal ?? void 0);
3255
- queue.push({ kind: "close", data: "", code: exitCode2, ...signal ? { signal } : {} });
3275
+ queue.push({
3276
+ kind: "close",
3277
+ data: "",
3278
+ bytes: 0,
3279
+ code: exitCode2,
3280
+ ...signal ? { signal } : {}
3281
+ });
3256
3282
  wake();
3257
3283
  });
3258
3284
  const onAbort = () => {
@@ -3264,7 +3290,7 @@ async function* spawnStream(opts) {
3264
3290
  } catch {
3265
3291
  }
3266
3292
  }
3267
- queue.push({ kind: "close", data: "", code: 124 });
3293
+ queue.push({ kind: "close", data: "", bytes: 0, code: 124 });
3268
3294
  completeTelemetry(124, "SIGKILL", true);
3269
3295
  wake();
3270
3296
  };
@@ -3282,6 +3308,7 @@ async function* spawnStream(opts) {
3282
3308
  });
3283
3309
  }
3284
3310
  const chunk = queue.shift();
3311
+ queuedBytes -= chunk.bytes;
3285
3312
  resume();
3286
3313
  if (chunk.kind === "close") {
3287
3314
  if (!spawnFailed) exitCode = chunk.code ?? 0;
@@ -3308,7 +3335,7 @@ async function* spawnStream(opts) {
3308
3335
  stdout: spooled ? stdout + spoolNote(spooled) : stdout,
3309
3336
  stderr,
3310
3337
  exitCode,
3311
- truncated: stdout.length >= max || stderr.length >= max,
3338
+ truncated: stdoutBytes > stdoutRetainedBytes || stderrBytes > stderrRetainedBytes,
3312
3339
  error,
3313
3340
  spoolPath: spooled?.path,
3314
3341
  spoolBytes: spooled?.bytes
@@ -3332,13 +3359,27 @@ async function* spawnStream(opts) {
3332
3359
  }
3333
3360
  }
3334
3361
  }
3362
+ function utf8Prefix(text, maxBytes) {
3363
+ if (maxBytes <= 0) return "";
3364
+ if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
3365
+ let low = 0;
3366
+ let high = text.length;
3367
+ while (low < high) {
3368
+ const mid = Math.ceil((low + high) / 2);
3369
+ if (Buffer.byteLength(text.slice(0, mid), "utf8") <= maxBytes) low = mid;
3370
+ else high = mid - 1;
3371
+ }
3372
+ let end = low;
3373
+ if (end > 0 && /[\uD800-\uDBFF]/.test(text[end - 1])) end--;
3374
+ return text.slice(0, end);
3375
+ }
3335
3376
  var isWin;
3336
3377
  var init_spawn_stream = __esm({
3337
3378
  "src/_spawn-stream.ts"() {
3338
3379
  "use strict";
3339
3380
  init_output_spool();
3340
- init_process_registry();
3341
3381
  init_win32_resolve();
3382
+ init_process_registry();
3342
3383
  isWin = process.platform === "win32";
3343
3384
  }
3344
3385
  });
@@ -3348,6 +3389,7 @@ import * as fs3 from "node:fs/promises";
3348
3389
  import * as path6 from "node:path";
3349
3390
  async function planLanguageOperation(options) {
3350
3391
  validateOperationOptions(options.operation, options.operationOptions?.packages);
3392
+ validateFilterOption(options.operationOptions?.filter);
3351
3393
  const detection = await detectLanguageWorkspaces(options);
3352
3394
  const candidates = detection.workspaces.filter(
3353
3395
  (workspace) => workspace.capabilities.includes(options.operation)
@@ -3520,6 +3562,15 @@ function workspaceDepth(workspaceRoot, projectRoot) {
3520
3562
  const relative5 = path6.relative(path6.resolve(projectRoot), path6.resolve(workspaceRoot));
3521
3563
  return relative5 === "" ? 0 : relative5.split(path6.sep).length;
3522
3564
  }
3565
+ function validateFilterOption(filter) {
3566
+ if (filter === void 0) return;
3567
+ if (filter.startsWith("-") || filter.includes(" --")) {
3568
+ throw new Error(`Invalid test filter (parsed as a runner option): "${filter}"`);
3569
+ }
3570
+ if (filter.length > MAX_ARGUMENT_LENGTH || /[\r\n\0]/.test(filter)) {
3571
+ throw new Error("Invalid test filter (oversized or contains a control character)");
3572
+ }
3573
+ }
3523
3574
  function validateOperationOptions(operation, packages) {
3524
3575
  if (!operation.startsWith("package-") || !packages) return;
3525
3576
  for (const value of packages) {
@@ -3561,7 +3612,7 @@ var init_plan = __esm({
3561
3612
  MAX_ARGUMENT_LENGTH = 4096;
3562
3613
  PACKAGE_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9*+._~^<>=|-]+)?$/i;
3563
3614
  COMPOSER_PACKAGE_RE = /^[a-z0-9_.-]+\/[a-z0-9_.-]+(?::[a-z0-9*+._~^<>=|-]+)?$/i;
3564
- GO_MODULE_RE = /^(?:[a-z0-9.-]+\.)+[a-z0-9.-]+\/[a-z0-9._~+/@-]+$/i;
3615
+ GO_MODULE_RE = /^(?:[a-z0-9-]+\.)+[a-z0-9-]+\/[a-z0-9._~+/@-]+$/i;
3565
3616
  PYTHON_PACKAGE_RE = /^[a-z0-9._-]+(?:\[[a-z0-9._,-]+\])?==[a-z0-9*+._!~-]+$/i;
3566
3617
  }
3567
3618
  });
@@ -4159,6 +4210,9 @@ var init_execute_tool = __esm({
4159
4210
  useInstead: ["language_info", "install", "audit", "outdated"]
4160
4211
  },
4161
4212
  permission: "confirm",
4213
+ // WS-046: gives permission decisions something to key on.
4214
+ // The action performed (build/test/lint); the cwd is where, not what.
4215
+ subjectKey: "action",
4162
4216
  mutating: true,
4163
4217
  riskTier: "standard",
4164
4218
  capabilities: ["shell.restricted", "fs.write"],
@@ -4391,6 +4445,9 @@ var init_package_tool = __esm({
4391
4445
  useInstead: ["language_info", "language", "install", "audit", "outdated"]
4392
4446
  },
4393
4447
  permission: "confirm",
4448
+ // WS-046: gives permission decisions something to key on.
4449
+ // The package operation performed — install/remove are not interchangeable.
4450
+ subjectKey: "operation",
4394
4451
  mutating: true,
4395
4452
  riskTier: "destructive",
4396
4453
  capabilities: ["shell.restricted", "fs.write", "net.outbound", "package.install"],
@@ -4894,6 +4951,9 @@ var outdatedTool = {
4894
4951
  // tool.confirm_needed flow on every invocation. M-1 originally
4895
4952
  // fixed four sibling tools (mcp_control, shellcheck, shellcheck (scan mode),
4896
4953
  // search) but missed this one; applying the same contract here.
4954
+ // WS-046: gives permission decisions something to key on.
4955
+ // The working directory scanned.
4956
+ subjectKey: "cwd",
4897
4957
  mutating: true,
4898
4958
  // Capability is outbound network — the tool only hits the package
4899
4959
  // registry over HTTP, never touches the filesystem or runs shell.