@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
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ensureInsideRoot,
3
3
  safeResolveReal
4
- } from "./chunk-QLF74MSV.js";
4
+ } from "./chunk-ZUFHAHTE.js";
5
5
 
6
6
  // src/document.ts
7
7
  import * as fs from "node:fs/promises";
@@ -40,7 +40,9 @@ var documentTool = {
40
40
  cwd: { type: "string", description: "Working directory (default: cwd)" }
41
41
  }
42
42
  },
43
- async execute(input, ctx) {
43
+ async execute(input, ctx, _opts) {
44
+ const signal = _opts?.signal ?? ctx?.signal;
45
+ signal?.throwIfAborted();
44
46
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
45
47
  const style = input.style ?? "jsdoc";
46
48
  const results = [];
@@ -52,6 +54,7 @@ var documentTool = {
52
54
  ctx
53
55
  ) : input.path ? [await safeResolveReal(input.path, ctx)] : [];
54
56
  for (const absPath of fileList) {
57
+ if (signal?.aborted) break;
55
58
  try {
56
59
  const content = await fs.readFile(absPath, "utf8");
57
60
  filesProcessed++;
@@ -67,7 +70,7 @@ var documentTool = {
67
70
  } catch (e) {
68
71
  results.push({
69
72
  path: absPath,
70
- name: absPath.split("/").pop() ?? absPath,
73
+ name: path.basename(absPath),
71
74
  signature: "",
72
75
  docstring: "",
73
76
  status: "error",
@@ -83,6 +86,13 @@ var documentTool = {
83
86
  };
84
87
  }
85
88
  };
89
+ function getLineNumber(content, index) {
90
+ let line = 1;
91
+ for (let i = 0; i < index; i++) {
92
+ if (content.charCodeAt(i) === 10) line++;
93
+ }
94
+ return line;
95
+ }
86
96
  async function resolveFiles(filesInput, cwd, ctx) {
87
97
  const files = filesInput.split(",");
88
98
  const resolved = [];
@@ -117,7 +127,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
117
127
  name: m[1],
118
128
  sig: m[2] ?? "",
119
129
  type: "function",
120
- line: content.slice(0, m.index).split("\n").length
130
+ line: getLineNumber(content, m.index)
121
131
  });
122
132
  }
123
133
  for (const m of content.matchAll(arrowRegex)) {
@@ -126,7 +136,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
126
136
  name: m[1],
127
137
  sig: m[2] ?? "",
128
138
  type: "arrow",
129
- line: content.slice(0, m.index).split("\n").length
139
+ line: getLineNumber(content, m.index)
130
140
  });
131
141
  }
132
142
  }
@@ -137,7 +147,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
137
147
  name: m[1],
138
148
  sig: "",
139
149
  type: "class",
140
- line: content.slice(0, m.index).split("\n").length
150
+ line: getLineNumber(content, m.index)
141
151
  });
142
152
  }
143
153
  }
@@ -148,7 +158,7 @@ function processFile(content, absPath, _style, _overwrite, target) {
148
158
  name: m[1],
149
159
  sig: m[0] ?? "",
150
160
  type: "type",
151
- line: content.slice(0, m.index).split("\n").length
161
+ line: getLineNumber(content, m.index)
152
162
  });
153
163
  }
154
164
  }
@@ -167,4 +177,4 @@ function processFile(content, absPath, _style, _overwrite, target) {
167
177
  export {
168
178
  documentTool
169
179
  };
170
- //# sourceMappingURL=chunk-HBM4QNXU.js.map
180
+ //# sourceMappingURL=chunk-IU4DV2PA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFilesContent
3
- } from "./chunk-TX3R5NVV.js";
3
+ } from "./chunk-NU2IZ72J.js";
4
4
  import {
5
5
  INDEXABLE_EXTENSIONS,
6
6
  LANG_FAMILY_ENTRIES,
@@ -514,7 +514,9 @@ function encodeVector(vec) {
514
514
  function decodeVector(buf) {
515
515
  if (buf.byteLength === 0) return new Float32Array(0);
516
516
  if (buf.byteLength % 4 !== 0) {
517
- throw new Error(`decodeVector: invalid vector byteLength ${buf.byteLength} (must be a multiple of 4)`);
517
+ throw new Error(
518
+ `decodeVector: invalid vector byteLength ${buf.byteLength} (must be a multiple of 4)`
519
+ );
518
520
  }
519
521
  const copy = new Float32Array(buf.byteLength / 4);
520
522
  new Uint8Array(copy.buffer).set(buf);
@@ -636,10 +638,7 @@ function bulkInsertSymbolsWithStatement(stmt, maxSqlVars, rows) {
636
638
  for (const take of ladder) {
637
639
  const chunk = rows.slice(cursor, cursor + take);
638
640
  cursor += take;
639
- const placeholders2 = getRowPlaceholders(
640
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
641
- chunk.length
642
- );
641
+ const placeholders2 = getRowPlaceholders("(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", chunk.length);
643
642
  const insert = stmt(
644
643
  `INSERT INTO symbols(id, lang, kind, name, file, line, col, signature, doc_comment, scope)
645
644
  VALUES ${placeholders2}`
@@ -697,10 +696,7 @@ function bulkInsertRefsWithStatement(stmt, maxSqlVars, refs) {
697
696
  for (const take of ladder) {
698
697
  const chunk = refs.slice(cursor, cursor + take);
699
698
  cursor += take;
700
- const placeholders2 = getRowPlaceholders(
701
- "(?, ?, ?, ?, ?, ?, ?, ?)",
702
- chunk.length
703
- );
699
+ const placeholders2 = getRowPlaceholders("(?, ?, ?, ?, ?, ?, ?, ?)", chunk.length);
704
700
  const insert = stmt(
705
701
  `INSERT INTO refs(from_id, to_name, to_id, call_type, line, lang, module, to_file)
706
702
  VALUES ${placeholders2}`
@@ -1769,9 +1765,7 @@ function commitBatchWithStatement(stmtFn, maxSqlVars, ftsAvailable, vectorsAvail
1769
1765
  if (vectorsAvailable) {
1770
1766
  vectorRows.push({
1771
1767
  id,
1772
- vector: encodeVector(
1773
- embedText(buildIndexableText(s.name, s.signature, s.docComment))
1774
- )
1768
+ vector: encodeVector(embedText(buildIndexableText(s.name, s.signature, s.docComment)))
1775
1769
  });
1776
1770
  }
1777
1771
  const inserted = { ...s, id };
@@ -3825,7 +3819,7 @@ var ParserWorkerPool = class {
3825
3819
  */
3826
3820
  async reparseInline(batch, orphaned, workerId) {
3827
3821
  try {
3828
- const { parseFileContent } = await import("./parser-dispatch-3PDBKIQ7.js");
3822
+ const { parseFileContent } = await import("./parser-dispatch-BDVCJXTV.js");
3829
3823
  for (const item of orphaned) {
3830
3824
  if (batch.settled) return;
3831
3825
  try {
@@ -4614,4 +4608,4 @@ export {
4614
4608
  incomingCallsService,
4615
4609
  outgoingCallsService
4616
4610
  };
4617
- //# sourceMappingURL=chunk-TFMVHYJI.js.map
4611
+ //# sourceMappingURL=chunk-J43O3BOX.js.map
@@ -1,15 +1,16 @@
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
  normalizeCommandOutput,
9
9
  safeResolveReal
10
- } from "./chunk-QLF74MSV.js";
10
+ } from "./chunk-ZUFHAHTE.js";
11
11
 
12
12
  // src/format.ts
13
+ import { ToolValidationError } from "@wrongstack/core/types";
13
14
  var formatTool = {
14
15
  name: "format",
15
16
  category: "Code Quality",
@@ -55,6 +56,14 @@ var formatTool = {
55
56
  async *executeStream(input, ctx, opts) {
56
57
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
57
58
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
59
+ signal.throwIfAborted();
60
+ const VALID_FIXERS = /* @__PURE__ */ new Set(["biome", "prettier", "auto"]);
61
+ if (input.fixer !== void 0 && !VALID_FIXERS.has(input.fixer)) {
62
+ throw new ToolValidationError({
63
+ message: `format: unsupported fixer "${input.fixer}". Allowed fixers: biome, prettier, auto`,
64
+ field: "fixer"
65
+ });
66
+ }
58
67
  const fixer = input.fixer ?? "auto";
59
68
  if (fixer === "auto" && !input.files) {
60
69
  const op = input.check ? "format-check" : "format-write";
@@ -98,7 +107,7 @@ var formatTool = {
98
107
  text: `Running ${detected}\u2026`,
99
108
  data: { fixer: detected, check: !!input.check }
100
109
  };
101
- const fileList = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim()) : [];
110
+ const fileList = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim().replace(/\\/g, "/")).filter(Boolean) : [];
102
111
  let args;
103
112
  if (detected === "prettier") {
104
113
  args = [input.check ? "--check" : "--write"];
@@ -116,33 +125,45 @@ var formatTool = {
116
125
  });
117
126
  const combinedOut = `${result.stdout}
118
127
  ${result.stderr}`;
119
- const counts = parseFormatterCounts(detected, combinedOut);
128
+ const counts = parseFormatterCounts(detected, combinedOut, !!input.check);
129
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
130
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
120
131
  yield {
121
132
  type: "final",
122
133
  output: {
123
134
  fixer: detected,
124
135
  files_checked: counts.checked,
125
136
  files_changed: counts.changed,
126
- output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
137
+ output: normalizeCommandOutput(rawOutput),
127
138
  truncated: result.truncated
128
139
  }
129
140
  };
130
141
  }
131
142
  };
132
- function parseFormatterCounts(fixer, output) {
143
+ function parseFormatterCounts(fixer, output, isCheck = false) {
133
144
  if (fixer !== "biome") return { checked: void 0, changed: void 0 };
134
145
  const checkedMatch = /\b(?:Checked|Formatted)\s+(\d+)\s+files?\b/i.exec(output);
135
- const changedMatch = /\bFixed\s+(\d+)\s+files?\b/i.exec(output);
146
+ const fixedMatch = /\bFixed\s+(\d+)\s+files?\b/i.exec(output);
147
+ const formattedCountMatch = /\bFormatted\s+(\d+)\s+files?\b/i.exec(output);
148
+ let changed;
149
+ if (isCheck) {
150
+ changed = 0;
151
+ } else if (fixedMatch?.[1] !== void 0) {
152
+ changed = Number(fixedMatch[1]);
153
+ } else if (formattedCountMatch?.[1] !== void 0) {
154
+ changed = Number(formattedCountMatch[1]);
155
+ }
136
156
  return {
137
157
  checked: checkedMatch?.[1] !== void 0 ? Number(checkedMatch[1]) : void 0,
138
- changed: changedMatch?.[1] !== void 0 ? Number(changedMatch[1]) : void 0
158
+ changed
139
159
  };
140
160
  }
141
161
  async function detectFixer(cwd) {
142
162
  const fs = await import("node:fs/promises");
163
+ const { join } = await import("node:path");
143
164
  const exists = async (file) => {
144
165
  try {
145
- await fs.stat(`${cwd}/${file}`);
166
+ await fs.stat(join(cwd, file));
146
167
  return true;
147
168
  } catch {
148
169
  return false;
@@ -152,20 +173,23 @@ async function detectFixer(cwd) {
152
173
  const PRETTIER_CONFIGS = [
153
174
  ".prettierrc",
154
175
  ".prettierrc.json",
176
+ ".prettierrc.json5",
155
177
  ".prettierrc.yml",
156
178
  ".prettierrc.yaml",
179
+ ".prettierrc.toml",
157
180
  ".prettierrc.js",
158
181
  ".prettierrc.cjs",
159
182
  ".prettierrc.mjs",
160
183
  "prettier.config.js",
161
184
  "prettier.config.cjs",
162
- "prettier.config.mjs"
185
+ "prettier.config.mjs",
186
+ "prettier.config.ts"
163
187
  ];
164
188
  for (const cfg of PRETTIER_CONFIGS) {
165
189
  if (await exists(cfg)) return "prettier";
166
190
  }
167
191
  try {
168
- const raw = await fs.readFile(`${cwd}/package.json`, "utf8");
192
+ const raw = await fs.readFile(join(cwd, "package.json"), "utf8");
169
193
  const pkg = JSON.parse(raw);
170
194
  if (pkg["prettier"] !== void 0) return "prettier";
171
195
  } catch {
@@ -176,4 +200,4 @@ async function detectFixer(cwd) {
176
200
  export {
177
201
  formatTool
178
202
  };
179
- //# sourceMappingURL=chunk-ODFVG54U.js.map
203
+ //# sourceMappingURL=chunk-J74TMK4M.js.map
@@ -1,17 +1,18 @@
1
1
  import {
2
2
  enqueueReindex
3
- } from "./chunk-MRKWZCHZ.js";
3
+ } from "./chunk-Z5Q3TXRY.js";
4
4
  import {
5
5
  resolveRealInsideRoot,
6
6
  safeResolveReal,
7
7
  sha256hex
8
- } from "./chunk-QLF74MSV.js";
8
+ } from "./chunk-ZUFHAHTE.js";
9
9
 
10
10
  // src/patch.ts
11
11
  import { spawn } from "node:child_process";
12
12
  import * as fs from "node:fs/promises";
13
13
  import * as os from "node:os";
14
14
  import * as path from "node:path";
15
+ import { ToolValidationError } from "@wrongstack/core/types";
15
16
  import { buildChildEnv, toErrorMessage } from "@wrongstack/core/utils";
16
17
  var patchTool = {
17
18
  name: "patch",
@@ -41,10 +42,31 @@ var patchTool = {
41
42
  },
42
43
  required: ["patch"]
43
44
  },
45
+ // VULN-001 Phase 2: the permission layer's input-key heuristic cannot see
46
+ // destinations encoded in the diff body, so the confirm prompt keyed on
47
+ // `directory` (".") while the patch wrote elsewhere. Declares the stripped
48
+ // relative destinations; execute()'s realpath containment above remains
49
+ // the authority on whether they are allowed.
50
+ writeTargets(input) {
51
+ if (!input?.patch || typeof input.patch !== "string" || !input.patch.trim()) return [];
52
+ const strip = typeof input.strip === "number" && Number.isFinite(input.strip) && input.strip >= 0 ? Math.floor(input.strip) : 1;
53
+ const out = [];
54
+ for (const target of extractDiffTargets(input.patch)) {
55
+ const stripped = stripPathComponents(target.raw, strip);
56
+ if (stripped) out.push(stripped);
57
+ }
58
+ return out;
59
+ },
44
60
  async execute(input, ctx, opts) {
45
- if (!input?.patch) throw new Error("patch: patch content is required");
61
+ if (!input?.patch || typeof input.patch !== "string" || !input.patch.trim()) {
62
+ throw new ToolValidationError({
63
+ message: "patch: patch content is required and cannot be empty",
64
+ field: "patch"
65
+ });
66
+ }
46
67
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
47
- const strip = Math.max(1, input.strip ?? 1);
68
+ signal.throwIfAborted();
69
+ const strip = typeof input.strip === "number" && Number.isFinite(input.strip) && input.strip >= 0 ? Math.floor(input.strip) : 1;
48
70
  const dryRun = input.dry_run ?? false;
49
71
  const refuse = (message) => ({
50
72
  applied: 0,
@@ -61,6 +83,11 @@ var patchTool = {
61
83
  }
62
84
  const realRoot = await fs.realpath(ctx.projectRoot).catch(() => path.resolve(ctx.projectRoot));
63
85
  const targets = extractDiffTargets(input.patch);
86
+ if (targets.length === 0) {
87
+ return refuse(
88
+ "patch refused: no file target could be parsed from the patch (fail-closed containment \u2014 refusing an unparseable diff)"
89
+ );
90
+ }
64
91
  const resolvedTargets = [];
65
92
  for (const t of targets) {
66
93
  const stripped = stripPathComponents(t.raw, strip);
@@ -196,7 +223,11 @@ function extractDiffTargets(patch) {
196
223
  const out = [];
197
224
  const clean = (raw) => {
198
225
  if (!raw) return "";
199
- return (raw.length > 4096 ? raw.slice(0, 4096) : raw).trim();
226
+ let s = (raw.length > 4096 ? raw.slice(0, 4096) : raw).trim();
227
+ if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
228
+ s = s.slice(1, -1).trim();
229
+ }
230
+ return s;
200
231
  };
201
232
  let lastOld;
202
233
  let inHunk = false;
@@ -222,6 +253,31 @@ function extractDiffTargets(patch) {
222
253
  if (oldLinesLeft <= 0 && newLinesLeft <= 0) inHunk = false;
223
254
  continue;
224
255
  }
256
+ if (line.startsWith("diff --git ")) {
257
+ const rest = line.slice("diff --git ".length);
258
+ if (rest.startsWith('"')) {
259
+ const quoted = /^"((?:[^"\\]|\\.)*)"\s+"((?:[^"\\]|\\.)*)"$/.exec(rest);
260
+ if (quoted) {
261
+ for (const half of [quoted[1], quoted[2]]) {
262
+ const p = clean(half);
263
+ if (p) out.push({ raw: p, deleted: false });
264
+ }
265
+ }
266
+ } else {
267
+ const bIdx = rest.lastIndexOf(" b/");
268
+ if (rest.startsWith("a/") && bIdx !== -1) {
269
+ out.push({ raw: rest.slice(0, bIdx), deleted: false });
270
+ out.push({ raw: rest.slice(bIdx + 1), deleted: false });
271
+ }
272
+ }
273
+ continue;
274
+ }
275
+ const extended = /^(?:rename|copy) (?:from|to) (.+)$/.exec(line);
276
+ if (extended) {
277
+ const p = clean(extended[1]);
278
+ if (p && p !== "/dev/null") out.push({ raw: p, deleted: false });
279
+ continue;
280
+ }
225
281
  const oldMatch = /^---\s+([^\t\r\n]+)/.exec(line);
226
282
  if (oldMatch) {
227
283
  lastOld = clean(oldMatch[1]);
@@ -258,7 +314,6 @@ function runPatch(args, cwd, signal, fallback) {
258
314
  if (!result.unavailable) return { ...result, engine: "patch" };
259
315
  const gitArgs = [
260
316
  "apply",
261
- "--unsafe-paths",
262
317
  `-p${fallback.strip}`,
263
318
  "--verbose",
264
319
  ...fallback.dryRun ? ["--check"] : [],
@@ -269,6 +324,14 @@ function runPatch(args, cwd, signal, fallback) {
269
324
  });
270
325
  }
271
326
  function runPatchProcess(command, args, cwd, signal) {
327
+ if (signal.aborted) {
328
+ return Promise.resolve({
329
+ exitCode: 124,
330
+ stdout: "",
331
+ stderr: "Aborted",
332
+ unavailable: false
333
+ });
334
+ }
272
335
  return new Promise((resolve2) => {
273
336
  let stdout = "";
274
337
  let stderr = "";
@@ -280,6 +343,7 @@ function runPatchProcess(command, args, cwd, signal) {
280
343
  stdio: ["pipe", "pipe", "pipe"],
281
344
  windowsHide: true
282
345
  });
346
+ child.stdin?.end();
283
347
  child.stdout?.on("data", (c) => {
284
348
  stdout += c.toString();
285
349
  });
@@ -305,7 +369,10 @@ function extractPatchedFiles(output) {
305
369
  const files = [];
306
370
  const re = /(?:patching|checking) file (.+)/gi;
307
371
  for (const m of output.matchAll(re)) {
308
- if (m[1]) files.push(m[1]);
372
+ if (m[1]) {
373
+ const sanitized = m[1].replace(/^['"`]|['"`\r\n]+$/g, "").trim();
374
+ if (sanitized) files.push(sanitized);
375
+ }
309
376
  }
310
377
  return files;
311
378
  }
@@ -313,4 +380,4 @@ function extractPatchedFiles(output) {
313
380
  export {
314
381
  patchTool
315
382
  };
316
- //# sourceMappingURL=chunk-COUNXG4X.js.map
383
+ //# sourceMappingURL=chunk-JLYA4X7K.js.map
@@ -12,11 +12,12 @@ function loadTypescript() {
12
12
  return tsLoad;
13
13
  }
14
14
  async function checkSyntax(filePath, content, previousContent) {
15
+ if (typeof filePath !== "string" || typeof content !== "string") return void 0;
15
16
  const errors = await parseErrors(filePath, content);
16
17
  if (errors === void 0) return void 0;
17
18
  if (errors.length === 0) return { errors, preExisting: false };
18
19
  let preExisting = false;
19
- if (previousContent !== void 0) {
20
+ if (typeof previousContent === "string") {
20
21
  const prevErrors = await parseErrors(filePath, previousContent);
21
22
  preExisting = prevErrors !== void 0 && prevErrors.length > 0;
22
23
  }
@@ -86,4 +87,4 @@ function formatDiag(ts, diag, content, sourceFile) {
86
87
  export {
87
88
  checkSyntax
88
89
  };
89
- //# sourceMappingURL=chunk-FQZOHLG3.js.map
90
+ //# sourceMappingURL=chunk-JSNSMG5R.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtinTools
3
- } from "./chunk-VALQFAY2.js";
3
+ } from "./chunk-4BABEJC2.js";
4
4
 
5
5
  // src/pack.ts
6
6
  var builtinToolsPack = {
@@ -12,4 +12,4 @@ var builtinToolsPack = {
12
12
  export {
13
13
  builtinToolsPack
14
14
  };
15
- //# sourceMappingURL=chunk-SYXW3Y5Z.js.map
15
+ //# sourceMappingURL=chunk-JVRLTEO7.js.map
@@ -56,7 +56,8 @@ function buildWin32CmdShimInvocation(command, args = []) {
56
56
  };
57
57
  }
58
58
  function quoteWin32CmdArg(arg) {
59
- return `"${arg}"`;
59
+ const escaped = arg.replace(/(\\+)$/, "$1$1");
60
+ return `"${escaped}"`;
60
61
  }
61
62
 
62
63
  export {
@@ -65,4 +66,4 @@ export {
65
66
  assertSafeWin32ShellArgs,
66
67
  buildWin32CmdShimInvocation
67
68
  };
68
- //# sourceMappingURL=chunk-V43EQ6VR.js.map
69
+ //# sourceMappingURL=chunk-KE7A76VC.js.map
@@ -1,15 +1,16 @@
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
  normalizeCommandOutput,
9
9
  safeResolveReal
10
- } from "./chunk-QLF74MSV.js";
10
+ } from "./chunk-ZUFHAHTE.js";
11
11
 
12
12
  // src/lint.ts
13
+ import { ToolValidationError } from "@wrongstack/core/types";
13
14
  var lintTool = {
14
15
  name: "lint",
15
16
  category: "Code Quality",
@@ -49,6 +50,14 @@ var lintTool = {
49
50
  async *executeStream(input, ctx, opts) {
50
51
  const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
51
52
  const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
53
+ signal.throwIfAborted();
54
+ const VALID_LINTERS = /* @__PURE__ */ new Set(["biome", "eslint", "tslint", "auto"]);
55
+ if (input.linter !== void 0 && !VALID_LINTERS.has(input.linter)) {
56
+ throw new ToolValidationError({
57
+ message: `lint: unsupported linter "${input.linter}". Allowed linters: biome, eslint, tslint, auto`,
58
+ field: "linter"
59
+ });
60
+ }
52
61
  const linter = input.linter ?? "auto";
53
62
  if (linter === "auto" && !input.files) {
54
63
  const bridge = await tryLegacyCodeOperation("lint", {
@@ -90,24 +99,49 @@ var lintTool = {
90
99
  return;
91
100
  }
92
101
  yield { type: "log", text: `Running ${detected}\u2026`, data: { linter: detected } };
93
- const args = ["lint"];
94
- if (input.fix) args.push("--write");
95
- if (input.files) {
96
- const files = Array.isArray(input.files) ? input.files : input.files.split(",");
97
- args.push("--", ...files.map((f) => f.trim()));
102
+ const files = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim().replace(/\\/g, "/")).filter(Boolean) : [];
103
+ const args = [];
104
+ if (detected === "eslint") {
105
+ if (input.fix) args.push("--fix");
106
+ if (files.length) args.push(...files);
107
+ } else {
108
+ args.push("lint");
109
+ if (input.fix) args.push("--write");
110
+ if (files.length) args.push("--", ...files);
98
111
  }
99
112
  const cmd = detected === "biome" ? "biome" : detected;
100
113
  const result = yield* spawnStream({ cmd, args, cwd, signal, maxBytes: 1e5 });
101
- const errors = [...result.stdout.matchAll(/\berror\b/gi)].length;
102
- const warnings = [...result.stdout.matchAll(/\bwarning\b/gi)].length;
114
+ const combined = `${result.stdout}
115
+ ${result.stderr}`;
116
+ let errors = 0;
117
+ let warnings = 0;
118
+ const biomeSummary = combined.match(/Found\s+(\d+)\s+errors?(?:\s+and\s+(\d+)\s+warnings?)?/i);
119
+ const eslintSummary = combined.match(
120
+ /(\d+)\s+problems?\s+\((\d+)\s+errors?,\s*(\d+)\s+warnings?\)/i
121
+ );
122
+ if (biomeSummary) {
123
+ errors = Number.parseInt(biomeSummary[1] ?? "0", 10);
124
+ warnings = Number.parseInt(biomeSummary[2] ?? "0", 10);
125
+ } else if (eslintSummary) {
126
+ errors = Number.parseInt(eslintSummary[2] ?? "0", 10);
127
+ warnings = Number.parseInt(eslintSummary[3] ?? "0", 10);
128
+ } else {
129
+ errors = [...combined.matchAll(/\berror\b/gi)].length;
130
+ warnings = [...combined.matchAll(/\bwarning\b/gi)].length;
131
+ }
132
+ if (errors === 0 && result.exitCode !== 0) {
133
+ errors = 1;
134
+ }
135
+ const rawOutput = result.stdout && result.stderr ? `${result.stdout}
136
+ ${result.stderr}` : result.stdout || result.stderr || result.error || "";
103
137
  yield {
104
138
  type: "final",
105
139
  output: {
106
140
  linter: detected,
107
- files_checked: input.files ? Array.isArray(input.files) ? input.files.length : input.files.split(",").length : 0,
141
+ files_checked: files.length,
108
142
  errors,
109
143
  warnings,
110
- output: normalizeCommandOutput(result.stdout),
144
+ output: normalizeCommandOutput(rawOutput),
111
145
  fix_applied: input.fix ?? false,
112
146
  truncated: result.truncated
113
147
  }
@@ -116,10 +150,25 @@ var lintTool = {
116
150
  };
117
151
  async function detectLinter(cwd) {
118
152
  const { stat } = await import("node:fs/promises");
119
- const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
153
+ const checks = [
154
+ "biome.json",
155
+ "biome.jsonc",
156
+ "eslint.config.js",
157
+ "eslint.config.mjs",
158
+ "eslint.config.cjs",
159
+ "eslint.config.ts",
160
+ ".eslintrc.json",
161
+ ".eslintrc.js",
162
+ ".eslintrc.cjs",
163
+ ".eslintrc.yaml",
164
+ ".eslintrc.yml",
165
+ "tslint.json",
166
+ "tsconfig.json"
167
+ ];
120
168
  for (const f of checks) {
121
169
  try {
122
- await stat(`${cwd}/${f}`);
170
+ const { join } = await import("node:path");
171
+ await stat(join(cwd, f));
123
172
  if (f.includes("biome")) return "biome";
124
173
  if (f.includes("eslint")) return "eslint";
125
174
  if (f.includes("tslint")) return "tslint";
@@ -132,4 +181,4 @@ async function detectLinter(cwd) {
132
181
  export {
133
182
  lintTool
134
183
  };
135
- //# sourceMappingURL=chunk-ZKSBHXVB.js.map
184
+ //# sourceMappingURL=chunk-KWXIKGE3.js.map