@wrongstack/tools 0.284.1 → 0.285.0

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 (311) hide show
  1. package/dist/_concurrency.d.ts +16 -0
  2. package/dist/_concurrency.d.ts.map +1 -0
  3. package/dist/_danger-detect.d.ts +54 -0
  4. package/dist/_danger-detect.d.ts.map +1 -0
  5. package/dist/_edit-match.d.ts +86 -0
  6. package/dist/_edit-match.d.ts.map +1 -0
  7. package/dist/_env.d.ts +6 -0
  8. package/dist/_env.d.ts.map +1 -0
  9. package/dist/_fetch-guard.d.ts +31 -0
  10. package/dist/_fetch-guard.d.ts.map +1 -0
  11. package/dist/_output-spool.d.ts +38 -0
  12. package/dist/_output-spool.d.ts.map +1 -0
  13. package/dist/_redact-command.d.ts +6 -0
  14. package/dist/_redact-command.d.ts.map +1 -0
  15. package/dist/_regex.d.ts +39 -0
  16. package/dist/_regex.d.ts.map +1 -0
  17. package/dist/_session-shell.d.ts +73 -0
  18. package/dist/_session-shell.d.ts.map +1 -0
  19. package/dist/_shell-pick.d.ts +158 -0
  20. package/dist/_shell-pick.d.ts.map +1 -0
  21. package/dist/_spawn-stream.d.ts +32 -0
  22. package/dist/_spawn-stream.d.ts.map +1 -0
  23. package/dist/_syntax-check.d.ts +16 -0
  24. package/dist/_syntax-check.d.ts.map +1 -0
  25. package/dist/_util.d.ts +68 -0
  26. package/dist/_util.d.ts.map +1 -0
  27. package/dist/_win32-resolve.d.ts +42 -0
  28. package/dist/_win32-resolve.d.ts.map +1 -0
  29. package/dist/audit.d.ts +4 -5
  30. package/dist/audit.d.ts.map +1 -0
  31. package/dist/audit.js +2743 -28
  32. package/dist/audit.js.map +7 -1
  33. package/dist/bash-kill-guard.d.ts +62 -0
  34. package/dist/bash-kill-guard.d.ts.map +1 -0
  35. package/dist/bash.d.ts +4 -5
  36. package/dist/bash.d.ts.map +1 -0
  37. package/dist/bash.js +59 -30
  38. package/dist/bash.js.map +7 -1
  39. package/dist/batch-tool-use.d.ts +4 -5
  40. package/dist/batch-tool-use.d.ts.map +1 -0
  41. package/dist/batch-tool-use.js +3 -3
  42. package/dist/batch-tool-use.js.map +7 -1
  43. package/dist/builtin.d.ts +7 -9
  44. package/dist/builtin.d.ts.map +1 -0
  45. package/dist/builtin.js +5282 -1019
  46. package/dist/builtin.js.map +7 -1
  47. package/dist/circuit-breaker.d.ts +4 -5
  48. package/dist/circuit-breaker.d.ts.map +1 -0
  49. package/dist/circuit-breaker.js +3 -3
  50. package/dist/circuit-breaker.js.map +7 -1
  51. package/dist/codebase-index/background-indexer.d.ts +116 -0
  52. package/dist/codebase-index/background-indexer.d.ts.map +1 -0
  53. package/dist/codebase-index/bm25.d.ts +40 -0
  54. package/dist/codebase-index/bm25.d.ts.map +1 -0
  55. package/dist/codebase-index/circuit-breaker.d.ts +94 -0
  56. package/dist/codebase-index/circuit-breaker.d.ts.map +1 -0
  57. package/dist/codebase-index/codebase-index-tool.d.ts +17 -0
  58. package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -0
  59. package/dist/codebase-index/codebase-search-tool.d.ts +43 -0
  60. package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -0
  61. package/dist/codebase-index/codebase-stats-tool.d.ts +23 -0
  62. package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -0
  63. package/dist/codebase-index/gitignore.d.ts +22 -0
  64. package/dist/codebase-index/gitignore.d.ts.map +1 -0
  65. package/dist/codebase-index/go-parser.d.ts +16 -0
  66. package/dist/codebase-index/go-parser.d.ts.map +1 -0
  67. package/dist/codebase-index/index-service.d.ts +25 -0
  68. package/dist/codebase-index/index-service.d.ts.map +1 -0
  69. package/dist/codebase-index/index.d.ts +23 -271
  70. package/dist/codebase-index/index.d.ts.map +1 -0
  71. package/dist/codebase-index/index.js +136 -68
  72. package/dist/codebase-index/index.js.map +7 -1
  73. package/dist/codebase-index/indexer.d.ts +28 -0
  74. package/dist/codebase-index/indexer.d.ts.map +1 -0
  75. package/dist/codebase-index/json-parser.d.ts +8 -0
  76. package/dist/codebase-index/json-parser.d.ts.map +1 -0
  77. package/dist/codebase-index/lsp-kind.d.ts +53 -0
  78. package/dist/codebase-index/lsp-kind.d.ts.map +1 -0
  79. package/dist/codebase-index/py-parser.d.ts +16 -0
  80. package/dist/codebase-index/py-parser.d.ts.map +1 -0
  81. package/dist/codebase-index/refs-extractor.d.ts +25 -0
  82. package/dist/codebase-index/refs-extractor.d.ts.map +1 -0
  83. package/dist/codebase-index/rs-parser.d.ts +8 -0
  84. package/dist/codebase-index/rs-parser.d.ts.map +1 -0
  85. package/dist/codebase-index/schema.d.ts +82 -0
  86. package/dist/codebase-index/schema.d.ts.map +1 -0
  87. package/dist/codebase-index/ts-parser.d.ts +30 -0
  88. package/dist/codebase-index/ts-parser.d.ts.map +1 -0
  89. package/dist/codebase-index/worker-protocol.d.ts +77 -0
  90. package/dist/codebase-index/worker-protocol.d.ts.map +1 -0
  91. package/dist/codebase-index/worker.d.ts +16 -2
  92. package/dist/codebase-index/worker.d.ts.map +1 -0
  93. package/dist/codebase-index/worker.js +103 -65
  94. package/dist/codebase-index/worker.js.map +7 -1
  95. package/dist/codebase-index/writer.d.ts +189 -0
  96. package/dist/codebase-index/writer.d.ts.map +1 -0
  97. package/dist/codebase-index/yaml-parser.d.ts +8 -0
  98. package/dist/codebase-index/yaml-parser.d.ts.map +1 -0
  99. package/dist/design.d.ts +38 -0
  100. package/dist/design.d.ts.map +1 -0
  101. package/dist/design.js +270 -0
  102. package/dist/design.js.map +7 -0
  103. package/dist/diff.d.ts +4 -5
  104. package/dist/diff.d.ts.map +1 -0
  105. package/dist/diff.js +14 -12
  106. package/dist/diff.js.map +7 -1
  107. package/dist/document.d.ts +4 -5
  108. package/dist/document.d.ts.map +1 -0
  109. package/dist/document.js +8 -7
  110. package/dist/document.js.map +7 -1
  111. package/dist/edit.d.ts +5 -36
  112. package/dist/edit.d.ts.map +1 -0
  113. package/dist/edit.js +28 -16
  114. package/dist/edit.js.map +7 -1
  115. package/dist/exec.d.ts +67 -2
  116. package/dist/exec.d.ts.map +1 -0
  117. package/dist/exec.js +50 -25
  118. package/dist/exec.js.map +7 -1
  119. package/dist/fetch.d.ts +4 -32
  120. package/dist/fetch.d.ts.map +1 -0
  121. package/dist/fetch.js +89 -77
  122. package/dist/fetch.js.map +7 -1
  123. package/dist/format.d.ts +4 -5
  124. package/dist/format.d.ts.map +1 -0
  125. package/dist/format.js +2415 -28
  126. package/dist/format.js.map +7 -1
  127. package/dist/git.d.ts +4 -5
  128. package/dist/git.d.ts.map +1 -0
  129. package/dist/git.js +10 -9
  130. package/dist/git.js.map +7 -1
  131. package/dist/glob.d.ts +4 -5
  132. package/dist/glob.d.ts.map +1 -0
  133. package/dist/glob.js +15 -12
  134. package/dist/glob.js.map +7 -1
  135. package/dist/grep.d.ts +4 -5
  136. package/dist/grep.d.ts.map +1 -0
  137. package/dist/grep.js +13 -10
  138. package/dist/grep.js.map +7 -1
  139. package/dist/index.d.ts +19 -724
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +6088 -1690
  142. package/dist/index.js.map +7 -1
  143. package/dist/install.d.ts +4 -5
  144. package/dist/install.d.ts.map +1 -0
  145. package/dist/install.js +2680 -31
  146. package/dist/install.js.map +7 -1
  147. package/dist/json.d.ts +4 -5
  148. package/dist/json.d.ts.map +1 -0
  149. package/dist/json.js +13 -11
  150. package/dist/json.js.map +7 -1
  151. package/dist/kanban.d.ts +107 -0
  152. package/dist/kanban.d.ts.map +1 -0
  153. package/dist/kanban.js +845 -0
  154. package/dist/kanban.js.map +7 -0
  155. package/dist/languages/detect.d.ts +3 -0
  156. package/dist/languages/detect.d.ts.map +1 -0
  157. package/dist/languages/diagnostics.d.ts +15 -0
  158. package/dist/languages/diagnostics.d.ts.map +1 -0
  159. package/dist/languages/execute-tool.d.ts +34 -0
  160. package/dist/languages/execute-tool.d.ts.map +1 -0
  161. package/dist/languages/execute.d.ts +18 -0
  162. package/dist/languages/execute.d.ts.map +1 -0
  163. package/dist/languages/index.d.ts +12 -0
  164. package/dist/languages/index.d.ts.map +1 -0
  165. package/dist/languages/index.js +4457 -0
  166. package/dist/languages/index.js.map +7 -0
  167. package/dist/languages/legacy-bridge.d.ts +29 -0
  168. package/dist/languages/legacy-bridge.d.ts.map +1 -0
  169. package/dist/languages/package-tool.d.ts +29 -0
  170. package/dist/languages/package-tool.d.ts.map +1 -0
  171. package/dist/languages/plan.d.ts +4 -0
  172. package/dist/languages/plan.d.ts.map +1 -0
  173. package/dist/languages/profile-helpers.d.ts +14 -0
  174. package/dist/languages/profile-helpers.d.ts.map +1 -0
  175. package/dist/languages/profiles/additional.d.ts +3 -0
  176. package/dist/languages/profiles/additional.d.ts.map +1 -0
  177. package/dist/languages/profiles/primary.d.ts +3 -0
  178. package/dist/languages/profiles/primary.d.ts.map +1 -0
  179. package/dist/languages/registry.d.ts +11 -0
  180. package/dist/languages/registry.d.ts.map +1 -0
  181. package/dist/languages/tool.d.ts +32 -0
  182. package/dist/languages/tool.d.ts.map +1 -0
  183. package/dist/languages/types.d.ts +207 -0
  184. package/dist/languages/types.d.ts.map +1 -0
  185. package/dist/lint.d.ts +4 -5
  186. package/dist/lint.d.ts.map +1 -0
  187. package/dist/lint.js +2415 -27
  188. package/dist/lint.js.map +7 -1
  189. package/dist/logs.d.ts +4 -5
  190. package/dist/logs.d.ts.map +1 -0
  191. package/dist/logs.js +16 -10
  192. package/dist/logs.js.map +7 -1
  193. package/dist/memory.d.ts +7 -8
  194. package/dist/memory.d.ts.map +1 -0
  195. package/dist/memory.js +7 -5
  196. package/dist/memory.js.map +7 -1
  197. package/dist/mode.d.ts +4 -5
  198. package/dist/mode.d.ts.map +1 -0
  199. package/dist/mode.js +3 -3
  200. package/dist/mode.js.map +7 -1
  201. package/dist/next-steps.d.ts +5 -6
  202. package/dist/next-steps.d.ts.map +1 -0
  203. package/dist/next-steps.js +5 -4
  204. package/dist/next-steps.js.map +7 -1
  205. package/dist/outdated.d.ts +4 -5
  206. package/dist/outdated.d.ts.map +1 -0
  207. package/dist/outdated.js +4777 -68
  208. package/dist/outdated.js.map +7 -1
  209. package/dist/pack.d.ts +3 -6
  210. package/dist/pack.d.ts.map +1 -0
  211. package/dist/pack.js +5275 -1019
  212. package/dist/pack.js.map +7 -1
  213. package/dist/patch.d.ts +4 -5
  214. package/dist/patch.d.ts.map +1 -0
  215. package/dist/patch.js +18 -16
  216. package/dist/patch.js.map +7 -1
  217. package/dist/plan.d.ts +55 -0
  218. package/dist/plan.d.ts.map +1 -0
  219. package/dist/plan.js +280 -0
  220. package/dist/plan.js.map +7 -0
  221. package/dist/process-guardian.d.ts +100 -0
  222. package/dist/process-guardian.d.ts.map +1 -0
  223. package/dist/process-registry-persistent.d.ts +121 -0
  224. package/dist/process-registry-persistent.d.ts.map +1 -0
  225. package/dist/process-registry.d.ts +13 -17
  226. package/dist/process-registry.d.ts.map +1 -0
  227. package/dist/process-registry.js +15 -9
  228. package/dist/process-registry.js.map +7 -1
  229. package/dist/ps-slash.d.ts +106 -0
  230. package/dist/ps-slash.d.ts.map +1 -0
  231. package/dist/ps-slash.js +1291 -0
  232. package/dist/ps-slash.js.map +7 -0
  233. package/dist/read.d.ts +4 -5
  234. package/dist/read.d.ts.map +1 -0
  235. package/dist/read.js +13 -11
  236. package/dist/read.js.map +7 -1
  237. package/dist/replace.d.ts +4 -5
  238. package/dist/replace.d.ts.map +1 -0
  239. package/dist/replace.js +26 -13
  240. package/dist/replace.js.map +7 -1
  241. package/dist/scaffold.d.ts +4 -5
  242. package/dist/scaffold.d.ts.map +1 -0
  243. package/dist/scaffold.js +16 -14
  244. package/dist/scaffold.js.map +7 -1
  245. package/dist/search.d.ts +5 -6
  246. package/dist/search.d.ts.map +1 -0
  247. package/dist/search.js +24 -25
  248. package/dist/search.js.map +7 -1
  249. package/dist/set-working-dir.d.ts +18 -0
  250. package/dist/set-working-dir.d.ts.map +1 -0
  251. package/dist/shim/node-sqlite.d.ts +3 -0
  252. package/dist/shim/node-sqlite.d.ts.map +1 -0
  253. package/dist/skill.d.ts +45 -0
  254. package/dist/skill.d.ts.map +1 -0
  255. package/dist/skill.js +175 -0
  256. package/dist/skill.js.map +7 -0
  257. package/dist/spawn-background.d.ts +80 -0
  258. package/dist/spawn-background.d.ts.map +1 -0
  259. package/dist/task.d.ts +35 -0
  260. package/dist/task.d.ts.map +1 -0
  261. package/dist/task.js +380 -0
  262. package/dist/task.js.map +7 -0
  263. package/dist/test.d.ts +4 -5
  264. package/dist/test.d.ts.map +1 -0
  265. package/dist/test.js +2419 -27
  266. package/dist/test.js.map +7 -1
  267. package/dist/todo.d.ts +4 -5
  268. package/dist/todo.d.ts.map +1 -0
  269. package/dist/todo.js +5 -5
  270. package/dist/todo.js.map +7 -1
  271. package/dist/tool-diff.d.ts +16 -17
  272. package/dist/tool-diff.d.ts.map +1 -0
  273. package/dist/tool-diff.js +11 -3
  274. package/dist/tool-diff.js.map +7 -1
  275. package/dist/tool-help.d.ts +4 -5
  276. package/dist/tool-help.d.ts.map +1 -0
  277. package/dist/tool-help.js +3 -3
  278. package/dist/tool-help.js.map +7 -1
  279. package/dist/tool-icon-map.d.ts +45 -0
  280. package/dist/tool-icon-map.d.ts.map +1 -0
  281. package/dist/tool-icons.d.ts +5 -6
  282. package/dist/tool-icons.d.ts.map +1 -0
  283. package/dist/tool-icons.js +16 -4
  284. package/dist/tool-icons.js.map +7 -1
  285. package/dist/tool-search.d.ts +4 -5
  286. package/dist/tool-search.d.ts.map +1 -0
  287. package/dist/tool-search.js +3 -3
  288. package/dist/tool-search.js.map +7 -1
  289. package/dist/tool-summary.d.ts +4 -5
  290. package/dist/tool-summary.d.ts.map +1 -0
  291. package/dist/tool-summary.js +5 -3
  292. package/dist/tool-summary.js.map +7 -1
  293. package/dist/tool-use.d.ts +4 -5
  294. package/dist/tool-use.d.ts.map +1 -0
  295. package/dist/tool-use.js +3 -3
  296. package/dist/tool-use.js.map +7 -1
  297. package/dist/tree.d.ts +4 -5
  298. package/dist/tree.d.ts.map +1 -0
  299. package/dist/tree.js +11 -9
  300. package/dist/tree.js.map +7 -1
  301. package/dist/typecheck.d.ts +4 -5
  302. package/dist/typecheck.d.ts.map +1 -0
  303. package/dist/typecheck.js +2416 -28
  304. package/dist/typecheck.js.map +7 -1
  305. package/dist/write.d.ts +4 -5
  306. package/dist/write.d.ts.map +1 -0
  307. package/dist/write.js +21 -16
  308. package/dist/write.js.map +7 -1
  309. package/package.json +34 -6
  310. package/dist/background-indexer-BeDBxfSh.d.ts +0 -371
  311. package/dist/exec-2OKoT6tk.d.ts +0 -121
package/dist/write.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Tool } from '@wrongstack/core';
2
-
1
+ import type { Tool } from '@wrongstack/core';
3
2
  interface WriteInput {
4
3
  path: string;
5
4
  content: string;
@@ -16,6 +15,6 @@ interface WriteOutput {
16
15
  syntax_errors?: string[] | undefined;
17
16
  note?: string | undefined;
18
17
  }
19
- declare const writeTool: Tool<WriteInput, WriteOutput>;
20
-
21
- export { writeTool };
18
+ export declare const writeTool: Tool<WriteInput, WriteOutput>;
19
+ export {};
20
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAItD,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,eAAO,MAAM,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,CAgDnD,CAAC"}
package/dist/write.js CHANGED
@@ -1,16 +1,15 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as Core from '@wrongstack/core';
3
- import { ToolValidationError, atomicWrite, unifiedDiff } from '@wrongstack/core';
4
- import * as path2 from 'node:path';
5
- import { createHash } from 'node:crypto';
6
-
7
1
  // src/write.ts
2
+ import * as fs from "node:fs/promises";
3
+ import { atomicWrite, ToolValidationError, unifiedDiff } from "@wrongstack/core";
4
+
5
+ // src/_syntax-check.ts
6
+ import * as path from "node:path";
8
7
  var TS_LIKE = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
9
8
  var MAX_CHECK_CHARS = 15e5;
10
9
  var MAX_ERRORS = 5;
11
10
  var tsLoad = null;
12
11
  function loadTypescript() {
13
- tsLoad ??= import('typescript').then(
12
+ tsLoad ??= import("@typescript/typescript6").then(
14
13
  (m) => m.default ?? m,
15
14
  () => null
16
15
  );
@@ -28,15 +27,15 @@ async function checkSyntax(filePath, content, previousContent) {
28
27
  return { errors, preExisting };
29
28
  }
30
29
  function isJsoncFile(filePath) {
31
- const base = path2.basename(filePath).toLowerCase();
30
+ const base = path.basename(filePath).toLowerCase();
32
31
  if (base.endsWith(".jsonc")) return true;
33
32
  if (/^(tsconfig|jsconfig)([.-].*)?\.json$/.test(base)) return true;
34
- const dir = path2.basename(path2.dirname(filePath)).toLowerCase();
33
+ const dir = path.basename(path.dirname(filePath)).toLowerCase();
35
34
  return dir === ".vscode";
36
35
  }
37
36
  async function parseErrors(filePath, content) {
38
37
  if (content.length > MAX_CHECK_CHARS) return void 0;
39
- const ext = path2.extname(filePath).toLowerCase();
38
+ const ext = path.extname(filePath).toLowerCase();
40
39
  if (ext === ".json" || ext === ".jsonc") {
41
40
  try {
42
41
  JSON.parse(content);
@@ -63,7 +62,7 @@ async function parseErrors(filePath, content) {
63
62
  ts.ScriptKind.JSX
64
63
  );
65
64
  const sourceFile = ts.createSourceFile(
66
- path2.basename(filePath),
65
+ path.basename(filePath),
67
66
  content,
68
67
  ts.ScriptTarget.Latest,
69
68
  /* setParentNodes */
@@ -87,6 +86,12 @@ function formatDiag(ts, diag, content, sourceFile) {
87
86
  }
88
87
  return `line ${line}: ${message}`;
89
88
  }
89
+
90
+ // src/_util.ts
91
+ import { createHash } from "node:crypto";
92
+ import * as fsp from "node:fs/promises";
93
+ import * as path2 from "node:path";
94
+ import * as Core from "@wrongstack/core";
90
95
  function sha256hex(content) {
91
96
  return createHash("sha256").update(content, "utf8").digest("hex");
92
97
  }
@@ -114,13 +119,13 @@ function safeResolve(input, ctx) {
114
119
  async function assertRealInsideRoot(absPath, ctx) {
115
120
  if (ctx.allowOutsideProjectRoot) return;
116
121
  const realRoots = await Promise.all(
117
- allowedRoots(ctx).map((r) => fs.realpath(r).catch(() => path2.resolve(r)))
122
+ allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path2.resolve(r)))
118
123
  );
119
124
  let probe = absPath;
120
125
  for (; ; ) {
121
126
  let real;
122
127
  try {
123
- real = await fs.realpath(probe);
128
+ real = await fsp.realpath(probe);
124
129
  } catch (err) {
125
130
  if (err.code === "ENOENT") {
126
131
  const parent = path2.dirname(probe);
@@ -250,7 +255,7 @@ async function finishWrite(input, ctx, prepared, signal) {
250
255
  note: hasSyntaxErrors ? syntax.preExisting ? "Syntax check: the file still has parse errors (they pre-date this write) \u2014 see syntax_errors." : `Syntax check: the written content has ${syntax.errors.length} parse error(s) \u2014 fix them now, see syntax_errors.` : void 0
251
256
  };
252
257
  }
253
-
254
- export { writeTool };
258
+ export {
259
+ writeTool
260
+ };
255
261
  //# sourceMappingURL=write.js.map
256
- //# sourceMappingURL=write.js.map
package/dist/write.js.map CHANGED
@@ -1 +1,7 @@
1
- {"version":3,"sources":["../src/_syntax-check.ts","../src/_util.ts","../src/write.ts"],"names":["path","ts","fsp","stat"],"mappings":";;;;;;;AAgBA,IAAM,OAAA,mBAAU,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAM,CAAC,CAAA;AAEtF,IAAM,eAAA,GAAkB,IAAA;AAExB,IAAM,UAAA,GAAa,CAAA;AAInB,IAAI,MAAA,GAAoC,IAAA;AAGxC,SAAS,cAAA,GAAqC;AAC5C,EAAA,MAAA,KAAW,OAAO,YAAY,CAAA,CAAE,IAAA;AAAA,IAC9B,CAAC,CAAA,KAAQ,CAAA,CAAkC,OAAA,IAAW,CAAA;AAAA,IACtD,MAAM;AAAA,GACR;AACA,EAAA,OAAO,MAAA;AACT;AAiBA,eAAsB,WAAA,CACpB,QAAA,EACA,OAAA,EACA,eAAA,EACwC;AACxC,EAAA,MAAM,MAAA,GAAS,MAAM,WAAA,CAAY,QAAA,EAAU,OAAO,CAAA;AAClD,EAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AACjC,EAAA,IAAI,OAAO,MAAA,KAAW,CAAA,SAAU,EAAE,MAAA,EAAQ,aAAa,KAAA,EAAM;AAC7D,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,IAAI,oBAAoB,MAAA,EAAW;AACjC,IAAA,MAAM,UAAA,GAAa,MAAM,WAAA,CAAY,QAAA,EAAU,eAAe,CAAA;AAC9D,IAAA,WAAA,GAAc,UAAA,KAAe,MAAA,IAAa,UAAA,CAAW,MAAA,GAAS,CAAA;AAAA,EAChE;AACA,EAAA,OAAO,EAAE,QAAQ,WAAA,EAAY;AAC/B;AAGA,SAAS,YAAY,QAAA,EAA2B;AAC9C,EAAA,MAAM,IAAA,GAAYA,KAAA,CAAA,QAAA,CAAS,QAAQ,CAAA,CAAE,WAAA,EAAY;AACjD,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,IAAA;AACpC,EAAA,IAAI,sCAAA,CAAuC,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,IAAA;AAC9D,EAAA,MAAM,MAAWA,KAAA,CAAA,QAAA,CAAcA,KAAA,CAAA,OAAA,CAAQ,QAAQ,CAAC,EAAE,WAAA,EAAY;AAC9D,EAAA,OAAO,GAAA,KAAQ,SAAA;AACjB;AAEA,eAAe,WAAA,CAAY,UAAkB,OAAA,EAAgD;AAC3F,EAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,eAAA,EAAiB,OAAO,MAAA;AAC7C,EAAA,MAAM,GAAA,GAAWA,KAAA,CAAA,OAAA,CAAQ,QAAQ,CAAA,CAAE,WAAA,EAAY;AAE/C,EAAA,IAAI,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,QAAA,EAAU;AACvC,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,MAAM,OAAO,CAAA;AAClB,MAAA,OAAO,EAAC;AAAA,IACV,SAAS,GAAA,EAAK;AAKZ,MAAA,IAAI,WAAA,CAAY,QAAQ,CAAA,EAAG;AACzB,QAAA,MAAMC,GAAAA,GAAK,MAAM,cAAA,EAAe;AAChC,QAAA,IAAIA,GAAAA,EAAI;AACN,UAAA,MAAM,KAAA,GAAQA,GAAAA,CAAG,yBAAA,CAA0B,QAAA,EAAU,OAAO,CAAA;AAC5D,UAAA,IAAI,CAAC,KAAA,CAAM,KAAA,EAAO,OAAO,EAAC;AAC1B,UAAA,OAAO,CAAC,UAAA,CAAWA,GAAAA,EAAI,KAAA,CAAM,KAAA,EAAO,OAAO,CAAC,CAAA;AAAA,QAC9C;AAAA,MACF;AACA,MAAA,OAAO,CAAC,CAAA,kBAAA,EAAsB,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,IACvD;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,GAAG,GAAG,OAAO,MAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,MAAM,cAAA,EAAe;AAChC,EAAA,IAAI,CAAC,IAAI,OAAO,MAAA;AAEhB,EAAA,MAAM,UAAA,GACJ,GAAA,KAAQ,MAAA,GACJ,EAAA,CAAG,UAAA,CAAW,GAAA,GACd,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,MAAA,IAAU,GAAA,KAAQ,MAAA,GACzC,GAAG,UAAA,CAAW,EAAA;AAAA;AAAA;AAAA;AAAA,IAId,GAAG,UAAA,CAAW;AAAA,GAAA;AAEtB,EAAA,MAAM,aAAa,EAAA,CAAG,gBAAA;AAAA,IACfD,eAAS,QAAQ,CAAA;AAAA,IACtB,OAAA;AAAA,IACA,GAAG,YAAA,CAAa,MAAA;AAAA;AAAA,IACK,KAAA;AAAA,IACrB;AAAA,GACF;AAIA,EAAA,MAAM,KAAA,GACH,UAAA,CACE,gBAAA,IAAoB,EAAC;AAC1B,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,UAAU,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,UAAA,CAAW,EAAA,EAAI,CAAA,EAAG,OAAA,EAAS,UAAU,CAAC,CAAA;AACrF;AAEA,SAAS,UAAA,CACP,EAAA,EACA,IAAA,EACA,OAAA,EACA,UAAA,EACQ;AACR,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,4BAAA,CAA6B,IAAA,CAAK,aAAa,GAAG,CAAA;AACrE,EAAA,IAAI,IAAA,CAAK,KAAA,KAAU,MAAA,EAAW,OAAO,OAAA;AACrC,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAA,GAAO,UAAA,CAAW,6BAAA,CAA8B,IAAA,CAAK,KAAK,EAAE,IAAA,GAAO,CAAA;AAAA,EACrE,CAAA,MAAO;AACL,IAAA,IAAA,GAAO,CAAA;AACP,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,IAAA,CAAK,SAAS,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACzD,MAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,CAAC,CAAA,KAAM,EAAA,EAAM,IAAA,EAAA;AAAA,IACtC;AAAA,EACF;AACA,EAAA,OAAO,CAAA,KAAA,EAAQ,IAAI,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA;AACjC;ACzIO,SAAS,UAAU,OAAA,EAAyB;AACjD,EAAA,OAAO,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,SAAS,MAAM,CAAA,CAAE,OAAO,KAAK,CAAA;AAClE;AA2BO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAY,KAAA,CAAA,UAAA,CAAW,KAAK,CAAA,GAAS,KAAA,CAAA,SAAA,CAAU,KAAK,CAAA,GAAS,KAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,UAAA,IAAc,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACvG;AAOA,SAAS,aAAa,GAAA,EAAwB;AAC5C,EAAA,OAAO,CAAM,cAAQ,GAAA,CAAI,WAAW,GAAQ,KAAA,CAAA,OAAA,CAAa,IAAA,CAAA,gBAAA,EAAkB,CAAC,CAAA;AAC9E;AAGA,SAAS,WAAA,CAAY,QAAgB,KAAA,EAA0B;AAC7D,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS;AAC1B,IAAA,MAAM,GAAA,GAAW,KAAA,CAAA,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AACtC,IAAA,OAAO,GAAA,KAAQ,MAAO,CAAC,GAAA,CAAI,WAAW,IAAI,CAAA,IAAK,CAAM,KAAA,CAAA,UAAA,CAAW,GAAG,CAAA;AAAA,EACrE,CAAC,CAAA;AACH;AAEO,SAAS,gBAAA,CAAiB,SAAiB,GAAA,EAAsB;AACtE,EAAA,MAAM,MAAA,GAAc,cAAQ,OAAO,CAAA;AAEnC,EAAA,IAAI,GAAA,CAAI,yBAAyB,OAAO,MAAA;AACxC,EAAA,IAAI,YAAY,MAAA,EAAQ,YAAA,CAAa,GAAG,CAAC,GAAG,OAAO,MAAA;AACnD,EAAA,MAAM,IAAI,MAAM,CAAA,MAAA,EAAS,OAAO,8BAAmC,KAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAC,CAAA,CAAA,CAAG,CAAA;AAChG;AAEO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AACtD;AAgBA,eAAsB,oBAAA,CAAqB,SAAiB,GAAA,EAA6B;AAEvF,EAAA,IAAI,IAAI,uBAAA,EAAyB;AAGjC,EAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CAAQ,GAAA;AAAA,IAC9B,YAAA,CAAa,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAUE,EAAA,CAAA,QAAA,CAAS,CAAC,CAAA,CAAE,KAAA,CAAM,MAAW,KAAA,CAAA,OAAA,CAAQ,CAAC,CAAC,CAAC;AAAA,GAC3E;AACA,EAAA,IAAI,KAAA,GAAQ,OAAA;AACZ,EAAA,WAAS;AACP,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAUA,YAAS,KAAK,CAAA;AAAA,IACjC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAK,GAAA,CAA8B,SAAS,QAAA,EAAU;AACpD,QAAA,MAAM,MAAA,GAAc,cAAQ,KAAK,CAAA;AACjC,QAAA,IAAI,WAAW,KAAA,EAAO;AACtB,QAAA,KAAA,GAAQ,MAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AACA,IAAA,IAAI,WAAA,CAAY,IAAA,EAAM,SAAS,CAAA,EAAG;AAClC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,MAAA,EAAS,OAAO,CAAA,mDAAA,EAAsD,SAAA,CAAU,CAAC,CAAC,CAAA,CAAA;AAAA,KACpF;AAAA,EACF;AACF;AAGA,eAAsB,eAAA,CAAgB,OAAe,GAAA,EAA+B;AAClF,EAAA,MAAM,GAAA,GAAM,WAAA,CAAY,KAAA,EAAO,GAAG,CAAA;AAClC,EAAA,MAAM,oBAAA,CAAqB,KAAK,GAAG,CAAA;AACnC,EAAA,OAAO,GAAA;AACT;;;ACjGO,IAAM,SAAA,GAA2C;AAAA,EACtD,IAAA,EAAM,OAAA;AAAA,EACN,QAAA,EAAU,YAAA;AAAA,EACV,WAAA,EACE,kPAAA;AAAA,EAGF,SAAA,EACE,seAAA;AAAA,EAKF,SAAA,EAAW;AAAA,IACT,YAAA,EAAc,sDAAA;AAAA,IACd,UAAA,EAAY,CAAC,MAAM;AAAA,GACrB;AAAA,EACA,UAAA,EAAY,SAAA;AAAA,EACZ,QAAA,EAAU,IAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,YAAA,EAAc,CAAC,UAAU,CAAA;AAAA,EACzB,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA,QAAA,EAAU,CAAC,MAAA,EAAQ,SAAS;AAAA,GAC9B;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,OAAO,SAAA,CAAU,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM,MAAM,CAAA;AAAA,EAC3C,CAAA;AAAA,EACA,OAAO,aAAA,CAAc,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AACrC,IAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,KAAA,EAAO,GAAG,CAAA;AAC9C,IAAA,IAAI,CAAC,SAAS,OAAA,EAAS;AACrB,MAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA,EAAG;AAC5C,QAAA,MAAM,EAAE,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,GAAG,IAAI;AAAA,CAAA,EAAM,IAAA,EAAM,EAAE,WAAA,EAAa,IAAA,EAAK,EAAE;AAAA,MACjF;AAAA,IACF;AACA,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,MAAA,EAAQ,MAAM,WAAA,CAAY,KAAA,EAAO,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,MAAM,CAAA,EAAE;AAAA,EACvF;AACF;AAQA,eAAe,SAAA,CACb,KAAA,EACA,GAAA,EACA,MAAA,EACsB;AACtB,EAAA,OAAO,WAAA,CAAY,OAAO,GAAA,EAAK,MAAM,aAAa,KAAA,EAAO,GAAG,GAAG,MAAM,CAAA;AACvE;AAEA,eAAe,YAAA,CAAa,OAAmB,GAAA,EAAsC;AACnF,EAAA,IAAI,CAAC,OAAO,IAAA,EAAM;AAChB,IAAA,MAAM,IAAI,mBAAA,CAAoB;AAAA,MAC5B,OAAA,EAAS,yBAAA;AAAA,MACT,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,IAAI,KAAA,CAAM,YAAY,MAAA,EAAW;AAC/B,IAAA,MAAM,IAAI,mBAAA,CAAoB;AAAA,MAC5B,OAAA,EAAS,4BAAA;AAAA,MACT,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,MAAM,OAAA,GAAU,MAAM,eAAA,CAAgB,KAAA,CAAM,MAAM,GAAG,CAAA;AAErD,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,IAAI;AACF,IAAA,MAAMC,KAAAA,GAAO,MAAS,EAAA,CAAA,IAAA,CAAK,OAAO,CAAA;AAClC,IAAA,OAAA,GAAUA,MAAK,MAAA,EAAO;AACtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAI,CAAC,GAAA,CAAI,OAAA,CAAQ,OAAO,CAAA,EAAG;AAOzB,QAAA,IAAA,GAAO,MAAS,EAAA,CAAA,QAAA,CAAS,OAAA,EAAS,MAAM,CAAA;AACxC,QAAA,GAAA,CAAI,WAAW,OAAA,EAASA,KAAAA,CAAK,SAAS,OAAA,EAAS,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,MAChE,CAAA,MAAO;AACL,QAAA,IAAA,GAAO,MAAS,EAAA,CAAA,QAAA,CAAS,OAAA,EAAS,MAAM,CAAA;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,IAAK,GAAA,CAA8B,SAAS,QAAA,EAAU;AACpD,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,IAAA,EAAK;AAClC;AAEA,eAAe,WAAA,CACb,KAAA,EACA,GAAA,EACA,QAAA,EACA,MAAA,EACsB;AAKtB,EAAA,MAAA,EAAQ,cAAA,EAAe;AACvB,EAAA,MAAM,WAAA,CAAY,QAAA,CAAS,OAAA,EAAS,KAAA,CAAM,OAAO,CAAA;AAEjD,EAAA,MAAM,OAAO,QAAA,CAAS,OAAA,GAClB,YAAY,QAAA,CAAS,IAAA,EAAM,MAAM,OAAA,EAAS,EAAE,UAAU,KAAA,CAAM,IAAA,EAAM,QAAQ,KAAA,CAAM,IAAA,EAAM,CAAA,GACtF,CAAA,IAAA,EAAO,MAAM,IAAI;AAAA,aAAA,EAAkB,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,IAAI,EAAE,MAAM,CAAA,OAAA,CAAA;AAEvE,EAAA,MAAMA,KAAAA,GAAO,MAAS,EAAA,CAAA,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA;AAI3C,EAAA,GAAA,CAAI,UAAA,CAAW,SAAS,OAAA,EAASA,KAAAA,CAAK,SAAS,OAAA,EAAS,SAAA,CAAU,KAAA,CAAM,OAAO,CAAC,CAAA;AAGhF,EAAA,GAAA,CAAI,QAAQ,gBAAA,CAAiB;AAAA,IAC3B,MAAM,QAAA,CAAS,OAAA;AAAA,IACf,MAAA,EAAQ,QAAA,CAAS,OAAA,GAAU,UAAA,GAAa,SAAA;AAAA,IACxC,MAAA,EAAQ,QAAA,CAAS,OAAA,GAAU,QAAA,CAAS,IAAA,GAAO,IAAA;AAAA,IAC3C,OAAO,KAAA,CAAM;AAAA,GACd,CAAA;AAKD,EAAA,MAAM,SAAS,MAAM,WAAA;AAAA,IACnB,QAAA,CAAS,OAAA;AAAA,IACT,KAAA,CAAM,OAAA;AAAA,IACN,QAAA,CAAS,OAAA,GAAU,QAAA,CAAS,IAAA,GAAO;AAAA,GACrC,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACvB,EAAA,MAAM,eAAA,GAAkB,MAAA,KAAW,MAAA,IAAa,MAAA,CAAO,OAAO,MAAA,GAAS,CAAA;AAEvE,EAAA,OAAO;AAAA,IACL,MAAM,QAAA,CAAS,OAAA;AAAA,IACf,aAAA,EAAe,MAAA,CAAO,UAAA,CAAW,KAAA,CAAM,SAAS,MAAM,CAAA;AAAA,IACtD,OAAA,EAAS,CAAC,QAAA,CAAS,OAAA;AAAA,IACnB,IAAA;AAAA,IACA,aAAA,EAAe,eAAA,GAAkB,MAAA,CAAO,MAAA,GAAS,MAAA;AAAA,IACjD,IAAA,EAAM,kBACF,MAAA,CAAO,WAAA,GACL,uGACA,CAAA,sCAAA,EAAyC,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,uDAAA,CAAA,GAC/D;AAAA,GACN;AACF","file":"write.js","sourcesContent":["/**\n * Post-edit syntax validation for `edit` and `write`.\n *\n * After a mutation lands on disk, the new content is parsed (TS/JS via the\n * TypeScript compiler's parser, JSON via JSON.parse with a JSONC fallback)\n * and any parse errors are surfaced in the tool output. The edit is NOT\n * rolled back — reverting would desynchronize the model's picture of the\n * file — but the errors come back in the same turn so the model fixes the\n * breakage before the user ever opens a broken file.\n *\n * The check is purely syntactic (no type checking, no project program), so\n * it costs single-digit milliseconds per file. Semantic type errors remain\n * the `type-gate` plugin's job.\n */\nimport * as path from 'node:path';\n\nconst TS_LIKE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\n/** Skip pathologically large files — the model never writes these in one go. */\nconst MAX_CHECK_CHARS = 1_500_000;\n/** Cap reported errors so a mangled file doesn't flood the context. */\nconst MAX_ERRORS = 5;\n\ntype Ts = typeof import('typescript');\n\nlet tsLoad: Promise<Ts | null> | null = null;\n/** Lazy-load the TypeScript compiler (already a dependency via the codebase\n * indexer) so tools that never touch TS files don't pay its import cost. */\nfunction loadTypescript(): Promise<Ts | null> {\n tsLoad ??= import('typescript').then(\n (m) => ((m as unknown as { default?: Ts }).default ?? m) as Ts,\n () => null,\n );\n return tsLoad;\n}\n\nexport interface SyntaxCheckResult {\n /** Parse errors in the new content (capped at {@link MAX_ERRORS}). */\n errors: string[];\n /** True when the pre-edit content already failed to parse — the edit did\n * not introduce the breakage (though it didn't fix it either). */\n preExisting: boolean;\n}\n\n/**\n * Check `content` for syntax errors based on the file extension.\n * Returns `undefined` when the file type is not checkable (or the checker\n * is unavailable) and a result with an empty `errors` array when clean.\n * Pass `previousContent` to distinguish newly-introduced errors from\n * pre-existing ones.\n */\nexport async function checkSyntax(\n filePath: string,\n content: string,\n previousContent?: string,\n): Promise<SyntaxCheckResult | undefined> {\n const errors = await parseErrors(filePath, content);\n if (errors === undefined) return undefined;\n if (errors.length === 0) return { errors, preExisting: false };\n let preExisting = false;\n if (previousContent !== undefined) {\n const prevErrors = await parseErrors(filePath, previousContent);\n preExisting = prevErrors !== undefined && prevErrors.length > 0;\n }\n return { errors, preExisting };\n}\n\n/** Files that are conventionally JSONC (comments + trailing commas allowed). */\nfunction isJsoncFile(filePath: string): boolean {\n const base = path.basename(filePath).toLowerCase();\n if (base.endsWith('.jsonc')) return true;\n if (/^(tsconfig|jsconfig)([.-].*)?\\.json$/.test(base)) return true;\n const dir = path.basename(path.dirname(filePath)).toLowerCase();\n return dir === '.vscode';\n}\n\nasync function parseErrors(filePath: string, content: string): Promise<string[] | undefined> {\n if (content.length > MAX_CHECK_CHARS) return undefined;\n const ext = path.extname(filePath).toLowerCase();\n\n if (ext === '.json' || ext === '.jsonc') {\n try {\n JSON.parse(content);\n return [];\n } catch (err) {\n // Known-JSONC files (tsconfig.json, VS Code settings, *.jsonc) accept\n // comments and trailing commas — re-parse with the TS config parser\n // before declaring them broken. Plain .json stays strict: a trailing\n // comma there IS an error the model should fix.\n if (isJsoncFile(filePath)) {\n const ts = await loadTypescript();\n if (ts) {\n const jsonc = ts.parseConfigFileTextToJson(filePath, content);\n if (!jsonc.error) return [];\n return [formatDiag(ts, jsonc.error, content)];\n }\n }\n return [`JSON parse error: ${(err as Error).message}`];\n }\n }\n\n if (!TS_LIKE.has(ext)) return undefined;\n const ts = await loadTypescript();\n if (!ts) return undefined;\n\n const scriptKind =\n ext === '.tsx'\n ? ts.ScriptKind.TSX\n : ext === '.ts' || ext === '.mts' || ext === '.cts'\n ? ts.ScriptKind.TS\n : // Plain JS may legitimately contain JSX; the JSX grammar is a\n // superset for untyped code, so parsing .js/.jsx as JSX avoids\n // false positives on React files.\n ts.ScriptKind.JSX;\n\n const sourceFile = ts.createSourceFile(\n path.basename(filePath),\n content,\n ts.ScriptTarget.Latest,\n /* setParentNodes */ false,\n scriptKind,\n );\n // parseDiagnostics is not in the public .d.ts but has been the stable\n // home of the parser's syntactic diagnostics for a decade (the public\n // alternative, program.getSyntacticDiagnostics, needs a full Program).\n const diags =\n (sourceFile as unknown as { parseDiagnostics?: import('typescript').Diagnostic[] })\n .parseDiagnostics ?? [];\n return diags.slice(0, MAX_ERRORS).map((d) => formatDiag(ts, d, content, sourceFile));\n}\n\nfunction formatDiag(\n ts: Ts,\n diag: import('typescript').Diagnostic,\n content: string,\n sourceFile?: import('typescript').SourceFile,\n): string {\n const message = ts.flattenDiagnosticMessageText(diag.messageText, ' ');\n if (diag.start === undefined) return message;\n let line: number;\n if (sourceFile) {\n line = sourceFile.getLineAndCharacterOfPosition(diag.start).line + 1;\n } else {\n line = 1;\n for (let i = 0; i < diag.start && i < content.length; i++) {\n if (content.charCodeAt(i) === 0x0a) line++;\n }\n }\n return `line ${line}: ${message}`;\n}\n","import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core';\nimport type { Context } from '@wrongstack/core';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` — the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm → yarn → npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root→out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink — macOS `/var`→`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// ─── Command-output normalization (token-saving) ────────────────────────────\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) — never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only — it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `… ⟨repeated ${run}×⟩`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends — the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n…[truncated ${total - kept} bytes]…\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI → collapse\n * carriage-return progress → trim trailing whitespace → collapse identical\n * consecutive lines → squeeze blank-line runs → head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines → 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n","import * as fs from 'node:fs/promises';\nimport { atomicWrite, ToolValidationError, unifiedDiff } from '@wrongstack/core';\nimport type { Context, Tool } from '@wrongstack/core';\nimport { checkSyntax } from './_syntax-check.js';\nimport { safeResolveReal, sha256hex } from './_util.js';\n\ninterface WriteInput {\n path: string;\n content: string;\n}\n\ninterface WriteOutput {\n path: string;\n bytes_written: number;\n created: boolean;\n diff?: string | undefined;\n /**\n * Parse errors found in the written content (TS/JS/JSON only). The file is\n * on disk as written — fix these with a follow-up edit now.\n */\n syntax_errors?: string[] | undefined;\n note?: string | undefined;\n}\n\nexport const writeTool: Tool<WriteInput, WriteOutput> = {\n name: 'write',\n category: 'Filesystem',\n description:\n 'Write or completely overwrite a file on disk. ' +\n 'This is a high-privilege operation. For modifying existing files, you should almost always prefer the `edit` tool instead, ' +\n 'because `edit` is safer and works on the last-read version of the file.',\n usageHint:\n 'RULES FOR CORRECT USAGE:\\n' +\n '- Use `write` primarily for **new files** or when you want to replace the entire content.\\n' +\n '- For any existing file, strongly prefer `edit` (it requires a prior `read` in the same session and is more precise).\\n' +\n '- You MUST have called `read` on the file earlier in the conversation before using `write` on an existing path (the system enforces this for safety).\\n' +\n '- The path is resolved relative to the project root and protected against escaping the workspace.',\n selection: {\n doNotUseWhen: 'making a precise change to part of an existing file.',\n useInstead: ['edit'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 5_000,\n capabilities: ['fs.write'],\n icon: 'file',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Relative path from project root. Must not escape the project.',\n },\n content: {\n type: 'string',\n description: 'The complete new content of the file.',\n },\n },\n required: ['path', 'content'],\n },\n async execute(input, ctx, opts) {\n return writeFile(input, ctx, opts?.signal);\n },\n async *executeStream(input, ctx, opts) {\n const prepared = await prepareWrite(input, ctx);\n if (!prepared.existed) {\n for (const line of input.content.split('\\n')) {\n yield { type: 'partial_output', text: `${line}\\n`, data: { livePreview: true } };\n }\n }\n yield { type: 'final', output: await finishWrite(input, ctx, prepared, opts?.signal) };\n },\n};\n\ntype PreparedWrite = {\n absPath: string;\n existed: boolean;\n prev: string;\n};\n\nasync function writeFile(\n input: WriteInput,\n ctx: Context,\n signal?: AbortSignal | undefined,\n): Promise<WriteOutput> {\n return finishWrite(input, ctx, await prepareWrite(input, ctx), signal);\n}\n\nasync function prepareWrite(input: WriteInput, ctx: Context): Promise<PreparedWrite> {\n if (!input?.path) {\n throw new ToolValidationError({\n message: 'write: path is required',\n field: 'path',\n });\n }\n if (input.content === undefined) {\n throw new ToolValidationError({\n message: 'write: content is required',\n field: 'content',\n });\n }\n const absPath = await safeResolveReal(input.path, ctx);\n\n let existed = false;\n let prev = '';\n try {\n const stat = await fs.stat(absPath);\n existed = stat.isFile();\n if (existed) {\n if (!ctx.hasRead(absPath)) {\n // User approved this write (confirm → yes/always) but ctx has no\n // read record. The model may call write without a prior explicit\n // read. Read the file now so we can compute the diff and honor\n // the user's intent to overwrite. Tag as 'write' (NOT 'user') so\n // this internal read-for-diff does not widen the permission bypass\n // — the user never saw the old content (P1 #1).\n prev = await fs.readFile(absPath, 'utf8');\n ctx.recordRead(absPath, stat.mtimeMs, 'write', sha256hex(prev));\n } else {\n prev = await fs.readFile(absPath, 'utf8');\n }\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n\n return { absPath, existed, prev };\n}\n\nasync function finishWrite(\n input: WriteInput,\n ctx: Context,\n prepared: PreparedWrite,\n signal?: AbortSignal | undefined,\n): Promise<WriteOutput> {\n // Last exit before mutating the filesystem: a run aborted during\n // prepare/diff must not leave a fresh write behind. (atomicWrite itself\n // is all-or-nothing, so past this point the file is old or new — never\n // partial.)\n signal?.throwIfAborted();\n await atomicWrite(prepared.absPath, input.content);\n\n const diff = prepared.existed\n ? unifiedDiff(prepared.prev, input.content, { fromFile: input.path, toFile: input.path })\n : `+++ ${input.path}\\n+ (new file, ${input.content.split('\\n').length} lines)`;\n\n const stat = await fs.stat(prepared.absPath);\n // Tag as 'write' so the permission bypass does not auto-approve a later\n // write to this path — the user approved THIS write, not future ones\n // (P1 #1).\n ctx.recordRead(prepared.absPath, stat.mtimeMs, 'write', sha256hex(input.content));\n\n // Record for session rewind\n ctx.session.recordFileChange({\n path: prepared.absPath,\n action: prepared.existed ? 'modified' : 'created',\n before: prepared.existed ? prepared.prev : null,\n after: input.content,\n });\n\n // Post-write syntax validation (TS/JS/JSON). The file stays on disk as\n // written — errors come back in the same turn so the model fixes them\n // before the user ever opens a broken file.\n const syntax = await checkSyntax(\n prepared.absPath,\n input.content,\n prepared.existed ? prepared.prev : undefined,\n ).catch(() => undefined);\n const hasSyntaxErrors = syntax !== undefined && syntax.errors.length > 0;\n\n return {\n path: prepared.absPath,\n bytes_written: Buffer.byteLength(input.content, 'utf8'),\n created: !prepared.existed,\n diff,\n syntax_errors: hasSyntaxErrors ? syntax.errors : undefined,\n note: hasSyntaxErrors\n ? syntax.preExisting\n ? 'Syntax check: the file still has parse errors (they pre-date this write) — see syntax_errors.'\n : `Syntax check: the written content has ${syntax.errors.length} parse error(s) — fix them now, see syntax_errors.`\n : undefined,\n };\n}\n"]}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/write.ts", "../src/_syntax-check.ts", "../src/_util.ts"],
4
+ "sourcesContent": ["import * as fs from 'node:fs/promises';\nimport { atomicWrite, ToolValidationError, unifiedDiff } from '@wrongstack/core';\nimport type { Context, Tool } from '@wrongstack/core';\nimport { checkSyntax } from './_syntax-check.js';\nimport { safeResolveReal, sha256hex } from './_util.js';\n\ninterface WriteInput {\n path: string;\n content: string;\n}\n\ninterface WriteOutput {\n path: string;\n bytes_written: number;\n created: boolean;\n diff?: string | undefined;\n /**\n * Parse errors found in the written content (TS/JS/JSON only). The file is\n * on disk as written \u2014 fix these with a follow-up edit now.\n */\n syntax_errors?: string[] | undefined;\n note?: string | undefined;\n}\n\nexport const writeTool: Tool<WriteInput, WriteOutput> = {\n name: 'write',\n category: 'Filesystem',\n description:\n 'Write or completely overwrite a file on disk. ' +\n 'This is a high-privilege operation. For modifying existing files, you should almost always prefer the `edit` tool instead, ' +\n 'because `edit` is safer and works on the last-read version of the file.',\n usageHint:\n 'RULES FOR CORRECT USAGE:\\n' +\n '- Use `write` primarily for **new files** or when you want to replace the entire content.\\n' +\n '- For any existing file, strongly prefer `edit` (it requires a prior `read` in the same session and is more precise).\\n' +\n '- You MUST have called `read` on the file earlier in the conversation before using `write` on an existing path (the system enforces this for safety).\\n' +\n '- The path is resolved relative to the project root and protected against escaping the workspace.',\n selection: {\n doNotUseWhen: 'making a precise change to part of an existing file.',\n useInstead: ['edit'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 5_000,\n capabilities: ['fs.write'],\n icon: 'file',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Relative path from project root. Must not escape the project.',\n },\n content: {\n type: 'string',\n description: 'The complete new content of the file.',\n },\n },\n required: ['path', 'content'],\n },\n async execute(input, ctx, opts) {\n return writeFile(input, ctx, opts?.signal);\n },\n async *executeStream(input, ctx, opts) {\n const prepared = await prepareWrite(input, ctx);\n if (!prepared.existed) {\n for (const line of input.content.split('\\n')) {\n yield { type: 'partial_output', text: `${line}\\n`, data: { livePreview: true } };\n }\n }\n yield { type: 'final', output: await finishWrite(input, ctx, prepared, opts?.signal) };\n },\n};\n\ntype PreparedWrite = {\n absPath: string;\n existed: boolean;\n prev: string;\n};\n\nasync function writeFile(\n input: WriteInput,\n ctx: Context,\n signal?: AbortSignal | undefined,\n): Promise<WriteOutput> {\n return finishWrite(input, ctx, await prepareWrite(input, ctx), signal);\n}\n\nasync function prepareWrite(input: WriteInput, ctx: Context): Promise<PreparedWrite> {\n if (!input?.path) {\n throw new ToolValidationError({\n message: 'write: path is required',\n field: 'path',\n });\n }\n if (input.content === undefined) {\n throw new ToolValidationError({\n message: 'write: content is required',\n field: 'content',\n });\n }\n const absPath = await safeResolveReal(input.path, ctx);\n\n let existed = false;\n let prev = '';\n try {\n const stat = await fs.stat(absPath);\n existed = stat.isFile();\n if (existed) {\n if (!ctx.hasRead(absPath)) {\n // User approved this write (confirm \u2192 yes/always) but ctx has no\n // read record. The model may call write without a prior explicit\n // read. Read the file now so we can compute the diff and honor\n // the user's intent to overwrite. Tag as 'write' (NOT 'user') so\n // this internal read-for-diff does not widen the permission bypass\n // \u2014 the user never saw the old content (P1 #1).\n prev = await fs.readFile(absPath, 'utf8');\n ctx.recordRead(absPath, stat.mtimeMs, 'write', sha256hex(prev));\n } else {\n prev = await fs.readFile(absPath, 'utf8');\n }\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n\n return { absPath, existed, prev };\n}\n\nasync function finishWrite(\n input: WriteInput,\n ctx: Context,\n prepared: PreparedWrite,\n signal?: AbortSignal | undefined,\n): Promise<WriteOutput> {\n // Last exit before mutating the filesystem: a run aborted during\n // prepare/diff must not leave a fresh write behind. (atomicWrite itself\n // is all-or-nothing, so past this point the file is old or new \u2014 never\n // partial.)\n signal?.throwIfAborted();\n await atomicWrite(prepared.absPath, input.content);\n\n const diff = prepared.existed\n ? unifiedDiff(prepared.prev, input.content, { fromFile: input.path, toFile: input.path })\n : `+++ ${input.path}\\n+ (new file, ${input.content.split('\\n').length} lines)`;\n\n const stat = await fs.stat(prepared.absPath);\n // Tag as 'write' so the permission bypass does not auto-approve a later\n // write to this path \u2014 the user approved THIS write, not future ones\n // (P1 #1).\n ctx.recordRead(prepared.absPath, stat.mtimeMs, 'write', sha256hex(input.content));\n\n // Record for session rewind\n ctx.session.recordFileChange({\n path: prepared.absPath,\n action: prepared.existed ? 'modified' : 'created',\n before: prepared.existed ? prepared.prev : null,\n after: input.content,\n });\n\n // Post-write syntax validation (TS/JS/JSON). The file stays on disk as\n // written \u2014 errors come back in the same turn so the model fixes them\n // before the user ever opens a broken file.\n const syntax = await checkSyntax(\n prepared.absPath,\n input.content,\n prepared.existed ? prepared.prev : undefined,\n ).catch(() => undefined);\n const hasSyntaxErrors = syntax !== undefined && syntax.errors.length > 0;\n\n return {\n path: prepared.absPath,\n bytes_written: Buffer.byteLength(input.content, 'utf8'),\n created: !prepared.existed,\n diff,\n syntax_errors: hasSyntaxErrors ? syntax.errors : undefined,\n note: hasSyntaxErrors\n ? syntax.preExisting\n ? 'Syntax check: the file still has parse errors (they pre-date this write) \u2014 see syntax_errors.'\n : `Syntax check: the written content has ${syntax.errors.length} parse error(s) \u2014 fix them now, see syntax_errors.`\n : undefined,\n };\n}\n", "/**\n * Post-edit syntax validation for `edit` and `write`.\n *\n * After a mutation lands on disk, the new content is parsed (TS/JS via the\n * TypeScript compiler's parser, JSON via JSON.parse with a JSONC fallback)\n * and any parse errors are surfaced in the tool output. The edit is NOT\n * rolled back \u2014 reverting would desynchronize the model's picture of the\n * file \u2014 but the errors come back in the same turn so the model fixes the\n * breakage before the user ever opens a broken file.\n *\n * The check is purely syntactic (no type checking, no project program), so\n * it costs single-digit milliseconds per file. Semantic type errors remain\n * the `type-gate` plugin's job.\n */\nimport * as path from 'node:path';\n\nconst TS_LIKE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);\n/** Skip pathologically large files \u2014 the model never writes these in one go. */\nconst MAX_CHECK_CHARS = 1_500_000;\n/** Cap reported errors so a mangled file doesn't flood the context. */\nconst MAX_ERRORS = 5;\n\ntype Ts = typeof import('@typescript/typescript6');\n\nlet tsLoad: Promise<Ts | null> | null = null;\n/** Lazy-load the TypeScript compiler (already a dependency via the codebase\n * indexer) so tools that never touch TS files don't pay its import cost. */\nfunction loadTypescript(): Promise<Ts | null> {\n tsLoad ??= import('@typescript/typescript6').then(\n (m) => ((m as unknown as { default?: Ts }).default ?? m) as Ts,\n () => null,\n );\n return tsLoad;\n}\n\nexport interface SyntaxCheckResult {\n /** Parse errors in the new content (capped at {@link MAX_ERRORS}). */\n errors: string[];\n /** True when the pre-edit content already failed to parse \u2014 the edit did\n * not introduce the breakage (though it didn't fix it either). */\n preExisting: boolean;\n}\n\n/**\n * Check `content` for syntax errors based on the file extension.\n * Returns `undefined` when the file type is not checkable (or the checker\n * is unavailable) and a result with an empty `errors` array when clean.\n * Pass `previousContent` to distinguish newly-introduced errors from\n * pre-existing ones.\n */\nexport async function checkSyntax(\n filePath: string,\n content: string,\n previousContent?: string,\n): Promise<SyntaxCheckResult | undefined> {\n const errors = await parseErrors(filePath, content);\n if (errors === undefined) return undefined;\n if (errors.length === 0) return { errors, preExisting: false };\n let preExisting = false;\n if (previousContent !== undefined) {\n const prevErrors = await parseErrors(filePath, previousContent);\n preExisting = prevErrors !== undefined && prevErrors.length > 0;\n }\n return { errors, preExisting };\n}\n\n/** Files that are conventionally JSONC (comments + trailing commas allowed). */\nfunction isJsoncFile(filePath: string): boolean {\n const base = path.basename(filePath).toLowerCase();\n if (base.endsWith('.jsonc')) return true;\n if (/^(tsconfig|jsconfig)([.-].*)?\\.json$/.test(base)) return true;\n const dir = path.basename(path.dirname(filePath)).toLowerCase();\n return dir === '.vscode';\n}\n\nasync function parseErrors(filePath: string, content: string): Promise<string[] | undefined> {\n if (content.length > MAX_CHECK_CHARS) return undefined;\n const ext = path.extname(filePath).toLowerCase();\n\n if (ext === '.json' || ext === '.jsonc') {\n try {\n JSON.parse(content);\n return [];\n } catch (err) {\n // Known-JSONC files (tsconfig.json, VS Code settings, *.jsonc) accept\n // comments and trailing commas \u2014 re-parse with the TS config parser\n // before declaring them broken. Plain .json stays strict: a trailing\n // comma there IS an error the model should fix.\n if (isJsoncFile(filePath)) {\n const ts = await loadTypescript();\n if (ts) {\n const jsonc = ts.parseConfigFileTextToJson(filePath, content);\n if (!jsonc.error) return [];\n return [formatDiag(ts, jsonc.error, content)];\n }\n }\n return [`JSON parse error: ${(err as Error).message}`];\n }\n }\n\n if (!TS_LIKE.has(ext)) return undefined;\n const ts = await loadTypescript();\n if (!ts) return undefined;\n\n const scriptKind =\n ext === '.tsx'\n ? ts.ScriptKind.TSX\n : ext === '.ts' || ext === '.mts' || ext === '.cts'\n ? ts.ScriptKind.TS\n : // Plain JS may legitimately contain JSX; the JSX grammar is a\n // superset for untyped code, so parsing .js/.jsx as JSX avoids\n // false positives on React files.\n ts.ScriptKind.JSX;\n\n const sourceFile = ts.createSourceFile(\n path.basename(filePath),\n content,\n ts.ScriptTarget.Latest,\n /* setParentNodes */ false,\n scriptKind,\n );\n // parseDiagnostics is not in the public .d.ts but has been the stable\n // home of the parser's syntactic diagnostics for a decade (the public\n // alternative, program.getSyntacticDiagnostics, needs a full Program).\n const diags =\n (sourceFile as unknown as {\n parseDiagnostics?: import('@typescript/typescript6').Diagnostic[];\n })\n .parseDiagnostics ?? [];\n return diags.slice(0, MAX_ERRORS).map((d) => formatDiag(ts, d, content, sourceFile));\n}\n\nfunction formatDiag(\n ts: Ts,\n diag: import('@typescript/typescript6').Diagnostic,\n content: string,\n sourceFile?: import('@typescript/typescript6').SourceFile,\n): string {\n const message = ts.flattenDiagnosticMessageText(diag.messageText, ' ');\n if (diag.start === undefined) return message;\n let line: number;\n if (sourceFile) {\n line = sourceFile.getLineAndCharacterOfPosition(diag.start).line + 1;\n } else {\n line = 1;\n for (let i = 0; i < diag.start && i < content.length; i++) {\n if (content.charCodeAt(i) === 0x0a) line++;\n }\n }\n return `line ${line}: ${message}`;\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core';\nimport type { Context } from '@wrongstack/core';\n\n/**\n * sha-256 hex of a UTF-8 string. Used by the file tools to record a content\n * hash alongside the mtime in `ctx.recordRead` \u2014 the hash is the authoritative\n * staleness arbiter for `edit` (mtime has a 2 s tolerance window on Windows).\n */\nexport function sha256hex(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm \u2192 yarn \u2192 npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);\n}\n\n/**\n * Roots every file tool may always reach, even in restricted mode: the\n * project root and the user-global `~/.wrongstack` directory (config, memory,\n * sessions, skills). `~/.wrongstack` honors the `WRONGSTACK_HOME` override.\n */\nfunction allowedRoots(ctx: Context): string[] {\n return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];\n}\n\n/** True if `target` is `root` itself or nested inside any of `roots`. */\nfunction isInsideAny(target: string, roots: string[]): boolean {\n return roots.some((root) => {\n const rel = path.relative(root, target);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n });\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const target = path.resolve(absPath);\n // Unrestricted filesystem access: skip the project-root containment check.\n if (ctx.allowOutsideProjectRoot) return target;\n if (isInsideAny(target, allowedRoots(ctx))) return target;\n throw new Error(`Path \"${absPath}\" is outside project root \"${path.resolve(ctx.projectRoot)}\"`);\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root\u2192out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink \u2014 macOS `/var`\u2192`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n // Unrestricted filesystem access: no symlink-escape check to perform.\n if (ctx.allowOutsideProjectRoot) return;\n // Compare like-for-like against the realpath of each always-allowed root\n // (project root + ~/.wrongstack), since a root may itself be a symlink.\n const realRoots = await Promise.all(\n allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r))),\n );\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n if (isInsideAny(real, realRoots)) return;\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoots[0]}\"`,\n );\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n\u2026[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]\u2026\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// \u2500\u2500\u2500 Command-output normalization (token-saving) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) \u2014 never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only \u2014 it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `\u2026 \u27E8repeated ${run}\u00D7\u27E9`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n /* v8 ignore next -- only caller (truncateHeadTail) passes a budget smaller than s; defensive. */\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends \u2014 the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n\u2026[truncated ${total - kept} bytes]\u2026\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI \u2192 collapse\n * carriage-return progress \u2192 trim trailing whitespace \u2192 collapse identical\n * consecutive lines \u2192 squeeze blank-line runs \u2192 head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines \u2192 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ts", "path", "stat"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/tools",
3
- "version": "0.284.1",
3
+ "version": "0.285.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack built-in tools: read/write/edit, bash/exec, grep/glob, git, fetch, test, lint, and more.",
6
6
  "repository": {
@@ -120,6 +120,10 @@
120
120
  "types": "./dist/typecheck.d.ts",
121
121
  "import": "./dist/typecheck.js"
122
122
  },
123
+ "./languages": {
124
+ "types": "./dist/languages/index.d.ts",
125
+ "import": "./dist/languages/index.js"
126
+ },
123
127
  "./test": {
124
128
  "types": "./dist/test.d.ts",
125
129
  "import": "./dist/test.js"
@@ -172,6 +176,30 @@
172
176
  "types": "./dist/mode.d.ts",
173
177
  "import": "./dist/mode.js"
174
178
  },
179
+ "./design": {
180
+ "types": "./dist/design.d.ts",
181
+ "import": "./dist/design.js"
182
+ },
183
+ "./kanban": {
184
+ "types": "./dist/kanban.d.ts",
185
+ "import": "./dist/kanban.js"
186
+ },
187
+ "./plan": {
188
+ "types": "./dist/plan.d.ts",
189
+ "import": "./dist/plan.js"
190
+ },
191
+ "./ps-slash": {
192
+ "types": "./dist/ps-slash.d.ts",
193
+ "import": "./dist/ps-slash.js"
194
+ },
195
+ "./skill": {
196
+ "types": "./dist/skill.d.ts",
197
+ "import": "./dist/skill.js"
198
+ },
199
+ "./task": {
200
+ "types": "./dist/task.d.ts",
201
+ "import": "./dist/task.js"
202
+ },
175
203
  "./process-registry": {
176
204
  "types": "./dist/process-registry.d.ts",
177
205
  "import": "./dist/process-registry.js"
@@ -193,21 +221,21 @@
193
221
  "dist"
194
222
  ],
195
223
  "dependencies": {
224
+ "@typescript/typescript6": "^6.0.2",
196
225
  "turndown": "^7.2.4",
197
- "typescript": "^6.0.3",
198
226
  "undici": "^8.5.0",
199
- "@wrongstack/core": "0.284.1",
200
- "@wrongstack/kanban": "0.284.1"
227
+ "@wrongstack/core": "0.285.0",
228
+ "@wrongstack/kanban": "0.285.0"
201
229
  },
202
230
  "devDependencies": {
203
231
  "@types/node": "^26.0.1",
204
- "tsup": "^8.5.1"
232
+ "typescript": "^7.0.2"
205
233
  },
206
234
  "publishConfig": {
207
235
  "access": "public"
208
236
  },
209
237
  "scripts": {
210
- "build": "tsup",
238
+ "build": "node ../../scripts/build-package.mjs",
211
239
  "typecheck": "tsc --noEmit",
212
240
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
213
241
  }