@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
@@ -3,27 +3,28 @@ import {
3
3
  } from "./chunk-BTSFTGIL.js";
4
4
  import {
5
5
  getPersistentProcessRegistry
6
- } from "./chunk-NKNOBUBU.js";
6
+ } from "./chunk-QGF2C7TF.js";
7
7
  import {
8
8
  createOutputSpool,
9
9
  spoolNote
10
- } from "./chunk-2VSZC6QM.js";
10
+ } from "./chunk-SCMRM6NL.js";
11
11
  import {
12
12
  getProcessRegistry,
13
13
  redactCommand
14
- } from "./chunk-3PVYQFRQ.js";
14
+ } from "./chunk-ESLPBT5A.js";
15
15
  import {
16
16
  buildWin32CmdShimInvocation,
17
17
  resolveWin32Command
18
- } from "./chunk-V43EQ6VR.js";
18
+ } from "./chunk-KE7A76VC.js";
19
19
  import {
20
20
  COMMAND_OUTPUT_MAX_BYTES,
21
21
  normalizeCommandOutput,
22
22
  safeResolveReal
23
- } from "./chunk-QLF74MSV.js";
23
+ } from "./chunk-ZUFHAHTE.js";
24
24
 
25
25
  // src/exec.ts
26
26
  import { spawn } from "node:child_process";
27
+ import { StringDecoder } from "node:string_decoder";
27
28
  import {
28
29
  emitProcessCompleted,
29
30
  emitProcessOutput,
@@ -85,9 +86,9 @@ var RULES = [
85
86
  level: "destructive",
86
87
  test: (cmd, args) => {
87
88
  if (cmd !== "powershell" && cmd !== "pwsh") return false;
88
- const hasRecurse = argMatches(args, /^-(?:r|recurse)/i);
89
- const hasForce = argMatches(args, /^-(?:f|force)$/i);
90
- if (argMatches(args, /^-whatif$/i)) return false;
89
+ const hasRecurse = argMatches(args, /^-(?:r|recurse)(?::\$true)?$/i);
90
+ const hasForce = argMatches(args, /^-(?:f|force)(?::\$true)?$/i);
91
+ if (argMatches(args, /^-whatif(?::\$true)?$/i)) return false;
91
92
  return hasRecurse && hasForce;
92
93
  },
93
94
  reason: "Remove-Item with -Recurse -Force"
@@ -99,7 +100,9 @@ var RULES = [
99
100
  test: (cmd, args) => {
100
101
  if (cmd !== "powershell" && cmd !== "pwsh") return false;
101
102
  return args.some(
102
- (a) => /^(?:Format-Volume|Clear-Disk|Initialize-Disk|Remove-Partition|Clear-Volume)(?:\s|$)/i.test(a)
103
+ (a) => /^(?:Format-Volume|Clear-Disk|Initialize-Disk|Remove-Partition|Clear-Volume)(?:\s|$)/i.test(
104
+ a
105
+ )
103
106
  );
104
107
  },
105
108
  reason: "PowerShell disk/volume partition destruction"
@@ -233,6 +236,7 @@ var RULES = [
233
236
  continue;
234
237
  }
235
238
  if (a.startsWith("--force")) return true;
239
+ if (/^-[a-z]+$/i.test(a) && a.toLowerCase().includes("f")) return true;
236
240
  }
237
241
  return false;
238
242
  },
@@ -424,12 +428,14 @@ var RULES = [
424
428
  }
425
429
  ];
426
430
  function detectDanger(cmd, args, bypass) {
431
+ if (typeof cmd !== "string") return { level: "safe", reasons: [] };
432
+ const safeArgs = Array.isArray(args) ? args : [];
427
433
  const reasons = [];
428
434
  let level = "safe";
429
435
  let matchedRule;
430
436
  for (const rule of RULES) {
431
437
  if (bypass?.has(rule.id)) continue;
432
- if (!rule.test(cmd, args)) continue;
438
+ if (!rule.test(cmd, safeArgs)) continue;
433
439
  reasons.push(rule.reason);
434
440
  if (matchedRule === void 0 || levelRank(rule.level) >= levelRank(level)) {
435
441
  matchedRule = rule.id;
@@ -1537,7 +1543,8 @@ var execTool = {
1537
1543
  };
1538
1544
  }
1539
1545
  const args = (input.args ?? []).slice(0, MAX_ARGS);
1540
- const timeout = Math.max(1, Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS));
1546
+ const rawTimeout = typeof input.timeout === "number" && !Number.isNaN(input.timeout) ? input.timeout : DEFAULT_TIMEOUT_MS;
1547
+ const timeout = Math.max(1, Math.min(rawTimeout, MAX_TIMEOUT_MS));
1541
1548
  const danger = detectDanger(cmd, args, dangerBypass);
1542
1549
  const killCheck = await checkExecKillCommand(cmd, args);
1543
1550
  if (killCheck.blocked) {
@@ -1582,7 +1589,30 @@ var execTool = {
1582
1589
  };
1583
1590
  }
1584
1591
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
1592
+ if (signal.aborted) {
1593
+ return {
1594
+ command: cmd,
1595
+ args,
1596
+ stdout: "",
1597
+ stderr: "Aborted",
1598
+ exitCode: 124,
1599
+ truncated: false,
1600
+ allowed: true,
1601
+ danger
1602
+ };
1603
+ }
1585
1604
  return runCommand(cmd, args, cwd, timeout, signal, ctx.session?.id, danger);
1605
+ },
1606
+ async cleanup(_input, ctx) {
1607
+ const registry = getProcessRegistry();
1608
+ const sessionId = ctx.session?.id;
1609
+ if (!sessionId) return;
1610
+ for (const entry of registry.bySession(sessionId)) {
1611
+ if (entry.name !== "exec") continue;
1612
+ if (entry.child && entry.child.exitCode !== null) continue;
1613
+ if (entry.protected) continue;
1614
+ registry.kill(entry.pid, { force: true });
1615
+ }
1586
1616
  }
1587
1617
  };
1588
1618
  function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
@@ -1602,6 +1632,20 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1602
1632
  let telemetryCompleted = false;
1603
1633
  let timedOut = false;
1604
1634
  const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT });
1635
+ if (signal.aborted) {
1636
+ spool.finalize();
1637
+ finish({
1638
+ command: cmd,
1639
+ args,
1640
+ stdout: "",
1641
+ stderr: "Aborted",
1642
+ exitCode: 124,
1643
+ truncated: false,
1644
+ allowed: true,
1645
+ danger
1646
+ });
1647
+ return;
1648
+ }
1605
1649
  const resolved = resolveWin32Command(cmd);
1606
1650
  const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
1607
1651
  const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
@@ -1711,19 +1755,25 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1711
1755
  if (signal.aborted) onAbort();
1712
1756
  else signal.addEventListener("abort", onAbort, { once: true });
1713
1757
  }
1758
+ const stdoutDecoder = new StringDecoder("utf8");
1759
+ const stderrDecoder = new StringDecoder("utf8");
1714
1760
  child.stdout?.on("data", (chunk) => {
1715
- const text = chunk.toString();
1761
+ const text = stdoutDecoder.write(chunk);
1716
1762
  stdoutBytes += chunk.byteLength;
1717
1763
  emitProcessOutput({ pid, stream: "stdout", chunk });
1718
- if (stdout.length < MAX_OUTPUT) stdout += text;
1719
- spool.write(text);
1764
+ if (text.length > 0) {
1765
+ if (stdout.length < MAX_OUTPUT) stdout += text.slice(0, MAX_OUTPUT - stdout.length);
1766
+ spool.write(text);
1767
+ }
1720
1768
  });
1721
1769
  child.stderr?.on("data", (chunk) => {
1722
- const text = chunk.toString();
1770
+ const text = stderrDecoder.write(chunk);
1723
1771
  stderrBytes += chunk.byteLength;
1724
1772
  emitProcessOutput({ pid, stream: "stderr", chunk });
1725
- if (stderr.length < MAX_OUTPUT) stderr += text;
1726
- spool.write(text);
1773
+ if (text.length > 0) {
1774
+ if (stderr.length < MAX_OUTPUT) stderr += text.slice(0, MAX_OUTPUT - stderr.length);
1775
+ spool.write(text);
1776
+ }
1727
1777
  });
1728
1778
  child.on("close", (code) => {
1729
1779
  clearTimeout(timer);
@@ -1733,14 +1783,25 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1733
1783
  const exitCode = killed ? 124 : code ?? 1;
1734
1784
  emitCompletedOnce(exitCode, pid);
1735
1785
  registry.afterCall(durationMs, exitCode !== 0);
1786
+ const stdoutTail = stdoutDecoder.end();
1787
+ if (stdoutTail) {
1788
+ if (stdout.length < MAX_OUTPUT) stdout += stdoutTail.slice(0, MAX_OUTPUT - stdout.length);
1789
+ spool.write(stdoutTail);
1790
+ }
1791
+ const stderrTail = stderrDecoder.end();
1792
+ if (stderrTail) {
1793
+ if (stderr.length < MAX_OUTPUT) stderr += stderrTail.slice(0, MAX_OUTPUT - stderr.length);
1794
+ spool.write(stderrTail);
1795
+ }
1736
1796
  const spooled = spool.finalize();
1797
+ const isTruncated = stdoutBytes > MAX_OUTPUT || stderrBytes > MAX_OUTPUT || Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES;
1737
1798
  finish({
1738
1799
  command: cmd,
1739
1800
  args,
1740
1801
  stdout: normalizeCommandOutput(stdout) + (spooled ? spoolNote(spooled) : ""),
1741
1802
  stderr: normalizeCommandOutput(stderr),
1742
1803
  exitCode,
1743
- truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
1804
+ truncated: isTruncated,
1744
1805
  allowed: true,
1745
1806
  danger
1746
1807
  });
@@ -1760,4 +1821,4 @@ export {
1760
1821
  getExecAllowlist,
1761
1822
  execTool
1762
1823
  };
1763
- //# sourceMappingURL=chunk-PMHVSHMU.js.map
1824
+ //# sourceMappingURL=chunk-OTPTQBYE.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  createOutputSpool,
3
3
  spoolNote
4
- } from "./chunk-2VSZC6QM.js";
4
+ } from "./chunk-SCMRM6NL.js";
5
5
  import {
6
6
  getProcessRegistry,
7
7
  redactCommand
8
- } from "./chunk-3PVYQFRQ.js";
8
+ } from "./chunk-ESLPBT5A.js";
9
9
  import {
10
10
  buildWin32CmdShimInvocation,
11
11
  resolveWin32Command
12
- } from "./chunk-V43EQ6VR.js";
12
+ } from "./chunk-KE7A76VC.js";
13
13
 
14
14
  // src/_spawn-stream.ts
15
15
  import { spawn } from "node:child_process";
@@ -258,4 +258,4 @@ function utf8Prefix(text, maxBytes) {
258
258
  export {
259
259
  spawnStream
260
260
  };
261
- //# sourceMappingURL=chunk-ZHH3BEWU.js.map
261
+ //# sourceMappingURL=chunk-OYP27E3I.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveIndexDir
3
- } from "./chunk-TFMVHYJI.js";
3
+ } from "./chunk-J43O3BOX.js";
4
4
 
5
5
  // src/codebase-index/binary-frame.ts
6
6
  import { decode, encode } from "@msgpack/msgpack";
@@ -120,4 +120,4 @@ export {
120
120
  PROJECT_INDEX_SERVER_MAX_FRAME_CHARS,
121
121
  encodeProjectServerMessage
122
122
  };
123
- //# sourceMappingURL=chunk-W4GAAI3M.js.map
123
+ //# sourceMappingURL=chunk-PV6HIZQD.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getProcessRegistry
3
- } from "./chunk-3PVYQFRQ.js";
3
+ } from "./chunk-ESLPBT5A.js";
4
4
 
5
5
  // src/process-registry-persistent.ts
6
6
  import * as fs from "node:fs/promises";
@@ -519,4 +519,4 @@ export {
519
519
  getPersistentProcessRegistry,
520
520
  resetPersistentProcessRegistry
521
521
  };
522
- //# sourceMappingURL=chunk-NKNOBUBU.js.map
522
+ //# sourceMappingURL=chunk-QGF2C7TF.js.map
@@ -56,17 +56,19 @@ var searchTool = {
56
56
  return final;
57
57
  },
58
58
  async *executeStream(input, ctx, opts) {
59
- if (!input?.query || input.query.trim() === "") {
59
+ if (!input?.query || typeof input.query !== "string" || input.query.trim() === "") {
60
60
  throw new ToolValidationError({
61
61
  message: "search: query is required and must be a non-empty string",
62
62
  field: "query"
63
63
  });
64
64
  }
65
65
  const signal = opts?.signal ?? ctx?.signal ?? new AbortController().signal;
66
- const num = Math.max(1, Math.min(input.num_results ?? DEFAULT_NUM, MAX_RESULTS));
66
+ const rawNum = typeof input.num_results === "number" && !Number.isNaN(input.num_results) ? input.num_results : DEFAULT_NUM;
67
+ const num = Math.max(1, Math.min(Math.floor(rawNum), MAX_RESULTS));
67
68
  const source = input.source ?? "duckduckgo";
68
69
  const skipCache = input.skip_cache ?? false;
69
- const cacheKey = `${source}:${input.query}`;
70
+ const normalizedQuery = input.query.trim().toLowerCase().replace(/\s+/g, " ");
71
+ const cacheKey = `${source}:${normalizedQuery}`;
70
72
  if (!skipCache) {
71
73
  const entry = cache.get(cacheKey);
72
74
  if (entry && Date.now() - entry.timestamp < CACHE_TTL_MS) {
@@ -426,11 +428,23 @@ function capSnippet(snippet) {
426
428
  return snippet.length > MAX_SNIPPET_CHARS ? `${snippet.slice(0, MAX_SNIPPET_CHARS - 1)}\u2026` : snippet;
427
429
  }
428
430
  function decodeHtmlEntities(text) {
429
- return text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
431
+ return text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/gi, "'").replace(/&#039;/g, "'").replace(/&nbsp;/g, " ").replace(/&ndash;/g, "\u2013").replace(/&mdash;/g, "\u2014").replace(/&ldquo;|&rdquo;/g, '"').replace(/&lsquo;|&rsquo;/g, "'").replace(/&#(\d+);/g, (_, code) => {
432
+ try {
433
+ return String.fromCodePoint(Number(code));
434
+ } catch {
435
+ return _;
436
+ }
437
+ }).replace(/&#x([0-9a-f]+);/gi, (_, hex) => {
438
+ try {
439
+ return String.fromCodePoint(parseInt(hex, 16));
440
+ } catch {
441
+ return _;
442
+ }
443
+ });
430
444
  }
431
445
 
432
446
  export {
433
447
  searchTool,
434
448
  __clearSearchCache
435
449
  };
436
- //# sourceMappingURL=chunk-PQTONQ4P.js.map
450
+ //# sourceMappingURL=chunk-QPNYDH5B.js.map
@@ -1,17 +1,18 @@
1
1
  import {
2
2
  tryLegacyCodeOperation
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/typecheck.ts
14
14
  import * as path from "node:path";
15
+ import { ToolValidationError } from "@wrongstack/core/types";
15
16
  var typecheckTool = {
16
17
  name: "typecheck",
17
18
  category: "Code Quality",
@@ -50,6 +51,21 @@ var typecheckTool = {
50
51
  async *executeStream(input, ctx, opts) {
51
52
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
52
53
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
54
+ signal.throwIfAborted();
55
+ if (input.all && input.project !== void 0) {
56
+ throw new ToolValidationError({
57
+ message: 'typecheck: cannot specify both "all: true" and "project"',
58
+ field: "project"
59
+ });
60
+ }
61
+ if (input.project !== void 0) {
62
+ if (typeof input.project !== "string" || !input.project.trim()) {
63
+ throw new ToolValidationError({
64
+ message: 'typecheck: "project" must be a non-empty string path',
65
+ field: "project"
66
+ });
67
+ }
68
+ }
53
69
  const bridge = await tryLegacyCodeOperation("semantic", {
54
70
  cwd,
55
71
  projectRoot: ctx.projectRoot,
@@ -86,7 +102,7 @@ var typecheckTool = {
86
102
  cmdArgs = ["tsc", ...tscArgs];
87
103
  }
88
104
  } else {
89
- const tsconfig = input.project ? await safeResolveReal(input.project, ctx) : await findTsConfig(cwd);
105
+ const tsconfig = input.project ? await safeResolveReal(input.project.trim(), ctx) : await findTsConfig(cwd);
90
106
  const tscArgs = ["--noEmit"];
91
107
  if (input.strict) tscArgs.push("--strict");
92
108
  if (tsconfig) tscArgs.push("--project", tsconfig);
@@ -104,8 +120,13 @@ var typecheckTool = {
104
120
  });
105
121
  const combined = `${result.stdout}
106
122
  ${result.stderr}`;
107
- const errors = [...combined.matchAll(/^.*\berror TS\d+:/gm)].length;
108
- const warnings = [...combined.matchAll(/^.*\bwarning TS\d+:/gm)].length;
123
+ let errors = [...combined.matchAll(/^.*\berror TS\d+:/gmi)].length;
124
+ const warnings = [...combined.matchAll(/^.*\bwarning TS\d+:/gmi)].length;
125
+ if (errors === 0 && result.exitCode !== 0) {
126
+ errors = 1;
127
+ }
128
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
129
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
109
130
  yield {
110
131
  type: "final",
111
132
  output: {
@@ -113,7 +134,7 @@ ${result.stderr}`;
113
134
  exit_code: result.exitCode,
114
135
  errors,
115
136
  warnings,
116
- output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
137
+ output: normalizeCommandOutput(rawOutput),
117
138
  truncated: result.truncated
118
139
  }
119
140
  };
@@ -121,7 +142,14 @@ ${result.stderr}`;
121
142
  };
122
143
  async function findTsConfig(cwd) {
123
144
  const { stat } = await import("node:fs/promises");
124
- const candidates = ["tsconfig.json", "tsconfig.base.json"];
145
+ const candidates = [
146
+ "tsconfig.json",
147
+ "tsconfig.base.json",
148
+ "tsconfig.build.json",
149
+ "tsconfig.app.json",
150
+ "tsconfig.node.json",
151
+ "tsconfig.lib.json"
152
+ ];
125
153
  for (const f of candidates) {
126
154
  try {
127
155
  const s = await stat(path.join(cwd, f));
@@ -135,4 +163,4 @@ async function findTsConfig(cwd) {
135
163
  export {
136
164
  typecheckTool
137
165
  };
138
- //# sourceMappingURL=chunk-VFF3Q5Z3.js.map
166
+ //# sourceMappingURL=chunk-QSNVDHOZ.js.map
@@ -214,6 +214,7 @@ function unbracket(hostname) {
214
214
  function safeBrowserUrl(rawUrl) {
215
215
  try {
216
216
  const url = new URL(rawUrl);
217
+ if (url.protocol !== "http:" && url.protocol !== "https:") return "about:blank";
217
218
  url.username = "";
218
219
  url.password = "";
219
220
  url.search = "";
@@ -224,8 +225,11 @@ function safeBrowserUrl(rawUrl) {
224
225
  }
225
226
  }
226
227
  function redactBrowserText(text) {
227
- return text.replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [REDACTED]").replace(
228
- /\b(api[-_ ]?key|access[-_ ]?token|auth[-_ ]?token|token|password|secret)\b\s*[:=]\s*[^\s,;]+/gi,
228
+ return text.replace(/\bauthorization\b\s*[:=]\s*[^\r\n]+/gi, "Authorization=[REDACTED]").replace(
229
+ /\bauthorization\b\s*[:=]\s*(?:Basic|Bearer)\s+[^\s,;]+/gi,
230
+ "Authorization=[REDACTED]"
231
+ ).replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [REDACTED]").replace(
232
+ /\b(authorization|set[-_ ]?cookie|cookie|api[-_ ]?key|private[-_ ]?key|client[-_ ]?secret|access[-_ ]?token|refresh[-_ ]?token|session[-_ ]?token|auth[-_ ]?token|token|password|secret)\b\s*[:=]\s*[^\s,;]+/gi,
229
233
  "$1=[REDACTED]"
230
234
  );
231
235
  }
@@ -1394,4 +1398,4 @@ export {
1394
1398
  browserTools,
1395
1399
  shutdownBrowserTools
1396
1400
  };
1397
- //# sourceMappingURL=chunk-45JPRMC2.js.map
1401
+ //# sourceMappingURL=chunk-QWKSKFKZ.js.map
@@ -29,13 +29,15 @@ function sweepOldSpoolFiles(dir) {
29
29
  })();
30
30
  }
31
31
  function spoolNote(info) {
32
+ if (!info) return "";
32
33
  const dropped = info.droppedBytes > 0 ? `, ~${info.droppedBytes} bytes dropped under backpressure` : "";
33
34
  return `
34
35
  [output truncated \u2014 full ${info.bytes} bytes at ${info.path}${dropped}; read/grep that file selectively instead of re-running with more output]`;
35
36
  }
36
37
  function createOutputSpool(opts) {
37
- const threshold = opts.thresholdBytes ?? 32768;
38
- const safeTool = opts.tool.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
38
+ const threshold = opts?.thresholdBytes ?? 32768;
39
+ const toolName = typeof opts?.tool === "string" ? opts.tool : "tool";
40
+ const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
39
41
  let head = "";
40
42
  let headBytes = 0;
41
43
  let totalBytes = 0;
@@ -68,7 +70,7 @@ function createOutputSpool(opts) {
68
70
  };
69
71
  return {
70
72
  write(text) {
71
- if (finalized || !text) return;
73
+ if (finalized || typeof text !== "string" || !text) return;
72
74
  const textBytes = Buffer.byteLength(text, "utf8");
73
75
  totalBytes += textBytes;
74
76
  if (!stream && !failed) {
@@ -110,4 +112,4 @@ export {
110
112
  spoolNote,
111
113
  createOutputSpool
112
114
  };
113
- //# sourceMappingURL=chunk-2VSZC6QM.js.map
115
+ //# sourceMappingURL=chunk-SCMRM6NL.js.map
@@ -39,7 +39,8 @@ var toolSearchTool = {
39
39
  }
40
40
  },
41
41
  async execute(input, ctx) {
42
- const limit = Math.min(input.limit ?? 20, 100);
42
+ const rawLimit = typeof input.limit === "number" && Number.isFinite(input.limit) ? input.limit : 20;
43
+ const limit = Math.max(1, Math.min(Math.floor(rawLimit), 100));
43
44
  const tools = ctx.catalogTools ?? ctx.tools;
44
45
  const query = input.query?.toLowerCase() ?? "";
45
46
  const filtered = tools.filter((t) => {
@@ -81,4 +82,4 @@ var toolSearchTool = {
81
82
  export {
82
83
  toolSearchTool
83
84
  };
84
- //# sourceMappingURL=chunk-RJZQWEMS.js.map
85
+ //# sourceMappingURL=chunk-T77XOX7E.js.map
@@ -1,17 +1,18 @@
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
  COMMAND_OUTPUT_MAX_BYTES,
9
9
  detectPackageManager,
10
10
  normalizeCommandOutput,
11
11
  safeResolveReal
12
- } from "./chunk-QLF74MSV.js";
12
+ } from "./chunk-ZUFHAHTE.js";
13
13
 
14
14
  // src/audit.ts
15
+ import { ToolValidationError } from "@wrongstack/core/types";
15
16
  var SEVERITY_RANK = {
16
17
  info: 0,
17
18
  low: 1,
@@ -56,12 +57,21 @@ var auditTool = {
56
57
  },
57
58
  async *executeStream(input, ctx, opts) {
58
59
  if (input.fix === true) {
59
- throw new Error(
60
- "audit: `fix: true` is not supported \u2014 this tool is read-only (mutating: false). To remediate vulnerabilities, upgrade the affected packages with the `install` tool (or `language_package` for non-JS ecosystems)."
61
- );
60
+ throw new ToolValidationError({
61
+ message: "audit: `fix: true` is not supported \u2014 this tool is read-only (mutating: false). To remediate vulnerabilities, upgrade the affected packages with the `install` tool (or `language_package` for non-JS ecosystems).",
62
+ field: "fix"
63
+ });
64
+ }
65
+ const VALID_LEVELS = /* @__PURE__ */ new Set(["low", "moderate", "high", "critical"]);
66
+ if (input.level !== void 0 && !VALID_LEVELS.has(input.level)) {
67
+ throw new ToolValidationError({
68
+ message: `audit: invalid severity level "${input.level}". Allowed: low, moderate, high, critical`,
69
+ field: "level"
70
+ });
62
71
  }
63
72
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
64
73
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
74
+ signal.throwIfAborted();
65
75
  const bridge = await tryLegacyPackageOperation("package-audit", {
66
76
  cwd,
67
77
  projectRoot: ctx.projectRoot,
@@ -173,13 +183,24 @@ function extractAdvisories(data) {
173
183
  for (const [pkg, value] of Object.entries(vulns)) {
174
184
  const vuln = value ?? {};
175
185
  const via = Array.isArray(vuln["via"]) ? vuln["via"] : [];
176
- const detail = via.find((v) => !!v && typeof v === "object");
177
- advisories.push({
178
- severity: typeof vuln["severity"] === "string" ? vuln["severity"] : "unknown",
179
- package: pkg,
180
- title: detail && typeof detail["title"] === "string" ? detail["title"] : "Unknown vulnerability",
181
- url: detail && typeof detail["url"] === "string" ? detail["url"] : ""
182
- });
186
+ const details = via.filter((v) => !!v && typeof v === "object");
187
+ if (details.length > 0) {
188
+ for (const detail of details) {
189
+ advisories.push({
190
+ severity: typeof detail["severity"] === "string" ? detail["severity"] : typeof vuln["severity"] === "string" ? vuln["severity"] : "unknown",
191
+ package: pkg,
192
+ title: typeof detail["title"] === "string" ? detail["title"] : "Unknown vulnerability",
193
+ url: typeof detail["url"] === "string" ? detail["url"] : ""
194
+ });
195
+ }
196
+ } else {
197
+ advisories.push({
198
+ severity: typeof vuln["severity"] === "string" ? vuln["severity"] : "unknown",
199
+ package: pkg,
200
+ title: "Unknown vulnerability",
201
+ url: ""
202
+ });
203
+ }
183
204
  }
184
205
  }
185
206
  return advisories;
@@ -188,4 +209,4 @@ function extractAdvisories(data) {
188
209
  export {
189
210
  auditTool
190
211
  };
191
- //# sourceMappingURL=chunk-QZDOQRF7.js.map
212
+ //# sourceMappingURL=chunk-UZFY3CKD.js.map