@wrongstack/tools 0.284.0 → 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/format.js CHANGED
@@ -1,18 +1,17 @@
1
- import { spawn } from 'node:child_process';
2
- import * as Core from '@wrongstack/core';
3
- import { buildChildEnv, expectDefined, wstackGlobalRoot } from '@wrongstack/core';
4
- import * as fs from 'node:fs';
5
- import { mkdirSync, createWriteStream } from 'node:fs';
6
- import * as fsp from 'node:fs/promises';
7
- import * as path3 from 'node:path';
8
- import * as os from 'node:os';
9
-
10
1
  // src/_spawn-stream.ts
2
+ import { spawn as spawn2 } from "node:child_process";
3
+ import { buildChildEnv } from "@wrongstack/core";
4
+
5
+ // src/_output-spool.ts
6
+ import { createWriteStream, mkdirSync } from "node:fs";
7
+ import * as fsp from "node:fs/promises";
8
+ import * as path from "node:path";
9
+ import { wstackGlobalRoot } from "@wrongstack/core";
11
10
  var SPOOL_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
12
11
  var SPOOL_WRITE_HWM_BYTES = 4 * 1024 * 1024;
13
12
  var sweepStarted = false;
14
13
  function toolOutputDir() {
15
- return path3.join(wstackGlobalRoot(), "tool-output");
14
+ return path.join(wstackGlobalRoot(), "tool-output");
16
15
  }
17
16
  function sweepOldSpoolFiles(dir) {
18
17
  if (sweepStarted) return;
@@ -22,7 +21,7 @@ function sweepOldSpoolFiles(dir) {
22
21
  const now = Date.now();
23
22
  for (const name of await fsp.readdir(dir)) {
24
23
  if (!name.endsWith(".log")) continue;
25
- const p = path3.join(dir, name);
24
+ const p = path.join(dir, name);
26
25
  try {
27
26
  const st = await fsp.stat(p);
28
27
  if (now - st.mtimeMs > SPOOL_RETENTION_MS) await fsp.unlink(p);
@@ -39,7 +38,7 @@ function spoolNote(info) {
39
38
  [output truncated \u2014 full ${info.bytes} bytes at ${info.path}${dropped}; read/grep that file selectively instead of re-running with more output]`;
40
39
  }
41
40
  function createOutputSpool(opts) {
42
- const threshold = opts.thresholdBytes;
41
+ const threshold = opts.thresholdBytes ?? 32768;
43
42
  const safeTool = opts.tool.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
44
43
  let head = "";
45
44
  let headBytes = 0;
@@ -57,7 +56,7 @@ function createOutputSpool(opts) {
57
56
  sweepOldSpoolFiles(dir);
58
57
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
59
58
  const rand = Math.random().toString(36).slice(2, 6);
60
- filePath = path3.join(dir, `${stamp}-${safeTool}-${rand}.log`);
59
+ filePath = path.join(dir, `${stamp}-${safeTool}-${rand}.log`);
61
60
  stream = createWriteStream(filePath, { flags: "w", encoding: "utf8" });
62
61
  stream.on("error", () => {
63
62
  failed = true;
@@ -110,6 +109,10 @@ function createOutputSpool(opts) {
110
109
  };
111
110
  }
112
111
 
112
+ // src/process-registry.ts
113
+ import { spawn } from "node:child_process";
114
+ import * as os from "node:os";
115
+
113
116
  // src/circuit-breaker.ts
114
117
  var DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;
115
118
  var DEFAULT_SLOW_CALL_THRESHOLD_MS = 18e4;
@@ -302,7 +305,8 @@ var CircuitBreaker = class {
302
305
  }
303
306
  };
304
307
 
305
- // src/process-registry.ts
308
+ // src/_redact-command.ts
309
+ import { expectDefined } from "@wrongstack/core";
306
310
  var SENSITIVE_FLAG_PATTERNS = [
307
311
  // --flag=value or --flag "value" (value captured up to next space or comma)
308
312
  /--(?:token|password|passwd|pwd|secret|api[-_]?key|api[-_]?secret|auth|credential|private[-_]?key|access[-_]?key|github[-_]?token|gh[-_]?token|bearer|jwt|oauth|pin|pincode|passphrase|access[-_]?token)(?:[=\s,][^\s]*)?/gi,
@@ -332,6 +336,8 @@ function redactCommand(cmd) {
332
336
  }
333
337
  return result;
334
338
  }
339
+
340
+ // src/process-registry.ts
335
341
  var DEFAULT_GRACE_MS = 2e3;
336
342
  var WIN32_TASKKILL_TIMEOUT_MS = 5e3;
337
343
  function killWin32Tree(pid, opts = {}) {
@@ -601,7 +607,8 @@ var ProcessRegistryImpl = class {
601
607
  if (liveRealChild && killWin32Tree(pid, {
602
608
  timeoutMs: Math.max(graceMs, WIN32_TASKKILL_TIMEOUT_MS),
603
609
  onSettled: directFallback
604
- })) ; else {
610
+ })) {
611
+ } else {
605
612
  try {
606
613
  p.child.kill(force ? "SIGKILL" : "SIGTERM");
607
614
  } catch {
@@ -688,15 +695,19 @@ function getProcessRegistry() {
688
695
  }
689
696
  return _registry;
690
697
  }
698
+
699
+ // src/_win32-resolve.ts
700
+ import * as fs from "node:fs";
701
+ import * as path2 from "node:path";
691
702
  function resolveWin32Command(cmd) {
692
703
  if (process.platform !== "win32") return cmd;
693
- if (cmd.includes("/") || cmd.includes("\\") || path3.extname(cmd.replace(/\//g, "\\"))) {
704
+ if (cmd.includes("/") || cmd.includes("\\") || path2.extname(cmd.replace(/\//g, "\\"))) {
694
705
  return cmd;
695
706
  }
696
707
  const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
697
- const pathDirs = (process.env["PATH"] ?? "").split(path3.delimiter);
708
+ const pathDirs = (process.env["PATH"] ?? "").split(path2.delimiter);
698
709
  for (const dir of pathDirs) {
699
- const base = path3.join(dir, cmd);
710
+ const base = path2.join(dir, cmd);
700
711
  for (const ext of pathext) {
701
712
  const full = `${base}${ext}`;
702
713
  try {
@@ -734,7 +745,7 @@ function quoteWin32CmdArg(arg) {
734
745
  // src/_spawn-stream.ts
735
746
  var isWin = process.platform === "win32";
736
747
  async function* spawnStream(opts) {
737
- const max = opts.maxBytes;
748
+ const max = opts.maxBytes ?? 2e5;
738
749
  const flushAt = opts.flushBytes ?? 4 * 1024;
739
750
  const maxQueue = opts.maxQueueSize ?? 500;
740
751
  let stdout = "";
@@ -747,7 +758,7 @@ async function* spawnStream(opts) {
747
758
  const shim = needsShell ? buildWin32CmdShimInvocation(resolved, opts.args) : null;
748
759
  const cmd = shim?.command ?? resolved;
749
760
  const args = shim?.args ?? opts.args;
750
- const child = spawn(cmd, args, {
761
+ const child = spawn2(cmd, args, {
751
762
  cwd: opts.cwd,
752
763
  env: buildChildEnv(),
753
764
  stdio: ["ignore", "pipe", "pipe"],
@@ -840,8 +851,8 @@ async function* spawnStream(opts) {
840
851
  try {
841
852
  for (; ; ) {
842
853
  while (queue.length === 0) {
843
- await new Promise((resolve2) => {
844
- waiter = resolve2;
854
+ await new Promise((resolve6) => {
855
+ waiter = resolve6;
845
856
  });
846
857
  }
847
858
  const chunk = queue.shift();
@@ -895,6 +906,10 @@ async function* spawnStream(opts) {
895
906
  }
896
907
  }
897
908
  }
909
+
910
+ // src/_util.ts
911
+ import * as path3 from "node:path";
912
+ import * as Core from "@wrongstack/core";
898
913
  function resolvePath(input, ctx) {
899
914
  return path3.isAbsolute(input) ? path3.normalize(input) : path3.resolve(ctx.workingDir ?? ctx.cwd, input);
900
915
  }
@@ -987,6 +1002,2356 @@ function normalizeCommandOutput(raw, opts = {}) {
987
1002
  return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);
988
1003
  }
989
1004
 
1005
+ // src/languages/legacy-bridge.ts
1006
+ import * as fs5 from "node:fs/promises";
1007
+ import * as path8 from "node:path";
1008
+
1009
+ // src/languages/detect.ts
1010
+ import { createHash } from "node:crypto";
1011
+ import * as fs2 from "node:fs/promises";
1012
+ import * as path4 from "node:path";
1013
+
1014
+ // src/languages/profile-helpers.ts
1015
+ var DEFAULT_TIMEOUT_MS = 12e4;
1016
+ var DEFAULT_OUTPUT_LIMIT_BYTES = 2e5;
1017
+ function processPlan(ctx, operation, command, args, options) {
1018
+ return {
1019
+ profileId: ctx.workspace.language,
1020
+ workspaceId: ctx.workspace.id,
1021
+ operation,
1022
+ kind: "process",
1023
+ command,
1024
+ args: Object.freeze([...args]),
1025
+ cwd: ctx.workspace.root,
1026
+ env: Object.freeze({}),
1027
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
1028
+ outputLimitBytes: DEFAULT_OUTPUT_LIMIT_BYTES,
1029
+ mutating: options.mutating ?? false,
1030
+ network: options.network ?? false,
1031
+ executesProjectCode: options.executesProjectCode ?? false,
1032
+ reason: options.reason,
1033
+ evidence: ctx.workspace.evidence,
1034
+ parser: options.parser
1035
+ };
1036
+ }
1037
+ function internalPlan(ctx, operation, parser, reason) {
1038
+ return {
1039
+ profileId: ctx.workspace.language,
1040
+ workspaceId: ctx.workspace.id,
1041
+ operation,
1042
+ kind: "internal",
1043
+ command: null,
1044
+ args: Object.freeze([]),
1045
+ cwd: ctx.workspace.root,
1046
+ env: Object.freeze({}),
1047
+ timeoutMs: 5e3,
1048
+ outputLimitBytes: 32768,
1049
+ mutating: false,
1050
+ network: false,
1051
+ executesProjectCode: false,
1052
+ reason,
1053
+ evidence: ctx.workspace.evidence,
1054
+ parser
1055
+ };
1056
+ }
1057
+ function unavailable(ctx, operation, reason) {
1058
+ return {
1059
+ status: "unavailable",
1060
+ profileId: ctx.workspace.language,
1061
+ workspaceId: ctx.workspace.id,
1062
+ operation,
1063
+ reason
1064
+ };
1065
+ }
1066
+ function packageNames(ctx) {
1067
+ return ctx.options.packages ?? [];
1068
+ }
1069
+
1070
+ // src/languages/profiles/additional.ts
1071
+ var IGNORES = Object.freeze([
1072
+ ".git",
1073
+ ".wrongstack",
1074
+ "node_modules",
1075
+ "vendor",
1076
+ "dist",
1077
+ "build",
1078
+ "coverage"
1079
+ ]);
1080
+ function pythonProfile() {
1081
+ return {
1082
+ id: "python",
1083
+ displayName: "Python",
1084
+ extensions: Object.freeze([".py", ".pyi"]),
1085
+ lspLanguageIds: Object.freeze(["python"]),
1086
+ detectors: Object.freeze([
1087
+ { kind: "config", filename: "pyproject.toml", weight: 90 },
1088
+ { kind: "config", filename: "setup.py", weight: 70 },
1089
+ { kind: "config", filename: "setup.cfg", weight: 60 },
1090
+ { kind: "manifest", filename: "requirements.txt", weight: 55 },
1091
+ { kind: "manifest", filename: "Pipfile", weight: 50 },
1092
+ { kind: "lockfile", filename: "poetry.lock", weight: 30 },
1093
+ { kind: "lockfile", filename: "Pipfile.lock", weight: 30 },
1094
+ { kind: "lockfile", filename: "uv.lock", weight: 30 }
1095
+ ]),
1096
+ ignoredDirectories: IGNORES,
1097
+ packageManagers: Object.freeze(["pip", "poetry", "pipenv", "uv"]),
1098
+ executables: Object.freeze(["python", "python3", "ruff", "black", "pytest", "mypy", "pyright"]),
1099
+ operations: Object.freeze({
1100
+ syntax: async (ctx) => {
1101
+ const py = ctx.options.target ? "python3" : "python3";
1102
+ return ctx.options.target ? processPlan(ctx, "syntax", py, ["-m", "py_compile", ctx.options.target], {
1103
+ parser: "python",
1104
+ reason: "Compile the target file to check for syntax errors."
1105
+ }) : unavailable(ctx, "syntax", "Python syntax check requires an explicit target file.");
1106
+ },
1107
+ semantic: async (ctx) => processPlan(ctx, "semantic", "mypy", [".", "--no-error-summary"], {
1108
+ parser: "mypy",
1109
+ reason: "Run mypy type checking on the workspace.",
1110
+ executesProjectCode: true
1111
+ }),
1112
+ lint: async (ctx) => processPlan(ctx, "lint", "ruff", ["check", "."], {
1113
+ parser: "ruff",
1114
+ reason: "Run the Ruff linter on the workspace."
1115
+ }),
1116
+ "format-check": async (ctx) => processPlan(ctx, "format-check", "ruff", ["format", "--check", "."], {
1117
+ parser: "ruff",
1118
+ reason: "Check Python formatting without writing files."
1119
+ }),
1120
+ "format-write": async (ctx) => processPlan(ctx, "format-write", "ruff", ["format", "."], {
1121
+ parser: "ruff",
1122
+ reason: "Format Python source files.",
1123
+ mutating: true
1124
+ }),
1125
+ test: async (ctx) => processPlan(
1126
+ ctx,
1127
+ "test",
1128
+ "pytest",
1129
+ [...ctx.options.filter ? ["-k", ctx.options.filter] : [], "."],
1130
+ {
1131
+ parser: "pytest",
1132
+ reason: ctx.options.filter ? "Run filtered Python tests." : "Run all Python tests.",
1133
+ mutating: true,
1134
+ executesProjectCode: true
1135
+ }
1136
+ ),
1137
+ build: async (ctx) => unavailable(ctx, "build", "Python is interpreted; use semantic or test instead."),
1138
+ "debug-compile": async (ctx) => processPlan(ctx, "debug-compile", "mypy", [".", "--no-error-summary"], {
1139
+ parser: "mypy",
1140
+ reason: "Collect mypy type diagnostics.",
1141
+ executesProjectCode: true
1142
+ }),
1143
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "pip", ["install", "--no-cache-dir"], {
1144
+ parser: "package-text",
1145
+ reason: "Install dependencies from requirements.",
1146
+ mutating: true,
1147
+ network: true
1148
+ }),
1149
+ "package-add": async (ctx) => {
1150
+ const names = packageNames(ctx);
1151
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one package name is required.") : processPlan(ctx, "package-add", "pip", ["install", ...names], {
1152
+ parser: "package-text",
1153
+ reason: "Install specified Python packages.",
1154
+ mutating: true,
1155
+ network: true
1156
+ });
1157
+ },
1158
+ "package-audit": async (ctx) => processPlan(ctx, "package-audit", "pip", ["audit"], {
1159
+ parser: "pip-audit",
1160
+ reason: "Audit Python dependencies for vulnerabilities.",
1161
+ network: true
1162
+ }),
1163
+ run: async (ctx) => {
1164
+ const entries = ["main.py", "app.py", "__main__.py", "manage.py"];
1165
+ const first = (await Promise.all(
1166
+ entries.map((name) => ctx.pathExists(name).then((ok) => ok ? name : void 0))
1167
+ )).find(Boolean);
1168
+ const args = first ? [first] : [];
1169
+ return processPlan(ctx, "run", "python3", args, {
1170
+ parser: "command-text",
1171
+ reason: first ? `Run the Python entry point ${first}.` : "Run the Python project (no common entry point detected \u2014 add the module path manually).",
1172
+ executesProjectCode: true,
1173
+ mutating: true
1174
+ });
1175
+ }
1176
+ })
1177
+ };
1178
+ }
1179
+ function hasGradleEvidence(ctx) {
1180
+ return ctx.workspace.evidence.some(
1181
+ (e) => e.kind === "manifest" && (e.value === "build.gradle" || e.value === "build.gradle.kts" || e.value === "settings.gradle") || e.kind === "lockfile" && e.value === "gradle.lockfile"
1182
+ );
1183
+ }
1184
+ async function gradleRunner(ctx) {
1185
+ if (await ctx.pathExists("gradlew")) return "gradlew";
1186
+ return "gradle";
1187
+ }
1188
+ function javaProfile() {
1189
+ return {
1190
+ id: "java",
1191
+ displayName: "Java / Kotlin",
1192
+ extensions: Object.freeze([".java", ".kt", ".kts", ".scala"]),
1193
+ lspLanguageIds: Object.freeze(["java", "kotlin"]),
1194
+ detectors: Object.freeze([
1195
+ { kind: "manifest", filename: "pom.xml", weight: 90 },
1196
+ { kind: "manifest", filename: "build.gradle", weight: 85 },
1197
+ { kind: "manifest", filename: "build.gradle.kts", weight: 85 },
1198
+ { kind: "manifest", filename: "settings.gradle", weight: 50 },
1199
+ { kind: "lockfile", filename: "gradle.lockfile", weight: 30 }
1200
+ ]),
1201
+ ignoredDirectories: IGNORES,
1202
+ packageManagers: Object.freeze(["maven", "gradle"]),
1203
+ executables: Object.freeze(["mvn", "gradle", "gradlew", "java", "kotlinc"]),
1204
+ operations: Object.freeze({
1205
+ semantic: async (ctx) => {
1206
+ const isGradle = hasGradleEvidence(ctx);
1207
+ const runner = isGradle ? await gradleRunner(ctx) : "mvn";
1208
+ const args = isGradle ? ["compileJava"] : ["compile", "-q"];
1209
+ const reason = isGradle ? "Compile the Gradle project." : "Compile the Maven project.";
1210
+ return processPlan(ctx, "semantic", runner, args, {
1211
+ parser: isGradle ? "gradle" : "maven",
1212
+ reason,
1213
+ mutating: true,
1214
+ executesProjectCode: true
1215
+ });
1216
+ },
1217
+ lint: async (ctx) => unavailable(
1218
+ ctx,
1219
+ "lint",
1220
+ "Java linting requires a configured checkstyle or spotbugs plugin."
1221
+ ),
1222
+ "format-check": async (ctx) => unavailable(
1223
+ ctx,
1224
+ "format-check",
1225
+ "Java formatting check requires a configured spotless or google-java-format plugin."
1226
+ ),
1227
+ test: async (ctx) => {
1228
+ const isGradle = hasGradleEvidence(ctx);
1229
+ const runner = isGradle ? await gradleRunner(ctx) : "mvn";
1230
+ const args = isGradle ? ["test"] : ["test", "-q"];
1231
+ const reason = isGradle ? "Run Gradle tests." : "Run Maven tests.";
1232
+ return processPlan(ctx, "test", runner, args, {
1233
+ parser: isGradle ? "gradle" : "maven",
1234
+ reason,
1235
+ mutating: true,
1236
+ executesProjectCode: true
1237
+ });
1238
+ },
1239
+ build: async (ctx) => {
1240
+ const isGradle = hasGradleEvidence(ctx);
1241
+ const runner = isGradle ? await gradleRunner(ctx) : "mvn";
1242
+ const args = isGradle ? ["build"] : ["package", "-q", "-DskipTests"];
1243
+ const reason = isGradle ? "Build the Gradle project." : "Build the Maven project, skipping tests.";
1244
+ return processPlan(ctx, "build", runner, args, {
1245
+ parser: isGradle ? "gradle" : "maven",
1246
+ reason,
1247
+ mutating: true,
1248
+ executesProjectCode: true
1249
+ });
1250
+ },
1251
+ run: async (ctx) => {
1252
+ if (!hasGradleEvidence(ctx))
1253
+ return unavailable(
1254
+ ctx,
1255
+ "run",
1256
+ "Maven run requires exec-maven-plugin configuration. Use `mvn exec:java -q` manually."
1257
+ );
1258
+ const runner = await gradleRunner(ctx);
1259
+ return processPlan(ctx, "run", runner, ["run"], {
1260
+ parser: "command-text",
1261
+ reason: `Run the Gradle project entry point via ${runner}.`,
1262
+ executesProjectCode: true,
1263
+ mutating: true
1264
+ });
1265
+ },
1266
+ "package-install": async (ctx) => unavailable(
1267
+ ctx,
1268
+ "package-install",
1269
+ "JVM dependency installation happens through build or dependency:get."
1270
+ ),
1271
+ "package-add": async (ctx) => {
1272
+ const names = packageNames(ctx);
1273
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one dependency coordinate is required.") : processPlan(ctx, "package-add", "mvn", ["dependency:get", `-Dartifact=${names[0]}`], {
1274
+ parser: "maven",
1275
+ reason: "Resolve and fetch a Maven dependency.",
1276
+ mutating: true,
1277
+ network: true
1278
+ });
1279
+ }
1280
+ })
1281
+ };
1282
+ }
1283
+ function rubyProfile() {
1284
+ return {
1285
+ id: "ruby",
1286
+ displayName: "Ruby",
1287
+ extensions: Object.freeze([".rb"]),
1288
+ lspLanguageIds: Object.freeze(["ruby"]),
1289
+ detectors: Object.freeze([
1290
+ { kind: "manifest", filename: "Gemfile", weight: 90 },
1291
+ { kind: "config", filename: ".ruby-version", weight: 40 },
1292
+ { kind: "lockfile", filename: "Gemfile.lock", weight: 30 }
1293
+ ]),
1294
+ ignoredDirectories: IGNORES,
1295
+ packageManagers: Object.freeze(["gem", "bundler"]),
1296
+ executables: Object.freeze(["ruby", "gem", "bundle", "rubocop", "rspec"]),
1297
+ operations: Object.freeze({
1298
+ syntax: async (ctx) => ctx.options.target ? processPlan(ctx, "syntax", "ruby", ["-c", ctx.options.target], {
1299
+ parser: "ruby",
1300
+ reason: "Check the target Ruby file for syntax errors."
1301
+ }) : unavailable(ctx, "syntax", "Ruby syntax check requires an explicit target file."),
1302
+ lint: async (ctx) => processPlan(ctx, "lint", "rubocop", ["--format=json"], {
1303
+ parser: "rubocop",
1304
+ reason: "Run RuboCop linter.",
1305
+ executesProjectCode: true
1306
+ }),
1307
+ "format-write": async (ctx) => processPlan(ctx, "format-write", "rubocop", ["--auto-correct"], {
1308
+ parser: "rubocop",
1309
+ reason: "Auto-correct RuboCop violations.",
1310
+ mutating: true,
1311
+ executesProjectCode: true
1312
+ }),
1313
+ test: async (ctx) => processPlan(ctx, "test", "rspec", [], {
1314
+ parser: "rspec",
1315
+ reason: "Run the RSpec test suite.",
1316
+ mutating: true,
1317
+ executesProjectCode: true
1318
+ }),
1319
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "bundle", ["install"], {
1320
+ parser: "package-text",
1321
+ reason: "Install Ruby gem dependencies.",
1322
+ mutating: true,
1323
+ network: true,
1324
+ executesProjectCode: true
1325
+ }),
1326
+ "package-add": async (ctx) => {
1327
+ const names = packageNames(ctx);
1328
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one gem name is required.") : processPlan(ctx, "package-add", "gem", ["install", ...names], {
1329
+ parser: "package-text",
1330
+ reason: "Install specified Ruby gems.",
1331
+ mutating: true,
1332
+ network: true
1333
+ });
1334
+ },
1335
+ "package-audit": async (ctx) => processPlan(ctx, "package-audit", "bundle", ["audit", "--format=json"], {
1336
+ parser: "bundler-audit",
1337
+ reason: "Audit Ruby gems for vulnerabilities.",
1338
+ network: true
1339
+ }),
1340
+ run: async (ctx) => {
1341
+ const entries = ["main.rb", "app.rb", "server.rb", "config.ru"];
1342
+ const first = (await Promise.all(
1343
+ entries.map((name) => ctx.pathExists(name).then((ok) => ok ? name : void 0))
1344
+ )).find(Boolean);
1345
+ const hasGemfile = await ctx.pathExists("Gemfile");
1346
+ const cmd = hasGemfile ? "bundle" : "ruby";
1347
+ const args = hasGemfile ? ["exec", "ruby", first ?? ""] : [first ?? ""];
1348
+ return processPlan(ctx, "run", cmd, args, {
1349
+ parser: "command-text",
1350
+ reason: first ? `Run the Ruby entry point ${first}.` : "Run the Ruby project (no common entry point detected \u2014 add the file path manually).",
1351
+ executesProjectCode: true,
1352
+ mutating: true
1353
+ });
1354
+ }
1355
+ })
1356
+ };
1357
+ }
1358
+ function cProfile() {
1359
+ return {
1360
+ id: "c",
1361
+ displayName: "C / C++",
1362
+ extensions: Object.freeze([".c", ".h"]),
1363
+ lspLanguageIds: Object.freeze(["c"]),
1364
+ detectors: Object.freeze([
1365
+ { kind: "manifest", filename: "CMakeLists.txt", weight: 85 },
1366
+ { kind: "manifest", filename: "Makefile", weight: 60 },
1367
+ { kind: "config", suffix: ".cmake", weight: 50 }
1368
+ ]),
1369
+ ignoredDirectories: IGNORES,
1370
+ packageManagers: Object.freeze([]),
1371
+ executables: Object.freeze(["cc", "gcc", "clang", "cmake", "make"]),
1372
+ operations: Object.freeze({
1373
+ semantic: async (ctx) => processPlan(ctx, "semantic", "cmake", ["--build", ".", "--target", "all"], {
1374
+ parser: "cmake",
1375
+ reason: "Build C project to collect compiler diagnostics.",
1376
+ mutating: true,
1377
+ executesProjectCode: true
1378
+ }),
1379
+ test: async (ctx) => unavailable(
1380
+ ctx,
1381
+ "test",
1382
+ "C test execution requires a configured test runner (ctest, etc.)."
1383
+ ),
1384
+ build: async (ctx) => processPlan(ctx, "build", "cmake", ["--build", "."], {
1385
+ parser: "cmake",
1386
+ reason: "Build the C project.",
1387
+ mutating: true,
1388
+ executesProjectCode: true
1389
+ })
1390
+ })
1391
+ };
1392
+ }
1393
+ function cppProfile() {
1394
+ return {
1395
+ ...cProfile(),
1396
+ id: "cpp",
1397
+ displayName: "C++",
1398
+ extensions: Object.freeze([".cpp", ".cc", ".cxx", ".hpp", ".hxx"]),
1399
+ lspLanguageIds: Object.freeze(["cpp"]),
1400
+ detectors: Object.freeze([
1401
+ { kind: "manifest", filename: "CMakeLists.txt", weight: 85 },
1402
+ { kind: "manifest", filename: "Makefile", weight: 50 }
1403
+ ]),
1404
+ executables: Object.freeze(["c++", "g++", "clang++", "cmake", "make"])
1405
+ };
1406
+ }
1407
+ function swiftProfile() {
1408
+ return {
1409
+ id: "swift",
1410
+ displayName: "Swift",
1411
+ extensions: Object.freeze([".swift"]),
1412
+ lspLanguageIds: Object.freeze(["swift"]),
1413
+ detectors: Object.freeze([{ kind: "manifest", filename: "Package.swift", weight: 90 }]),
1414
+ ignoredDirectories: IGNORES,
1415
+ packageManagers: Object.freeze(["swift"]),
1416
+ executables: Object.freeze(["swift", "swiftc"]),
1417
+ operations: Object.freeze({
1418
+ semantic: async (ctx) => processPlan(ctx, "semantic", "swift", ["build"], {
1419
+ parser: "swift",
1420
+ reason: "Build Swift package to collect compiler diagnostics.",
1421
+ mutating: true,
1422
+ executesProjectCode: true
1423
+ }),
1424
+ test: async (ctx) => processPlan(ctx, "test", "swift", ["test"], {
1425
+ parser: "swift-test",
1426
+ reason: "Run Swift tests.",
1427
+ mutating: true,
1428
+ executesProjectCode: true
1429
+ }),
1430
+ build: async (ctx) => processPlan(ctx, "build", "swift", ["build", "-c", "release"], {
1431
+ parser: "swift",
1432
+ reason: "Build the Swift package in release mode.",
1433
+ mutating: true,
1434
+ executesProjectCode: true
1435
+ }),
1436
+ run: async (ctx) => processPlan(ctx, "run", "swift", ["run"], {
1437
+ parser: "command-text",
1438
+ reason: "Run the Swift package entry point.",
1439
+ executesProjectCode: true
1440
+ })
1441
+ })
1442
+ };
1443
+ }
1444
+ function dartProfile() {
1445
+ return {
1446
+ id: "dart",
1447
+ displayName: "Dart / Flutter",
1448
+ extensions: Object.freeze([".dart"]),
1449
+ lspLanguageIds: Object.freeze(["dart"]),
1450
+ detectors: Object.freeze([
1451
+ { kind: "manifest", filename: "pubspec.yaml", weight: 90 },
1452
+ { kind: "lockfile", filename: "pubspec.lock", weight: 30 }
1453
+ ]),
1454
+ ignoredDirectories: IGNORES,
1455
+ packageManagers: Object.freeze(["pub"]),
1456
+ executables: Object.freeze(["dart", "flutter"]),
1457
+ operations: Object.freeze({
1458
+ semantic: async (ctx) => processPlan(ctx, "semantic", "dart", ["analyze"], {
1459
+ parser: "dart-analyze",
1460
+ reason: "Run Dart static analysis."
1461
+ }),
1462
+ "format-check": async (ctx) => processPlan(
1463
+ ctx,
1464
+ "format-check",
1465
+ "dart",
1466
+ ["format", "--output=none", "--set-exit-if-changed", "."],
1467
+ {
1468
+ parser: "dart-format",
1469
+ reason: "Check Dart formatting without writing files."
1470
+ }
1471
+ ),
1472
+ "format-write": async (ctx) => processPlan(ctx, "format-write", "dart", ["format", "."], {
1473
+ parser: "dart-format",
1474
+ reason: "Format Dart source files.",
1475
+ mutating: true
1476
+ }),
1477
+ test: async (ctx) => processPlan(ctx, "test", "dart", ["test"], {
1478
+ parser: "dart-test",
1479
+ reason: "Run Dart tests.",
1480
+ mutating: true,
1481
+ executesProjectCode: true
1482
+ }),
1483
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "dart", ["pub", "get"], {
1484
+ parser: "package-text",
1485
+ reason: "Fetch Dart package dependencies.",
1486
+ mutating: true,
1487
+ network: true
1488
+ }),
1489
+ "package-outdated": async (ctx) => processPlan(ctx, "package-outdated", "dart", ["pub", "outdated"], {
1490
+ parser: "dart-outdated",
1491
+ reason: "Check for outdated Dart packages.",
1492
+ network: true
1493
+ }),
1494
+ run: async (ctx) => processPlan(ctx, "run", "dart", ["run"], {
1495
+ parser: "command-text",
1496
+ reason: "Run the Dart / Flutter project entry point.",
1497
+ executesProjectCode: true
1498
+ })
1499
+ })
1500
+ };
1501
+ }
1502
+ function denoProfile() {
1503
+ return {
1504
+ id: "deno",
1505
+ displayName: "Deno",
1506
+ extensions: Object.freeze([".ts", ".tsx", ".js", ".jsx"]),
1507
+ lspLanguageIds: Object.freeze(["typescript", "javascript"]),
1508
+ detectors: Object.freeze([
1509
+ { kind: "config", filename: "deno.json", weight: 90 },
1510
+ { kind: "config", filename: "deno.jsonc", weight: 90 },
1511
+ { kind: "config", filename: "import_map.json", weight: 60 }
1512
+ ]),
1513
+ ignoredDirectories: IGNORES,
1514
+ packageManagers: Object.freeze([]),
1515
+ executables: Object.freeze(["deno"]),
1516
+ // .ts/.js extensions are shared with the TypeScript/JavaScript profiles;
1517
+ // only a deno.json(c)/import_map.json detector hit may establish a workspace.
1518
+ sourceFallback: false,
1519
+ operations: Object.freeze({
1520
+ test: async (ctx) => processPlan(ctx, "test", "deno", ["test"], {
1521
+ parser: "deno-test",
1522
+ reason: "Run Deno tests.",
1523
+ mutating: true,
1524
+ executesProjectCode: true
1525
+ }),
1526
+ run: async (ctx) => processPlan(ctx, "run", "deno", ["run", "--allow-all", "main.ts"], {
1527
+ parser: "command-text",
1528
+ reason: "Run the Deno project entry point with full permission.",
1529
+ executesProjectCode: true,
1530
+ mutating: true
1531
+ })
1532
+ })
1533
+ };
1534
+ }
1535
+ function elixirProfile() {
1536
+ return {
1537
+ id: "elixir",
1538
+ displayName: "Elixir",
1539
+ extensions: Object.freeze([".ex", ".exs"]),
1540
+ lspLanguageIds: Object.freeze(["elixir"]),
1541
+ detectors: Object.freeze([
1542
+ { kind: "manifest", filename: "mix.exs", weight: 90 },
1543
+ { kind: "lockfile", filename: "mix.lock", weight: 30 }
1544
+ ]),
1545
+ ignoredDirectories: IGNORES,
1546
+ packageManagers: Object.freeze(["mix"]),
1547
+ executables: Object.freeze(["mix", "elixir"]),
1548
+ operations: Object.freeze({
1549
+ semantic: async (ctx) => unavailable(ctx, "semantic", "Elixir compilation is handled by the build operation."),
1550
+ lint: async (ctx) => unavailable(ctx, "lint", "Elixir linting requires the credo package."),
1551
+ test: async (ctx) => processPlan(ctx, "test", "mix", ["test"], {
1552
+ parser: "mix-test",
1553
+ reason: "Run ExUnit tests.",
1554
+ mutating: true,
1555
+ executesProjectCode: true
1556
+ }),
1557
+ build: async (ctx) => processPlan(ctx, "build", "mix", ["compile"], {
1558
+ parser: "mix",
1559
+ reason: "Compile the Mix project.",
1560
+ mutating: true,
1561
+ executesProjectCode: true
1562
+ }),
1563
+ run: async (ctx) => processPlan(ctx, "run", "mix", ["run"], {
1564
+ parser: "command-text",
1565
+ reason: "Run the Mix project entry point.",
1566
+ executesProjectCode: true
1567
+ }),
1568
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "mix", ["deps.get"], {
1569
+ parser: "package-text",
1570
+ reason: "Fetch Elixir dependencies.",
1571
+ mutating: true,
1572
+ network: true
1573
+ })
1574
+ })
1575
+ };
1576
+ }
1577
+ function shellProfile() {
1578
+ return {
1579
+ id: "shell",
1580
+ displayName: "Shell",
1581
+ extensions: Object.freeze([".sh", ".bash"]),
1582
+ lspLanguageIds: Object.freeze(["shellscript"]),
1583
+ detectors: Object.freeze([{ kind: "config", filename: "ShellCheckrc", weight: 50 }]),
1584
+ ignoredDirectories: IGNORES,
1585
+ packageManagers: Object.freeze([]),
1586
+ executables: Object.freeze(["bash", "sh", "shellcheck", "shfmt"]),
1587
+ operations: Object.freeze({
1588
+ syntax: async (ctx) => ctx.options.target ? processPlan(ctx, "syntax", "bash", ["-n", ctx.options.target], {
1589
+ parser: "shell",
1590
+ reason: "Check the shell script for syntax errors."
1591
+ }) : unavailable(ctx, "syntax", "Shell syntax check requires an explicit target file."),
1592
+ lint: async (ctx) => ctx.options.target ? processPlan(ctx, "lint", "shellcheck", ["--format=json", ctx.options.target], {
1593
+ parser: "shellcheck",
1594
+ reason: "Run ShellCheck on the target script."
1595
+ }) : unavailable(ctx, "lint", "Shell linting requires an explicit target file."),
1596
+ "format-check": async (ctx) => ctx.options.target ? processPlan(ctx, "format-check", "shfmt", ["-d", ctx.options.target], {
1597
+ parser: "shell",
1598
+ reason: "Check shell formatting without writing."
1599
+ }) : unavailable(
1600
+ ctx,
1601
+ "format-check",
1602
+ "Shell formatting check requires an explicit target file."
1603
+ ),
1604
+ "format-write": async (ctx) => ctx.options.target ? processPlan(ctx, "format-write", "shfmt", ["-w", ctx.options.target], {
1605
+ parser: "shell",
1606
+ reason: "Format the shell script.",
1607
+ mutating: true
1608
+ }) : unavailable(ctx, "format-write", "Shell formatting requires an explicit target file.")
1609
+ })
1610
+ };
1611
+ }
1612
+ var ADDITIONAL_LANGUAGE_PROFILES = Object.freeze([
1613
+ pythonProfile(),
1614
+ javaProfile(),
1615
+ rubyProfile(),
1616
+ cProfile(),
1617
+ cppProfile(),
1618
+ swiftProfile(),
1619
+ dartProfile(),
1620
+ denoProfile(),
1621
+ elixirProfile(),
1622
+ shellProfile()
1623
+ ]);
1624
+
1625
+ // src/languages/profiles/primary.ts
1626
+ var COMMON_IGNORES = Object.freeze([
1627
+ ".git",
1628
+ ".wrongstack",
1629
+ "node_modules",
1630
+ "vendor",
1631
+ "target",
1632
+ "bin",
1633
+ "obj",
1634
+ "dist",
1635
+ "build",
1636
+ "coverage"
1637
+ ]);
1638
+ function nodeManager(ctx) {
1639
+ const lockfiles = new Set(
1640
+ ctx.workspace.evidence.filter((evidence) => evidence.kind === "lockfile").map((evidence) => evidence.value.toLowerCase())
1641
+ );
1642
+ if (lockfiles.size > 1 && !ctx.workspace.packageManager) return void 0;
1643
+ return ctx.workspace.packageManager ?? "npm";
1644
+ }
1645
+ function scriptPlan(ctx, operation, script) {
1646
+ const manager = nodeManager(ctx);
1647
+ if (!manager)
1648
+ return unavailable(
1649
+ ctx,
1650
+ operation,
1651
+ "Conflicting Node lockfiles make the package manager ambiguous."
1652
+ );
1653
+ const args = manager === "npm" ? ["run", script] : [script];
1654
+ return processPlan(ctx, operation, manager, args, {
1655
+ parser: "command-text",
1656
+ reason: `Run the detected ${manager} ${script} script for this workspace.`,
1657
+ mutating: true,
1658
+ executesProjectCode: true
1659
+ });
1660
+ }
1661
+ function nodeExec(ctx, executable, args) {
1662
+ const manager = ctx.workspace.packageManager ?? "npm";
1663
+ if (manager === "pnpm") return { command: "pnpm", args: ["exec", executable, ...args] };
1664
+ if (manager === "yarn") return { command: "yarn", args: ["exec", executable, ...args] };
1665
+ if (manager === "bun") return { command: "bun", args: ["x", executable, ...args] };
1666
+ return { command: "npx", args: ["--no-install", executable, ...args] };
1667
+ }
1668
+ function typescriptProfile() {
1669
+ return {
1670
+ id: "typescript",
1671
+ displayName: "TypeScript",
1672
+ extensions: Object.freeze([".ts", ".tsx", ".mts", ".cts"]),
1673
+ lspLanguageIds: Object.freeze(["typescript", "typescriptreact"]),
1674
+ detectors: Object.freeze([
1675
+ { kind: "config", filename: "tsconfig.json", weight: 90 },
1676
+ { kind: "manifest", filename: "package.json", weight: 55 },
1677
+ { kind: "lockfile", filename: "pnpm-lock.yaml", weight: 30 },
1678
+ { kind: "lockfile", filename: "yarn.lock", weight: 30 },
1679
+ { kind: "lockfile", filename: "package-lock.json", weight: 30 },
1680
+ { kind: "lockfile", filename: "bun.lock", weight: 30 },
1681
+ { kind: "lockfile", filename: "bun.lockb", weight: 30 }
1682
+ ]),
1683
+ ignoredDirectories: COMMON_IGNORES,
1684
+ packageManagers: Object.freeze(["pnpm", "yarn", "bun", "npm"]),
1685
+ executables: Object.freeze(["pnpm", "yarn", "bun", "npx", "npm"]),
1686
+ operations: Object.freeze({
1687
+ syntax: async (ctx) => internalPlan(
1688
+ ctx,
1689
+ "syntax",
1690
+ "typescript-parser",
1691
+ "Parse the target with the TypeScript compiler API."
1692
+ ),
1693
+ semantic: async (ctx) => {
1694
+ const run = nodeExec(ctx, "tsc", ["--noEmit", "--pretty", "false"]);
1695
+ return processPlan(ctx, "semantic", run.command, run.args, {
1696
+ parser: "typescript",
1697
+ reason: "Run the workspace TypeScript compiler without emitting files.",
1698
+ executesProjectCode: true
1699
+ });
1700
+ },
1701
+ lint: async (ctx) => {
1702
+ const run = nodeExec(ctx, "biome", ["lint", "."]);
1703
+ return processPlan(ctx, "lint", run.command, run.args, {
1704
+ parser: "biome",
1705
+ reason: "Run the project-local Biome linter.",
1706
+ executesProjectCode: true
1707
+ });
1708
+ },
1709
+ "format-check": async (ctx) => {
1710
+ const run = nodeExec(ctx, "biome", ["format", "--check", "."]);
1711
+ return processPlan(ctx, "format-check", run.command, run.args, {
1712
+ parser: "biome",
1713
+ reason: "Check formatting with the project-local Biome formatter.",
1714
+ executesProjectCode: true
1715
+ });
1716
+ },
1717
+ "format-write": async (ctx) => {
1718
+ const run = nodeExec(ctx, "biome", ["format", "--write", "."]);
1719
+ return processPlan(ctx, "format-write", run.command, run.args, {
1720
+ parser: "biome",
1721
+ reason: "Format the workspace with the project-local Biome formatter.",
1722
+ mutating: true,
1723
+ executesProjectCode: true
1724
+ });
1725
+ },
1726
+ test: async (ctx) => ctx.options.filter || ctx.options.coverage ? unavailable(
1727
+ ctx,
1728
+ "test",
1729
+ "The detected package script does not expose deterministic filter or coverage adapters."
1730
+ ) : scriptPlan(ctx, "test", "test"),
1731
+ build: async (ctx) => scriptPlan(ctx, "build", "build"),
1732
+ run: async (ctx) => scriptPlan(ctx, "run", "dev"),
1733
+ "debug-compile": async (ctx) => {
1734
+ const run = nodeExec(ctx, "tsc", ["--noEmit", "--pretty", "false"]);
1735
+ return processPlan(ctx, "debug-compile", run.command, run.args, {
1736
+ parser: "typescript",
1737
+ reason: "Collect deterministic TypeScript compiler diagnostics.",
1738
+ executesProjectCode: true
1739
+ });
1740
+ },
1741
+ "package-install": async (ctx) => {
1742
+ const manager = ctx.workspace.packageManager ?? "npm";
1743
+ const args = manager === "yarn" ? ["install", "--ignore-scripts"] : ["install", "--ignore-scripts"];
1744
+ return processPlan(ctx, "package-install", manager, args, {
1745
+ parser: "package-text",
1746
+ reason: `Restore declared dependencies with ${manager} and lifecycle scripts disabled.`,
1747
+ mutating: true,
1748
+ network: true,
1749
+ executesProjectCode: false
1750
+ });
1751
+ },
1752
+ "package-add": async (ctx) => {
1753
+ const names = packageNames(ctx);
1754
+ if (names.length === 0)
1755
+ return unavailable(ctx, "package-add", "At least one package name is required.");
1756
+ const manager = ctx.workspace.packageManager ?? "npm";
1757
+ const args = manager === "npm" ? ["install", "--ignore-scripts", ...names] : ["add", "--ignore-scripts", ...names];
1758
+ return processPlan(ctx, "package-add", manager, args, {
1759
+ parser: "package-text",
1760
+ reason: `Add validated packages with ${manager} and lifecycle scripts disabled.`,
1761
+ mutating: true,
1762
+ network: true
1763
+ });
1764
+ },
1765
+ "package-audit": async (ctx) => {
1766
+ const manager = ctx.workspace.packageManager ?? "npm";
1767
+ return processPlan(ctx, "package-audit", manager, ["audit", "--json"], {
1768
+ parser: "npm-audit",
1769
+ reason: `Audit dependencies with the detected ${manager} package manager.`,
1770
+ network: true
1771
+ });
1772
+ },
1773
+ "package-outdated": async (ctx) => {
1774
+ const manager = ctx.workspace.packageManager ?? "npm";
1775
+ return processPlan(ctx, "package-outdated", manager, ["outdated", "--json"], {
1776
+ parser: "npm-outdated",
1777
+ reason: `Check outdated dependencies with ${manager}.`,
1778
+ network: true
1779
+ });
1780
+ }
1781
+ })
1782
+ };
1783
+ }
1784
+ function javascriptProfile() {
1785
+ const ts = typescriptProfile();
1786
+ return {
1787
+ ...ts,
1788
+ id: "javascript",
1789
+ displayName: "JavaScript",
1790
+ extensions: Object.freeze([".js", ".jsx", ".mjs", ".cjs"]),
1791
+ lspLanguageIds: Object.freeze(["javascript", "javascriptreact"]),
1792
+ detectors: Object.freeze([
1793
+ { kind: "config", filename: "jsconfig.json", weight: 90 },
1794
+ { kind: "manifest", filename: "package.json", weight: 70 },
1795
+ { kind: "lockfile", filename: "pnpm-lock.yaml", weight: 30 },
1796
+ { kind: "lockfile", filename: "yarn.lock", weight: 30 },
1797
+ { kind: "lockfile", filename: "package-lock.json", weight: 30 },
1798
+ { kind: "lockfile", filename: "bun.lock", weight: 30 },
1799
+ { kind: "lockfile", filename: "bun.lockb", weight: 30 }
1800
+ ]),
1801
+ operations: Object.freeze({
1802
+ ...ts.operations,
1803
+ syntax: async (ctx) => internalPlan(
1804
+ ctx,
1805
+ "syntax",
1806
+ "typescript-parser",
1807
+ "Parse JavaScript with the TypeScript compiler API."
1808
+ )
1809
+ })
1810
+ };
1811
+ }
1812
+ var goProfile = {
1813
+ id: "go",
1814
+ displayName: "Go",
1815
+ extensions: Object.freeze([".go"]),
1816
+ lspLanguageIds: Object.freeze(["go"]),
1817
+ detectors: Object.freeze([
1818
+ { kind: "manifest", filename: "go.mod", weight: 90 },
1819
+ { kind: "manifest", filename: "go.work", weight: 95 },
1820
+ { kind: "lockfile", filename: "go.sum", weight: 30 }
1821
+ ]),
1822
+ ignoredDirectories: COMMON_IGNORES,
1823
+ packageManagers: Object.freeze(["go"]),
1824
+ executables: Object.freeze(["go", "gofmt"]),
1825
+ operations: Object.freeze({
1826
+ syntax: async (ctx) => {
1827
+ if (!ctx.target)
1828
+ return unavailable(ctx, "syntax", "Go syntax planning requires a target file.");
1829
+ return processPlan(ctx, "syntax", "gofmt", ["-e", "-d", ctx.target], {
1830
+ parser: "gofmt",
1831
+ reason: "Parse the target and report syntax errors without writing it."
1832
+ });
1833
+ },
1834
+ semantic: async (ctx) => processPlan(ctx, "semantic", "go", ["test", "-run", "^$", "./..."], {
1835
+ parser: "go-test",
1836
+ reason: "Compile all Go packages without selecting tests.",
1837
+ mutating: true,
1838
+ executesProjectCode: true
1839
+ }),
1840
+ lint: async (ctx) => processPlan(ctx, "lint", "go", ["vet", "./..."], {
1841
+ parser: "go-compiler",
1842
+ reason: "Run the standard Go vet checks.",
1843
+ mutating: true,
1844
+ executesProjectCode: true
1845
+ }),
1846
+ "format-check": async (ctx) => ctx.target ? processPlan(ctx, "format-check", "gofmt", ["-d", ctx.target], {
1847
+ parser: "gofmt",
1848
+ reason: "Report Go formatting differences for the target without writing it."
1849
+ }) : unavailable(ctx, "format-check", "Go formatting requires an explicit target file."),
1850
+ "format-write": async (ctx) => ctx.target ? processPlan(ctx, "format-write", "gofmt", ["-w", ctx.target], {
1851
+ parser: "gofmt",
1852
+ reason: "Format the target Go source file.",
1853
+ mutating: true
1854
+ }) : unavailable(ctx, "format-write", "Go formatting requires an explicit target file."),
1855
+ test: async (ctx) => processPlan(
1856
+ ctx,
1857
+ "test",
1858
+ "go",
1859
+ ["test", ...ctx.options.filter ? ["-run", ctx.options.filter] : [], "./..."],
1860
+ {
1861
+ parser: "go-test",
1862
+ reason: ctx.options.filter ? "Run filtered Go tests." : "Run all Go tests.",
1863
+ mutating: true,
1864
+ executesProjectCode: true
1865
+ }
1866
+ ),
1867
+ build: async (ctx) => processPlan(ctx, "build", "go", ["build", "./..."], {
1868
+ parser: "go-compiler",
1869
+ reason: "Build all Go packages.",
1870
+ mutating: true,
1871
+ executesProjectCode: true
1872
+ }),
1873
+ run: async (ctx) => processPlan(ctx, "run", "go", ["run", "."], {
1874
+ parser: "command-text",
1875
+ reason: "Run the Go module entry point.",
1876
+ executesProjectCode: true
1877
+ }),
1878
+ "debug-race": async (ctx) => processPlan(ctx, "debug-race", "go", ["test", "-race", "./..."], {
1879
+ parser: "go-test",
1880
+ reason: "Collect Go race-detector evidence.",
1881
+ mutating: true,
1882
+ executesProjectCode: true
1883
+ }),
1884
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "go", ["mod", "download"], {
1885
+ parser: "go-module",
1886
+ reason: "Download the dependencies declared by go.mod.",
1887
+ mutating: true,
1888
+ network: true
1889
+ }),
1890
+ "package-add": async (ctx) => {
1891
+ const names = packageNames(ctx);
1892
+ if (names.length === 0)
1893
+ return unavailable(ctx, "package-add", "At least one Go module is required.");
1894
+ return processPlan(ctx, "package-add", "go", ["get", ...names], {
1895
+ parser: "go-module",
1896
+ reason: "Add validated Go modules.",
1897
+ mutating: true,
1898
+ network: true
1899
+ });
1900
+ },
1901
+ "package-update": async (ctx) => processPlan(ctx, "package-update", "go", ["get", "-u", "./..."], {
1902
+ parser: "go-module",
1903
+ reason: "Update dependencies of all Go packages.",
1904
+ mutating: true,
1905
+ network: true
1906
+ })
1907
+ })
1908
+ };
1909
+ var rustProfile = {
1910
+ id: "rust",
1911
+ displayName: "Rust",
1912
+ extensions: Object.freeze([".rs"]),
1913
+ lspLanguageIds: Object.freeze(["rust"]),
1914
+ detectors: Object.freeze([
1915
+ { kind: "manifest", filename: "Cargo.toml", weight: 90 },
1916
+ { kind: "lockfile", filename: "Cargo.lock", weight: 30 }
1917
+ ]),
1918
+ ignoredDirectories: COMMON_IGNORES,
1919
+ packageManagers: Object.freeze(["cargo"]),
1920
+ executables: Object.freeze(["cargo"]),
1921
+ operations: Object.freeze({
1922
+ syntax: async (ctx) => processPlan(ctx, "syntax", "cargo", ["check", "--message-format=json"], {
1923
+ parser: "cargo-json",
1924
+ reason: "Check Rust syntax and semantics with Cargo.",
1925
+ mutating: true,
1926
+ executesProjectCode: true
1927
+ }),
1928
+ semantic: async (ctx) => processPlan(ctx, "semantic", "cargo", ["check", "--message-format=json"], {
1929
+ parser: "cargo-json",
1930
+ reason: "Collect Rust compiler diagnostics with Cargo check.",
1931
+ mutating: true,
1932
+ executesProjectCode: true
1933
+ }),
1934
+ lint: async (ctx) => processPlan(ctx, "lint", "cargo", ["clippy", "--message-format=json"], {
1935
+ parser: "cargo-json",
1936
+ reason: "Run Clippy for this crate.",
1937
+ mutating: true,
1938
+ executesProjectCode: true
1939
+ }),
1940
+ "format-check": async (ctx) => processPlan(ctx, "format-check", "cargo", ["fmt", "--check"], {
1941
+ parser: "cargo-fmt",
1942
+ reason: "Check Rust formatting without writing files."
1943
+ }),
1944
+ "format-write": async (ctx) => processPlan(ctx, "format-write", "cargo", ["fmt"], {
1945
+ parser: "cargo-fmt",
1946
+ reason: "Format Rust source files in the workspace.",
1947
+ mutating: true
1948
+ }),
1949
+ "test-compile": async (ctx) => processPlan(ctx, "test-compile", "cargo", ["test", "--no-run", "--message-format=json"], {
1950
+ parser: "cargo-json",
1951
+ reason: "Compile Rust tests without running them.",
1952
+ mutating: true,
1953
+ executesProjectCode: true
1954
+ }),
1955
+ test: async (ctx) => processPlan(
1956
+ ctx,
1957
+ "test",
1958
+ "cargo",
1959
+ ["test", ...ctx.options.filter ? [ctx.options.filter] : []],
1960
+ {
1961
+ parser: "cargo-test",
1962
+ reason: ctx.options.filter ? "Run filtered Rust tests." : "Run Rust tests.",
1963
+ mutating: true,
1964
+ executesProjectCode: true
1965
+ }
1966
+ ),
1967
+ build: async (ctx) => processPlan(ctx, "build", "cargo", ["build", "--message-format=json"], {
1968
+ parser: "cargo-json",
1969
+ reason: "Build the Rust workspace.",
1970
+ mutating: true,
1971
+ executesProjectCode: true
1972
+ }),
1973
+ run: async (ctx) => processPlan(ctx, "run", "cargo", ["run"], {
1974
+ parser: "command-text",
1975
+ reason: "Run the Rust workspace entry point.",
1976
+ executesProjectCode: true
1977
+ }),
1978
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "cargo", ["fetch", "--locked"], {
1979
+ parser: "cargo-json",
1980
+ reason: "Fetch locked Rust dependencies.",
1981
+ mutating: true,
1982
+ network: true
1983
+ }),
1984
+ "package-add": async (ctx) => {
1985
+ const names = packageNames(ctx);
1986
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one crate is required.") : processPlan(ctx, "package-add", "cargo", ["add", ...names], {
1987
+ parser: "cargo-text",
1988
+ reason: "Add validated Rust crates.",
1989
+ mutating: true,
1990
+ network: true
1991
+ });
1992
+ },
1993
+ "package-remove": async (ctx) => {
1994
+ const names = packageNames(ctx);
1995
+ return names.length === 0 ? unavailable(ctx, "package-remove", "At least one crate is required.") : processPlan(ctx, "package-remove", "cargo", ["remove", ...names], {
1996
+ parser: "cargo-text",
1997
+ reason: "Remove validated Rust crates.",
1998
+ mutating: true
1999
+ });
2000
+ },
2001
+ "package-update": async (ctx) => processPlan(ctx, "package-update", "cargo", ["update"], {
2002
+ parser: "cargo-text",
2003
+ reason: "Update the Cargo lockfile.",
2004
+ mutating: true,
2005
+ network: true
2006
+ }),
2007
+ "package-audit": async (ctx) => processPlan(ctx, "package-audit", "cargo", ["audit", "--json"], {
2008
+ parser: "cargo-audit",
2009
+ reason: "Audit Rust dependencies when cargo-audit is installed.",
2010
+ network: true
2011
+ })
2012
+ })
2013
+ };
2014
+ var phpProfile = {
2015
+ id: "php",
2016
+ displayName: "PHP",
2017
+ extensions: Object.freeze([".php"]),
2018
+ lspLanguageIds: Object.freeze(["php"]),
2019
+ detectors: Object.freeze([
2020
+ { kind: "manifest", filename: "composer.json", weight: 90 },
2021
+ { kind: "lockfile", filename: "composer.lock", weight: 30 },
2022
+ { kind: "config", filename: "phpunit.xml", weight: 25 },
2023
+ { kind: "config", filename: "phpunit.xml.dist", weight: 25 }
2024
+ ]),
2025
+ ignoredDirectories: COMMON_IGNORES,
2026
+ packageManagers: Object.freeze(["composer"]),
2027
+ executables: Object.freeze(["php", "composer"]),
2028
+ operations: Object.freeze({
2029
+ syntax: async (ctx) => ctx.target ? processPlan(ctx, "syntax", "php", ["-l", ctx.target], {
2030
+ parser: "php-lint",
2031
+ reason: "Lint the target PHP file without executing it."
2032
+ }) : unavailable(ctx, "syntax", "PHP syntax planning requires a target file."),
2033
+ semantic: async (ctx) => unavailable(
2034
+ ctx,
2035
+ "semantic",
2036
+ "No configured PHPStan or Psalm adapter was detected in Phase 1."
2037
+ ),
2038
+ test: async (ctx) => processPlan(
2039
+ ctx,
2040
+ "test",
2041
+ "php",
2042
+ ["vendor/bin/phpunit", ...ctx.options.filter ? ["--filter", ctx.options.filter] : []],
2043
+ {
2044
+ parser: "phpunit",
2045
+ reason: ctx.options.filter ? "Run filtered tests with the project-local PHPUnit runner." : "Run the project-local PHPUnit test runner.",
2046
+ executesProjectCode: true
2047
+ }
2048
+ ),
2049
+ "package-install": async (ctx) => processPlan(
2050
+ ctx,
2051
+ "package-install",
2052
+ "composer",
2053
+ ["install", "--no-interaction", "--no-scripts"],
2054
+ {
2055
+ parser: "composer",
2056
+ reason: "Restore Composer dependencies without scripts.",
2057
+ mutating: true,
2058
+ network: true
2059
+ }
2060
+ ),
2061
+ "package-add": async (ctx) => {
2062
+ const names = packageNames(ctx);
2063
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one Composer package is required.") : processPlan(
2064
+ ctx,
2065
+ "package-add",
2066
+ "composer",
2067
+ ["require", "--no-interaction", "--no-scripts", ...names],
2068
+ {
2069
+ parser: "composer",
2070
+ reason: "Add validated Composer packages without scripts.",
2071
+ mutating: true,
2072
+ network: true
2073
+ }
2074
+ );
2075
+ },
2076
+ "package-remove": async (ctx) => {
2077
+ const names = packageNames(ctx);
2078
+ return names.length === 0 ? unavailable(ctx, "package-remove", "At least one Composer package is required.") : processPlan(
2079
+ ctx,
2080
+ "package-remove",
2081
+ "composer",
2082
+ ["remove", "--no-interaction", "--no-scripts", ...names],
2083
+ {
2084
+ parser: "composer",
2085
+ reason: "Remove validated Composer packages without scripts.",
2086
+ mutating: true
2087
+ }
2088
+ );
2089
+ },
2090
+ "package-update": async (ctx) => processPlan(
2091
+ ctx,
2092
+ "package-update",
2093
+ "composer",
2094
+ ["update", "--no-interaction", "--no-scripts"],
2095
+ {
2096
+ parser: "composer",
2097
+ reason: "Update Composer dependencies without scripts.",
2098
+ mutating: true,
2099
+ network: true
2100
+ }
2101
+ ),
2102
+ "package-audit": async (ctx) => processPlan(ctx, "package-audit", "composer", ["audit", "--format=json"], {
2103
+ parser: "composer-audit",
2104
+ reason: "Audit Composer dependencies.",
2105
+ network: true
2106
+ }),
2107
+ "package-outdated": async (ctx) => processPlan(ctx, "package-outdated", "composer", ["outdated", "--format=json"], {
2108
+ parser: "composer-outdated",
2109
+ reason: "Check outdated Composer dependencies.",
2110
+ network: true
2111
+ })
2112
+ })
2113
+ };
2114
+ var csharpProfile = {
2115
+ id: "csharp",
2116
+ displayName: "C# / .NET",
2117
+ extensions: Object.freeze([".cs"]),
2118
+ lspLanguageIds: Object.freeze(["csharp"]),
2119
+ detectors: Object.freeze([
2120
+ { kind: "config", filename: "global.json", weight: 25 },
2121
+ { kind: "manifest", suffix: ".slnx", weight: 95 },
2122
+ { kind: "manifest", suffix: ".sln", weight: 95 },
2123
+ { kind: "manifest", suffix: ".csproj", weight: 90 },
2124
+ { kind: "manifest", suffix: ".fsproj", weight: 90 },
2125
+ { kind: "lockfile", filename: "packages.lock.json", weight: 30 }
2126
+ ]),
2127
+ ignoredDirectories: COMMON_IGNORES,
2128
+ packageManagers: Object.freeze(["dotnet"]),
2129
+ executables: Object.freeze(["dotnet"]),
2130
+ operations: Object.freeze({
2131
+ syntax: async (ctx) => processPlan(ctx, "syntax", "dotnet", ["build", "--no-restore"], {
2132
+ parser: "dotnet-build",
2133
+ reason: "Use the nearest project or solution to collect C# syntax diagnostics.",
2134
+ mutating: true,
2135
+ executesProjectCode: true
2136
+ }),
2137
+ semantic: async (ctx) => processPlan(ctx, "semantic", "dotnet", ["build", "--no-restore"], {
2138
+ parser: "dotnet-build",
2139
+ reason: "Build without restoring to collect .NET compiler diagnostics.",
2140
+ mutating: true,
2141
+ executesProjectCode: true
2142
+ }),
2143
+ lint: async (ctx) => processPlan(ctx, "lint", "dotnet", ["format", "--verify-no-changes", "--no-restore"], {
2144
+ parser: "dotnet-format",
2145
+ reason: "Verify .NET formatting and analyzers without writing source files.",
2146
+ mutating: true,
2147
+ executesProjectCode: true
2148
+ }),
2149
+ "format-check": async (ctx) => processPlan(
2150
+ ctx,
2151
+ "format-check",
2152
+ "dotnet",
2153
+ ["format", "--verify-no-changes", "--no-restore"],
2154
+ {
2155
+ parser: "dotnet-format",
2156
+ reason: "Verify .NET formatting without source writes.",
2157
+ mutating: true,
2158
+ executesProjectCode: true
2159
+ }
2160
+ ),
2161
+ "format-write": async (ctx) => processPlan(ctx, "format-write", "dotnet", ["format", "--no-restore"], {
2162
+ parser: "dotnet-format",
2163
+ reason: "Format .NET source files without restoring packages.",
2164
+ mutating: true,
2165
+ executesProjectCode: true
2166
+ }),
2167
+ test: async (ctx) => processPlan(
2168
+ ctx,
2169
+ "test",
2170
+ "dotnet",
2171
+ ["test", "--no-restore", ...ctx.options.filter ? ["--filter", ctx.options.filter] : []],
2172
+ {
2173
+ parser: "dotnet-test",
2174
+ reason: ctx.options.filter ? "Run filtered .NET tests without restoring packages." : "Run .NET tests without restoring packages.",
2175
+ mutating: true,
2176
+ executesProjectCode: true
2177
+ }
2178
+ ),
2179
+ build: async (ctx) => processPlan(ctx, "build", "dotnet", ["build", "--no-restore"], {
2180
+ parser: "dotnet-build",
2181
+ reason: "Build the nearest .NET project or solution without restoring packages.",
2182
+ mutating: true,
2183
+ executesProjectCode: true
2184
+ }),
2185
+ run: async (ctx) => processPlan(ctx, "run", "dotnet", ["run", "--no-restore"], {
2186
+ parser: "command-text",
2187
+ reason: "Run the .NET project entry point.",
2188
+ mutating: true,
2189
+ executesProjectCode: true
2190
+ }),
2191
+ "package-install": async (ctx) => processPlan(ctx, "package-install", "dotnet", ["restore", "--locked-mode"], {
2192
+ parser: "dotnet-restore",
2193
+ reason: "Restore locked .NET dependencies.",
2194
+ mutating: true,
2195
+ network: true,
2196
+ executesProjectCode: true
2197
+ }),
2198
+ "package-add": async (ctx) => {
2199
+ const names = packageNames(ctx);
2200
+ return names.length === 0 ? unavailable(ctx, "package-add", "At least one NuGet package is required.") : processPlan(ctx, "package-add", "dotnet", ["add", "package", ...names], {
2201
+ parser: "dotnet-package",
2202
+ reason: "Add validated NuGet packages.",
2203
+ mutating: true,
2204
+ network: true,
2205
+ executesProjectCode: true
2206
+ });
2207
+ },
2208
+ "package-remove": async (ctx) => {
2209
+ const names = packageNames(ctx);
2210
+ return names.length === 0 ? unavailable(ctx, "package-remove", "At least one NuGet package is required.") : processPlan(ctx, "package-remove", "dotnet", ["remove", "package", ...names], {
2211
+ parser: "dotnet-package",
2212
+ reason: "Remove validated NuGet packages.",
2213
+ mutating: true
2214
+ });
2215
+ },
2216
+ "package-audit": async (ctx) => processPlan(
2217
+ ctx,
2218
+ "package-audit",
2219
+ "dotnet",
2220
+ ["list", "package", "--vulnerable", "--format", "json"],
2221
+ { parser: "dotnet-package", reason: "List vulnerable NuGet dependencies.", network: true }
2222
+ ),
2223
+ "package-outdated": async (ctx) => processPlan(
2224
+ ctx,
2225
+ "package-outdated",
2226
+ "dotnet",
2227
+ ["list", "package", "--outdated", "--format", "json"],
2228
+ { parser: "dotnet-package", reason: "List outdated NuGet dependencies.", network: true }
2229
+ )
2230
+ })
2231
+ };
2232
+ var PRIMARY_LANGUAGE_PROFILES = Object.freeze([
2233
+ Object.freeze(typescriptProfile()),
2234
+ Object.freeze(javascriptProfile()),
2235
+ Object.freeze(goProfile),
2236
+ Object.freeze(rustProfile),
2237
+ Object.freeze(phpProfile),
2238
+ Object.freeze(csharpProfile)
2239
+ ]);
2240
+
2241
+ // src/languages/registry.ts
2242
+ var PROFILE_ID_RE = /^[a-z][a-z0-9-]{0,63}$/;
2243
+ var EXECUTABLE_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
2244
+ var KNOWN_OPERATIONS = /* @__PURE__ */ new Set([
2245
+ "syntax",
2246
+ "semantic",
2247
+ "lint",
2248
+ "format-check",
2249
+ "format-write",
2250
+ "test-compile",
2251
+ "test",
2252
+ "build",
2253
+ "run",
2254
+ "debug-compile",
2255
+ "debug-test",
2256
+ "debug-runtime",
2257
+ "debug-race",
2258
+ "package-install",
2259
+ "package-add",
2260
+ "package-remove",
2261
+ "package-update",
2262
+ "package-audit",
2263
+ "package-outdated"
2264
+ ]);
2265
+ var LanguageProfileRegistry = class {
2266
+ #profiles = /* @__PURE__ */ new Map();
2267
+ constructor(profiles = []) {
2268
+ for (const profile of profiles) this.register(profile);
2269
+ }
2270
+ register(profile) {
2271
+ const errors = validateLanguageProfile(profile);
2272
+ if (errors.length > 0) {
2273
+ throw new Error(`Invalid language profile "${profile.id}": ${errors.join("; ")}`);
2274
+ }
2275
+ if (this.#profiles.has(profile.id)) {
2276
+ throw new Error(`Language profile "${profile.id}" is already registered`);
2277
+ }
2278
+ this.#profiles.set(profile.id, freezeProfile(profile));
2279
+ }
2280
+ get(id) {
2281
+ return this.#profiles.get(id);
2282
+ }
2283
+ list() {
2284
+ return Object.freeze([...this.#profiles.values()]);
2285
+ }
2286
+ };
2287
+ function validateLanguageProfile(profile) {
2288
+ const errors = [];
2289
+ if (!PROFILE_ID_RE.test(profile.id)) errors.push("id must be a lowercase stable identifier");
2290
+ if (!profile.displayName.trim()) errors.push("displayName is required");
2291
+ if (profile.extensions.length === 0) errors.push("at least one extension is required");
2292
+ for (const ext of profile.extensions) {
2293
+ if (!/^\.[a-z0-9+.-]+$/i.test(ext)) errors.push(`invalid extension "${ext}"`);
2294
+ }
2295
+ if (profile.detectors.length === 0) errors.push("at least one detector is required");
2296
+ for (const detector of profile.detectors) {
2297
+ if ((detector.filename ? 1 : 0) + (detector.suffix ? 1 : 0) !== 1) {
2298
+ errors.push("each detector must declare exactly one of filename or suffix");
2299
+ }
2300
+ if (!Number.isFinite(detector.weight) || detector.weight <= 0 || detector.weight > 100) {
2301
+ errors.push("detector weights must be in the range 1..100");
2302
+ }
2303
+ const marker = detector.filename ?? detector.suffix ?? "";
2304
+ if (marker.includes("/") || marker.includes("\\") || marker.includes("\0")) {
2305
+ errors.push(`detector marker "${marker}" must be a basename or suffix`);
2306
+ }
2307
+ }
2308
+ if (profile.executables.length === 0) errors.push("at least one executable is required");
2309
+ for (const executable of profile.executables) {
2310
+ if (!EXECUTABLE_RE.test(executable)) errors.push(`invalid executable token "${executable}"`);
2311
+ }
2312
+ for (const operation of Object.keys(profile.operations)) {
2313
+ if (!KNOWN_OPERATIONS.has(operation)) {
2314
+ errors.push(`unknown operation "${operation}"`);
2315
+ }
2316
+ if (typeof profile.operations[operation] !== "function") {
2317
+ errors.push(`operation "${operation}" must be a resolver function`);
2318
+ }
2319
+ }
2320
+ return [...new Set(errors)];
2321
+ }
2322
+ function freezeProfile(profile) {
2323
+ const detectors = Object.freeze(profile.detectors.map((item) => Object.freeze({ ...item })));
2324
+ const operations = Object.freeze({ ...profile.operations });
2325
+ return Object.freeze({
2326
+ ...profile,
2327
+ extensions: Object.freeze([...profile.extensions]),
2328
+ lspLanguageIds: Object.freeze([...profile.lspLanguageIds]),
2329
+ detectors,
2330
+ ignoredDirectories: Object.freeze([...profile.ignoredDirectories]),
2331
+ packageManagers: Object.freeze([...profile.packageManagers]),
2332
+ executables: Object.freeze([...profile.executables]),
2333
+ operations
2334
+ });
2335
+ }
2336
+ var languageProfileRegistry = new LanguageProfileRegistry([
2337
+ ...PRIMARY_LANGUAGE_PROFILES,
2338
+ ...ADDITIONAL_LANGUAGE_PROFILES
2339
+ ]);
2340
+
2341
+ // src/languages/detect.ts
2342
+ var DEFAULT_LIMITS = { maxDepth: 6, maxEntries: 5e3 };
2343
+ var SOURCE_WEIGHT = 5;
2344
+ var SOURCE_WEIGHT_CAP = 25;
2345
+ var TARGET_WEIGHT = 100;
2346
+ var GLOBAL_IGNORES = /* @__PURE__ */ new Set([
2347
+ ".git",
2348
+ ".wrongstack",
2349
+ "node_modules",
2350
+ "vendor",
2351
+ "target",
2352
+ "bin",
2353
+ "obj",
2354
+ "dist",
2355
+ "build",
2356
+ "out",
2357
+ "coverage",
2358
+ ".cache",
2359
+ ".idea",
2360
+ ".vscode",
2361
+ ".next",
2362
+ ".nuxt"
2363
+ ]);
2364
+ async function detectLanguageWorkspaces(options) {
2365
+ const projectRoot = await canonicalDirectory(options.projectRoot);
2366
+ const cwdInput = options.cwd ? path4.isAbsolute(options.cwd) ? options.cwd : path4.resolve(projectRoot, options.cwd) : projectRoot;
2367
+ const cwd = await canonicalInside(cwdInput, projectRoot, "cwd");
2368
+ const target = options.target ? await canonicalInside(resolveFrom(cwd, options.target), projectRoot, "target") : void 0;
2369
+ const profiles = (options.profiles ?? languageProfileRegistry.list()).filter((profile) => !options.language || profile.id === options.language).slice().sort((a, b) => a.id.localeCompare(b.id));
2370
+ const limits = normalizeLimits(options.limits);
2371
+ const state = {
2372
+ entries: 0,
2373
+ truncated: false,
2374
+ sourcePaths: /* @__PURE__ */ new Map(),
2375
+ candidates: /* @__PURE__ */ new Map()
2376
+ };
2377
+ await scanDirectory(projectRoot, 0, profiles, limits, state, options.signal);
2378
+ addSourceFallbacks(projectRoot, profiles, state);
2379
+ if (target) addTargetEvidence(target, projectRoot, profiles, state);
2380
+ const workspaces = await Promise.all(
2381
+ [...state.candidates.values()].map((candidate) => finalizeCandidate(candidate, projectRoot))
2382
+ );
2383
+ workspaces.sort(compareWorkspaces);
2384
+ return {
2385
+ projectRoot,
2386
+ scannedEntries: state.entries,
2387
+ truncated: state.truncated,
2388
+ workspaces
2389
+ };
2390
+ }
2391
+ async function scanDirectory(directory, depth, profiles, limits, state, signal) {
2392
+ signal?.throwIfAborted();
2393
+ if (depth > limits.maxDepth || state.entries >= limits.maxEntries) {
2394
+ state.truncated = true;
2395
+ return;
2396
+ }
2397
+ let entries;
2398
+ try {
2399
+ entries = await fs2.readdir(directory, { withFileTypes: true });
2400
+ } catch {
2401
+ return;
2402
+ }
2403
+ entries.sort((a, b) => a.name.localeCompare(b.name));
2404
+ for (const entry of entries) {
2405
+ signal?.throwIfAborted();
2406
+ if (state.entries >= limits.maxEntries) {
2407
+ state.truncated = true;
2408
+ return;
2409
+ }
2410
+ state.entries++;
2411
+ const fullPath = path4.join(directory, entry.name);
2412
+ if (entry.isSymbolicLink()) continue;
2413
+ if (entry.isDirectory()) {
2414
+ if (shouldIgnoreDirectory(entry.name, profiles)) continue;
2415
+ if (depth >= limits.maxDepth) {
2416
+ state.truncated = true;
2417
+ continue;
2418
+ }
2419
+ await scanDirectory(fullPath, depth + 1, profiles, limits, state, signal);
2420
+ continue;
2421
+ }
2422
+ if (!entry.isFile()) continue;
2423
+ collectFileEvidence(directory, fullPath, entry.name, profiles, state);
2424
+ }
2425
+ }
2426
+ function collectFileEvidence(directory, fullPath, basename4, profiles, state) {
2427
+ const lower = basename4.toLowerCase();
2428
+ const extension = path4.extname(lower);
2429
+ for (const profile of profiles) {
2430
+ const detector = profile.detectors.find(
2431
+ (rule) => rule.filename ? lower === rule.filename.toLowerCase() : lower.endsWith(rule.suffix.toLowerCase())
2432
+ );
2433
+ if (detector) {
2434
+ const candidate = getCandidate(state, profile, directory);
2435
+ candidate.evidence.push({
2436
+ kind: detector.kind,
2437
+ path: fullPath,
2438
+ value: basename4,
2439
+ weight: detector.weight
2440
+ });
2441
+ if (detector.kind === "manifest" || detector.kind === "config") {
2442
+ candidate.manifests.push(fullPath);
2443
+ }
2444
+ }
2445
+ if (profile.extensions.includes(extension)) {
2446
+ const paths = state.sourcePaths.get(profile.id) ?? [];
2447
+ if (paths.length < SOURCE_WEIGHT_CAP / SOURCE_WEIGHT) paths.push(fullPath);
2448
+ state.sourcePaths.set(profile.id, paths);
2449
+ }
2450
+ }
2451
+ }
2452
+ function addSourceFallbacks(projectRoot, profiles, state) {
2453
+ for (const profile of profiles) {
2454
+ const sources = state.sourcePaths.get(profile.id) ?? [];
2455
+ if (sources.length === 0) continue;
2456
+ const profileCandidates = [...state.candidates.values()].filter(
2457
+ (item) => item.profile.id === profile.id
2458
+ );
2459
+ for (const source of sources) {
2460
+ const containing = profileCandidates.filter((candidate2) => isInside(source, candidate2.root)).sort(
2461
+ (a, b) => pathDepth(b.root, projectRoot) - pathDepth(a.root, projectRoot) || a.root.localeCompare(b.root)
2462
+ );
2463
+ const candidate = containing[0] ?? (profile.sourceFallback === false ? void 0 : getCandidate(state, profile, projectRoot));
2464
+ if (!candidate) continue;
2465
+ candidate.evidence.push({
2466
+ kind: "source",
2467
+ path: source,
2468
+ value: path4.extname(source),
2469
+ weight: SOURCE_WEIGHT
2470
+ });
2471
+ }
2472
+ }
2473
+ }
2474
+ function addTargetEvidence(target, projectRoot, profiles, state) {
2475
+ const extension = path4.extname(target).toLowerCase();
2476
+ for (const profile of profiles) {
2477
+ if (!profile.extensions.includes(extension)) continue;
2478
+ const candidates = [...state.candidates.values()].filter(
2479
+ (candidate2) => candidate2.profile.id === profile.id && isInside(target, candidate2.root)
2480
+ );
2481
+ const candidate = candidates.length > 0 ? candidates.sort(
2482
+ (a, b) => pathDepth(b.root, projectRoot) - pathDepth(a.root, projectRoot) || a.root.localeCompare(b.root)
2483
+ )[0] : profile.sourceFallback === false ? void 0 : getCandidate(state, profile, path4.dirname(target));
2484
+ if (!candidate) continue;
2485
+ candidate.evidence.push({
2486
+ kind: "target",
2487
+ path: target,
2488
+ value: extension,
2489
+ weight: TARGET_WEIGHT
2490
+ });
2491
+ }
2492
+ }
2493
+ async function finalizeCandidate(candidate, projectRoot) {
2494
+ const evidence = dedupeEvidence(candidate.evidence).sort(compareEvidence);
2495
+ const manifests = [...new Set(candidate.manifests)].sort();
2496
+ const confidence = Math.min(1, evidence.reduce((sum, item) => sum + item.weight, 0) / 100);
2497
+ const packageManager = await detectPackageManager(candidate.profile, candidate.root, evidence);
2498
+ const id = createHash("sha256").update(`${candidate.profile.id}\0${path4.relative(projectRoot, candidate.root)}`).digest("hex").slice(0, 16);
2499
+ return Object.freeze({
2500
+ id,
2501
+ language: candidate.profile.id,
2502
+ root: candidate.root,
2503
+ confidence,
2504
+ evidence: Object.freeze(evidence.map((item) => Object.freeze(item))),
2505
+ ...packageManager ? { packageManager } : {},
2506
+ manifests: Object.freeze(manifests),
2507
+ capabilities: Object.freeze(
2508
+ Object.keys(candidate.profile.operations).sort()
2509
+ )
2510
+ });
2511
+ }
2512
+ async function detectPackageManager(profile, root, evidence) {
2513
+ if (profile.packageManagers.length === 1) return profile.packageManagers[0];
2514
+ if (profile.id !== "typescript" && profile.id !== "javascript") return void 0;
2515
+ let declared;
2516
+ try {
2517
+ const pkg = JSON.parse(await fs2.readFile(path4.join(root, "package.json"), "utf8"));
2518
+ if (typeof pkg.packageManager === "string") {
2519
+ const manager = pkg.packageManager.split("@")[0];
2520
+ if (manager && profile.packageManagers.includes(manager)) declared = manager;
2521
+ }
2522
+ } catch {
2523
+ }
2524
+ const lockManagers = /* @__PURE__ */ new Set();
2525
+ for (const item of evidence) {
2526
+ const name = path4.basename(item.path).toLowerCase();
2527
+ if (name === "pnpm-lock.yaml") lockManagers.add("pnpm");
2528
+ else if (name === "yarn.lock") lockManagers.add("yarn");
2529
+ else if (name === "bun.lock" || name === "bun.lockb") lockManagers.add("bun");
2530
+ else if (name === "package-lock.json") lockManagers.add("npm");
2531
+ }
2532
+ if (declared && (lockManagers.size === 0 || lockManagers.size === 1 && lockManagers.has(declared))) {
2533
+ return declared;
2534
+ }
2535
+ if (lockManagers.size === 1) return [...lockManagers][0];
2536
+ if (lockManagers.size > 1) return void 0;
2537
+ return declared ?? "npm";
2538
+ }
2539
+ function getCandidate(state, profile, root) {
2540
+ const key = `${profile.id}\0${root}`;
2541
+ let candidate = state.candidates.get(key);
2542
+ if (!candidate) {
2543
+ candidate = { profile, root, evidence: [], manifests: [] };
2544
+ state.candidates.set(key, candidate);
2545
+ }
2546
+ return candidate;
2547
+ }
2548
+ function shouldIgnoreDirectory(name, profiles) {
2549
+ if (GLOBAL_IGNORES.has(name) || name.startsWith(".")) return true;
2550
+ return profiles.some((profile) => profile.ignoredDirectories.includes(name));
2551
+ }
2552
+ function normalizeLimits(input) {
2553
+ const maxDepth = Math.max(
2554
+ 0,
2555
+ Math.min(12, Math.trunc(input?.maxDepth ?? DEFAULT_LIMITS.maxDepth))
2556
+ );
2557
+ const maxEntries = Math.max(
2558
+ 1,
2559
+ Math.min(5e4, Math.trunc(input?.maxEntries ?? DEFAULT_LIMITS.maxEntries))
2560
+ );
2561
+ return { maxDepth, maxEntries };
2562
+ }
2563
+ async function canonicalDirectory(input) {
2564
+ const resolved = path4.resolve(input);
2565
+ const real = await fs2.realpath(resolved);
2566
+ const stat5 = await fs2.stat(real);
2567
+ if (!stat5.isDirectory()) throw new Error(`Project root is not a directory: ${input}`);
2568
+ return real;
2569
+ }
2570
+ async function canonicalInside(input, root, label) {
2571
+ const resolved = path4.resolve(input);
2572
+ let real;
2573
+ try {
2574
+ real = await fs2.realpath(resolved);
2575
+ } catch (error) {
2576
+ if (error.code !== "ENOENT") throw error;
2577
+ const parent = await fs2.realpath(path4.dirname(resolved));
2578
+ real = path4.join(parent, path4.basename(resolved));
2579
+ }
2580
+ if (!isInside(real, root)) throw new Error(`${label} is outside project root: ${input}`);
2581
+ return real;
2582
+ }
2583
+ function resolveFrom(cwd, input) {
2584
+ return path4.isAbsolute(input) ? input : path4.resolve(cwd, input);
2585
+ }
2586
+ function isInside(candidate, root) {
2587
+ const relative5 = path4.relative(root, candidate);
2588
+ return relative5 === "" || !relative5.startsWith("..") && !path4.isAbsolute(relative5);
2589
+ }
2590
+ function pathDepth(candidate, root) {
2591
+ const relative5 = path4.relative(root, candidate);
2592
+ return relative5 === "" ? 0 : relative5.split(path4.sep).length;
2593
+ }
2594
+ function dedupeEvidence(items) {
2595
+ const seen = /* @__PURE__ */ new Set();
2596
+ return items.filter((item) => {
2597
+ const key = `${item.kind}\0${item.path}\0${item.value}\0${item.weight}`;
2598
+ if (seen.has(key)) return false;
2599
+ seen.add(key);
2600
+ return true;
2601
+ });
2602
+ }
2603
+ function compareEvidence(a, b) {
2604
+ return a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind) || b.weight - a.weight;
2605
+ }
2606
+ function compareWorkspaces(a, b) {
2607
+ return b.confidence - a.confidence || a.language.localeCompare(b.language) || a.root.localeCompare(b.root) || a.id.localeCompare(b.id);
2608
+ }
2609
+
2610
+ // src/languages/diagnostics.ts
2611
+ import * as path5 from "node:path";
2612
+ var MAX_DIAGNOSTICS = 200;
2613
+ function parseLanguageDiagnostics(parser, stdout, stderr, workspaceRoot) {
2614
+ const text = `${stdout}${stdout && stderr ? "\n" : ""}${stderr}`;
2615
+ let diagnostics;
2616
+ switch (parser) {
2617
+ case "typescript":
2618
+ diagnostics = parseTypeScript(text, workspaceRoot);
2619
+ break;
2620
+ case "cargo-json":
2621
+ diagnostics = parseCargoJson(text, workspaceRoot);
2622
+ break;
2623
+ case "php-lint":
2624
+ diagnostics = parsePhpLint(text, workspaceRoot);
2625
+ break;
2626
+ case "dotnet-build":
2627
+ case "dotnet-format":
2628
+ case "dotnet-test":
2629
+ diagnostics = parseDotnet(text, workspaceRoot);
2630
+ break;
2631
+ case "go-test":
2632
+ case "go-compiler":
2633
+ case "gofmt":
2634
+ diagnostics = parseGo(text, workspaceRoot);
2635
+ break;
2636
+ case "biome":
2637
+ diagnostics = parseBiome(text, workspaceRoot);
2638
+ break;
2639
+ default:
2640
+ diagnostics = parseGeneric(text, parser, workspaceRoot);
2641
+ break;
2642
+ }
2643
+ const sorted = dedupeDiagnostics(diagnostics).sort(compareDiagnostics);
2644
+ const omitted = Math.max(0, sorted.length - MAX_DIAGNOSTICS);
2645
+ const kept = Object.freeze(sorted.slice(0, MAX_DIAGNOSTICS).map((item) => Object.freeze(item)));
2646
+ return {
2647
+ diagnostics: kept,
2648
+ omitted,
2649
+ summary: summarize(kept)
2650
+ };
2651
+ }
2652
+ function diagnosticsForInternalSyntax(language, target, sourceText) {
2653
+ if (language !== "typescript" && language !== "javascript") {
2654
+ return Promise.resolve({ diagnostics: [], omitted: 0, summary: emptySummary() });
2655
+ }
2656
+ return import("@typescript/typescript6").then((tsModule) => {
2657
+ const ts = tsModule.default ?? tsModule;
2658
+ const extension = path5.extname(target).toLowerCase();
2659
+ const scriptKind = extension === ".tsx" ? ts.ScriptKind.TSX : extension === ".ts" || extension === ".mts" || extension === ".cts" ? ts.ScriptKind.TS : ts.ScriptKind.JSX;
2660
+ const sourceFile = ts.createSourceFile(
2661
+ path5.basename(target),
2662
+ sourceText,
2663
+ ts.ScriptTarget.Latest,
2664
+ false,
2665
+ scriptKind
2666
+ );
2667
+ const native = sourceFile.parseDiagnostics ?? [];
2668
+ const diagnostics = native.map((diagnostic) => {
2669
+ const start = diagnostic.start ?? 0;
2670
+ const location = sourceFile.getLineAndCharacterOfPosition(start);
2671
+ return {
2672
+ severity: diagnostic.category === ts.DiagnosticCategory.Warning ? "warning" : "error",
2673
+ ...diagnostic.code ? { code: `TS${diagnostic.code}` } : {},
2674
+ message: ts.flattenDiagnosticMessageText(diagnostic.messageText, " "),
2675
+ file: target,
2676
+ range: { start: { line: location.line + 1, column: location.character + 1 } },
2677
+ source: "typescript-parser"
2678
+ };
2679
+ });
2680
+ const sorted = dedupeDiagnostics(diagnostics).sort(compareDiagnostics);
2681
+ const omitted = Math.max(0, sorted.length - MAX_DIAGNOSTICS);
2682
+ const kept = Object.freeze(sorted.slice(0, MAX_DIAGNOSTICS).map((item) => Object.freeze(item)));
2683
+ return { diagnostics: kept, omitted, summary: summarize(kept) };
2684
+ });
2685
+ }
2686
+ function parseTypeScript(text, root) {
2687
+ const diagnostics = [];
2688
+ const regex = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s*(.+)$/gm;
2689
+ for (const match of text.matchAll(regex)) {
2690
+ diagnostics.push({
2691
+ severity: match[4] === "warning" ? "warning" : "error",
2692
+ code: match[5],
2693
+ message: match[6].trim(),
2694
+ file: normalizeDiagnosticPath(match[1], root),
2695
+ range: { start: { line: toPositiveInt(match[2]), column: toPositiveInt(match[3]) } },
2696
+ source: "typescript"
2697
+ });
2698
+ }
2699
+ return diagnostics;
2700
+ }
2701
+ function parseCargoJson(text, root) {
2702
+ const diagnostics = [];
2703
+ for (const line of text.split(/\r?\n/)) {
2704
+ if (!line.trim().startsWith("{")) continue;
2705
+ try {
2706
+ const value = JSON.parse(line);
2707
+ if (value.reason !== "compiler-message" || !value.message?.message) continue;
2708
+ const primary = value.message.spans?.find((span) => span.is_primary) ?? value.message.spans?.[0];
2709
+ diagnostics.push({
2710
+ severity: normalizeSeverity(value.message.level),
2711
+ ...value.message.code?.code ? { code: value.message.code.code } : {},
2712
+ message: value.message.message,
2713
+ ...primary?.file_name ? { file: normalizeDiagnosticPath(primary.file_name, root) } : {},
2714
+ ...primary?.line_start ? { range: { start: { line: primary.line_start, column: primary.column_start ?? 1 } } } : {},
2715
+ source: "rustc"
2716
+ });
2717
+ } catch {
2718
+ }
2719
+ }
2720
+ return diagnostics;
2721
+ }
2722
+ function parseGo(text, root) {
2723
+ return parseLinePattern(
2724
+ text,
2725
+ /^(.*?\.go):(\d+):(\d+):\s*(.+)$/gm,
2726
+ root,
2727
+ "go",
2728
+ (_match, message) => ({ message, severity: /warning/i.test(message) ? "warning" : "error" })
2729
+ );
2730
+ }
2731
+ function parsePhpLint(text, root) {
2732
+ const diagnostics = [];
2733
+ const regex = /(?:PHP\s+)?(?:Parse|Fatal) error:\s*(.+?)\s+in\s+(.+?)\s+on line\s+(\d+)/gi;
2734
+ for (const match of text.matchAll(regex)) {
2735
+ diagnostics.push({
2736
+ severity: "error",
2737
+ message: match[1].trim(),
2738
+ file: normalizeDiagnosticPath(match[2], root),
2739
+ range: { start: { line: toPositiveInt(match[3]), column: 1 } },
2740
+ source: "php"
2741
+ });
2742
+ }
2743
+ return diagnostics;
2744
+ }
2745
+ function parseDotnet(text, root) {
2746
+ const diagnostics = [];
2747
+ const regex = /^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s+([A-Z]+\d+):\s*(.+?)(?:\s+\[.+\])?$/gm;
2748
+ for (const match of text.matchAll(regex)) {
2749
+ diagnostics.push({
2750
+ severity: match[4] === "warning" ? "warning" : "error",
2751
+ code: match[5],
2752
+ message: match[6].trim(),
2753
+ file: normalizeDiagnosticPath(match[1], root),
2754
+ range: { start: { line: toPositiveInt(match[2]), column: toPositiveInt(match[3]) } },
2755
+ source: "dotnet"
2756
+ });
2757
+ }
2758
+ return diagnostics;
2759
+ }
2760
+ function parseBiome(text, root) {
2761
+ const diagnostics = [];
2762
+ const regex = /^(.+?):(\d+):(\d+)\s+(lint\/[^\s]+|format)\s+(.+)$/gm;
2763
+ for (const match of text.matchAll(regex)) {
2764
+ diagnostics.push({
2765
+ severity: "warning",
2766
+ code: match[4],
2767
+ message: match[5].trim(),
2768
+ file: normalizeDiagnosticPath(match[1], root),
2769
+ range: { start: { line: toPositiveInt(match[2]), column: toPositiveInt(match[3]) } },
2770
+ source: "biome"
2771
+ });
2772
+ }
2773
+ return diagnostics;
2774
+ }
2775
+ function parseGeneric(text, source, root) {
2776
+ return parseLinePattern(
2777
+ text,
2778
+ /^(.+?):(\d+):(\d+):\s*(?:(error|warning|info):\s*)?(.+)$/gm,
2779
+ root,
2780
+ source,
2781
+ (match, fallback) => ({
2782
+ severity: normalizeSeverity(match[4]),
2783
+ message: match[5]?.trim() || fallback
2784
+ })
2785
+ );
2786
+ }
2787
+ function parseLinePattern(text, regex, root, source, details) {
2788
+ const diagnostics = [];
2789
+ for (const match of text.matchAll(regex)) {
2790
+ const parsed = details(match, match.at(-1)?.trim() ?? "Diagnostic");
2791
+ diagnostics.push({
2792
+ severity: parsed.severity,
2793
+ message: parsed.message,
2794
+ file: normalizeDiagnosticPath(match[1], root),
2795
+ range: { start: { line: toPositiveInt(match[2]), column: toPositiveInt(match[3]) } },
2796
+ source
2797
+ });
2798
+ }
2799
+ return diagnostics;
2800
+ }
2801
+ function normalizeDiagnosticPath(value, root) {
2802
+ const clean = value.trim().replace(/^['"]|['"]$/g, "");
2803
+ return path5.resolve(root, clean);
2804
+ }
2805
+ function normalizeSeverity(value) {
2806
+ if (value === "warning" || value === "warn") return "warning";
2807
+ if (value === "info" || value === "note" || value === "help") return "info";
2808
+ if (value === "hint") return "hint";
2809
+ return "error";
2810
+ }
2811
+ function toPositiveInt(value) {
2812
+ return Math.max(1, Number.parseInt(value ?? "1", 10) || 1);
2813
+ }
2814
+ function summarize(diagnostics) {
2815
+ return {
2816
+ errors: diagnostics.filter((item) => item.severity === "error").length,
2817
+ warnings: diagnostics.filter((item) => item.severity === "warning").length,
2818
+ infos: diagnostics.filter((item) => item.severity === "info" || item.severity === "hint").length
2819
+ };
2820
+ }
2821
+ function emptySummary() {
2822
+ return { errors: 0, warnings: 0, infos: 0 };
2823
+ }
2824
+ function dedupeDiagnostics(items) {
2825
+ const seen = /* @__PURE__ */ new Set();
2826
+ return items.filter((item) => {
2827
+ const key = [
2828
+ item.source,
2829
+ item.code ?? "",
2830
+ item.file ?? "",
2831
+ item.range?.start.line ?? 0,
2832
+ item.range?.start.column ?? 0,
2833
+ item.message
2834
+ ].join("\0");
2835
+ if (seen.has(key)) return false;
2836
+ seen.add(key);
2837
+ return true;
2838
+ });
2839
+ }
2840
+ function compareDiagnostics(a, b) {
2841
+ return (a.file ?? "").localeCompare(b.file ?? "") || (a.range?.start.line ?? 0) - (b.range?.start.line ?? 0) || (a.range?.start.column ?? 0) - (b.range?.start.column ?? 0) || severityRank(a.severity) - severityRank(b.severity) || (a.code ?? "").localeCompare(b.code ?? "") || a.message.localeCompare(b.message);
2842
+ }
2843
+ function severityRank(value) {
2844
+ return value === "error" ? 0 : value === "warning" ? 1 : value === "info" ? 2 : 3;
2845
+ }
2846
+
2847
+ // src/languages/execute.ts
2848
+ import * as fs4 from "node:fs/promises";
2849
+ import * as path7 from "node:path";
2850
+
2851
+ // src/languages/plan.ts
2852
+ import * as fs3 from "node:fs/promises";
2853
+ import * as path6 from "node:path";
2854
+ var MAX_ARGUMENTS = 128;
2855
+ var MAX_ARGUMENT_LENGTH = 4096;
2856
+ var PACKAGE_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9*+._~^<>=|-]+)?$/i;
2857
+ var COMPOSER_PACKAGE_RE = /^[a-z0-9_.-]+\/[a-z0-9_.-]+(?::[a-z0-9*+._~^<>=|-]+)?$/i;
2858
+ var GO_MODULE_RE = /^(?:[a-z0-9.-]+\.)+[a-z0-9.-]+\/[a-z0-9._~+/@-]+$/i;
2859
+ async function planLanguageOperation(options) {
2860
+ validateOperationOptions(options.operation, options.operationOptions?.packages);
2861
+ const detection = await detectLanguageWorkspaces(options);
2862
+ const candidates = detection.workspaces.filter(
2863
+ (workspace) => workspace.capabilities.includes(options.operation)
2864
+ );
2865
+ const selected = selectWorkspace(candidates, options);
2866
+ if (selected.status !== "selected") return selected.result;
2867
+ const profile = (options.profiles ?? languageProfileRegistry.list()).find(
2868
+ (item) => item.id === selected.workspace.language
2869
+ );
2870
+ if (!profile) {
2871
+ return {
2872
+ status: "not_found",
2873
+ reason: `Profile ${selected.workspace.language} is unavailable.`,
2874
+ candidates
2875
+ };
2876
+ }
2877
+ if ((profile.id === "typescript" || profile.id === "javascript") && options.operation !== "syntax" && selected.workspace.packageManager === void 0 && new Set(
2878
+ selected.workspace.evidence.filter((evidence) => evidence.kind === "lockfile").map((evidence) => evidence.value.toLowerCase())
2879
+ ).size > 1) {
2880
+ return {
2881
+ status: "unavailable",
2882
+ workspace: selected.workspace,
2883
+ unavailable: {
2884
+ status: "unavailable",
2885
+ profileId: profile.id,
2886
+ workspaceId: selected.workspace.id,
2887
+ operation: options.operation,
2888
+ reason: "Conflicting Node lockfiles make the package manager ambiguous."
2889
+ }
2890
+ };
2891
+ }
2892
+ const resolver = profile.operations[options.operation];
2893
+ if (!resolver) {
2894
+ return {
2895
+ status: "unavailable",
2896
+ workspace: selected.workspace,
2897
+ unavailable: {
2898
+ status: "unavailable",
2899
+ profileId: profile.id,
2900
+ workspaceId: selected.workspace.id,
2901
+ operation: options.operation,
2902
+ reason: `${profile.displayName} does not define ${options.operation}.`
2903
+ }
2904
+ };
2905
+ }
2906
+ const canonicalCwd = options.cwd ? path6.isAbsolute(options.cwd) ? options.cwd : path6.resolve(detection.projectRoot, options.cwd) : detection.projectRoot;
2907
+ const target = options.target ? await canonicalTarget(canonicalCwd, options.target, detection.projectRoot) : void 0;
2908
+ const ctx = {
2909
+ projectRoot: detection.projectRoot,
2910
+ workspace: selected.workspace,
2911
+ ...target ? { target } : {},
2912
+ mode: options.mode ?? "standard",
2913
+ options: options.operationOptions ?? {},
2914
+ platform: process.platform,
2915
+ pathExists: async (candidate) => {
2916
+ try {
2917
+ await fs3.access(path6.resolve(detection.projectRoot, candidate));
2918
+ return true;
2919
+ } catch {
2920
+ return false;
2921
+ }
2922
+ }
2923
+ };
2924
+ const result = await resolver(ctx);
2925
+ if ("status" in result) {
2926
+ return { status: "unavailable", workspace: selected.workspace, unavailable: result };
2927
+ }
2928
+ const errors = validateCommandPlan(result, profile, detection.projectRoot);
2929
+ if (errors.length > 0) {
2930
+ return {
2931
+ status: "unavailable",
2932
+ workspace: selected.workspace,
2933
+ unavailable: {
2934
+ status: "unavailable",
2935
+ profileId: profile.id,
2936
+ workspaceId: selected.workspace.id,
2937
+ operation: options.operation,
2938
+ reason: `Generated plan failed validation: ${errors.join("; ")}`
2939
+ }
2940
+ };
2941
+ }
2942
+ return { status: "planned", workspace: selected.workspace, plan: Object.freeze(result) };
2943
+ }
2944
+ function validateCommandPlan(plan, profile, projectRoot) {
2945
+ const errors = [];
2946
+ if (plan.profileId !== profile.id) errors.push("profile id does not match");
2947
+ if (!isInside2(plan.cwd, projectRoot)) errors.push("cwd is outside project root");
2948
+ if (plan.args.length > MAX_ARGUMENTS) errors.push(`argument count exceeds ${MAX_ARGUMENTS}`);
2949
+ if (plan.args.some((arg) => arg.length > MAX_ARGUMENT_LENGTH || /[\r\n\0]/.test(arg))) {
2950
+ errors.push("arguments contain an invalid or oversized value");
2951
+ }
2952
+ if (!Number.isFinite(plan.timeoutMs) || plan.timeoutMs < 1 || plan.timeoutMs > 6e5) {
2953
+ errors.push("timeout is outside 1..600000ms");
2954
+ }
2955
+ if (!Number.isFinite(plan.outputLimitBytes) || plan.outputLimitBytes < 1 || plan.outputLimitBytes > 1e6) {
2956
+ errors.push("output limit is outside 1..1000000 bytes");
2957
+ }
2958
+ if (plan.kind === "internal") {
2959
+ if (plan.command !== null || plan.args.length !== 0)
2960
+ errors.push("internal plans cannot declare a command");
2961
+ } else {
2962
+ if (!plan.command || !profile.executables.includes(plan.command)) {
2963
+ errors.push(`executable "${plan.command ?? ""}" is not allowlisted by the profile`);
2964
+ }
2965
+ }
2966
+ if (Object.keys(plan.env).length > 0)
2967
+ errors.push("Phase 1 plans cannot override environment variables");
2968
+ return errors;
2969
+ }
2970
+ function selectWorkspace(candidates, options) {
2971
+ if (candidates.length === 0) {
2972
+ return {
2973
+ status: "result",
2974
+ result: {
2975
+ status: "not_found",
2976
+ reason: `No workspace supports ${options.operation}.`,
2977
+ candidates: []
2978
+ }
2979
+ };
2980
+ }
2981
+ if (options.workspace) {
2982
+ const requested = path6.isAbsolute(options.workspace) ? path6.resolve(options.workspace) : path6.resolve(options.projectRoot, options.workspace);
2983
+ const matches = candidates.filter(
2984
+ (item) => item.id === options.workspace || path6.resolve(item.root) === requested
2985
+ );
2986
+ if (matches.length === 1) return { status: "selected", workspace: matches[0] };
2987
+ return {
2988
+ status: "result",
2989
+ result: {
2990
+ status: "not_found",
2991
+ reason: `Requested workspace "${options.workspace}" was not detected.`,
2992
+ candidates: [...candidates]
2993
+ }
2994
+ };
2995
+ }
2996
+ if (options.target) {
2997
+ const base = options.cwd ? path6.isAbsolute(options.cwd) ? path6.resolve(options.cwd) : path6.resolve(options.projectRoot, options.cwd) : path6.resolve(options.projectRoot);
2998
+ const target = path6.isAbsolute(options.target) ? path6.resolve(options.target) : path6.resolve(base, options.target);
2999
+ const targeted = candidates.filter((item) => item.evidence.some((evidence) => evidence.kind === "target")).sort(
3000
+ (a, b) => workspaceDepth(b.root, options.projectRoot) - workspaceDepth(a.root, options.projectRoot) || compareCandidates(a, b)
3001
+ );
3002
+ if (targeted[0]) return { status: "selected", workspace: targeted[0] };
3003
+ const containing = candidates.filter((item) => isInside2(target, item.root)).sort(
3004
+ (a, b) => workspaceDepth(b.root, options.projectRoot) - workspaceDepth(a.root, options.projectRoot) || compareCandidates(a, b)
3005
+ );
3006
+ if (containing[0]) return { status: "selected", workspace: containing[0] };
3007
+ }
3008
+ const sorted = [...candidates].sort(compareCandidates);
3009
+ const first = sorted[0];
3010
+ const firstDepth = workspaceDepth(first.root, options.projectRoot);
3011
+ const tied = sorted.filter(
3012
+ (item) => item.confidence === first.confidence && workspaceDepth(item.root, options.projectRoot) === firstDepth
3013
+ );
3014
+ if (tied.length > 1) {
3015
+ return {
3016
+ status: "result",
3017
+ result: {
3018
+ status: "ambiguous",
3019
+ reason: "Multiple workspaces have equal confidence; provide target, language, or workspace.",
3020
+ candidates: tied
3021
+ }
3022
+ };
3023
+ }
3024
+ return { status: "selected", workspace: first };
3025
+ }
3026
+ function compareCandidates(a, b) {
3027
+ return b.confidence - a.confidence || a.language.localeCompare(b.language) || a.root.localeCompare(b.root);
3028
+ }
3029
+ function workspaceDepth(workspaceRoot, projectRoot) {
3030
+ const relative5 = path6.relative(path6.resolve(projectRoot), path6.resolve(workspaceRoot));
3031
+ return relative5 === "" ? 0 : relative5.split(path6.sep).length;
3032
+ }
3033
+ function validateOperationOptions(operation, packages) {
3034
+ if (!operation.startsWith("package-") || !packages) return;
3035
+ for (const value of packages) {
3036
+ if (!value || value.length > 214 || value.startsWith("-") || /[\r\n\0;&|`$<>\\]/.test(value)) {
3037
+ throw new Error(`Invalid package identifier "${value}"`);
3038
+ }
3039
+ if (value.startsWith(".") || value.startsWith("/") || /^[A-Za-z]:/.test(value) || value.includes("://")) {
3040
+ throw new Error(`Package paths and URLs are not supported: "${value}"`);
3041
+ }
3042
+ if (!PACKAGE_NAME_RE.test(value) && !COMPOSER_PACKAGE_RE.test(value) && !GO_MODULE_RE.test(value)) {
3043
+ throw new Error(`Invalid package identifier "${value}"`);
3044
+ }
3045
+ }
3046
+ }
3047
+ async function canonicalTarget(cwd, target, projectRoot) {
3048
+ const resolved = path6.isAbsolute(target) ? path6.resolve(target) : path6.resolve(cwd, target);
3049
+ let real;
3050
+ try {
3051
+ real = await fs3.realpath(resolved);
3052
+ } catch (error) {
3053
+ if (error.code !== "ENOENT") throw error;
3054
+ const parent = await fs3.realpath(path6.dirname(resolved));
3055
+ real = path6.join(parent, path6.basename(resolved));
3056
+ }
3057
+ if (!isInside2(real, projectRoot)) throw new Error(`target is outside project root: ${target}`);
3058
+ return real;
3059
+ }
3060
+ function isInside2(candidate, root) {
3061
+ const relative5 = path6.relative(path6.resolve(root), path6.resolve(candidate));
3062
+ return relative5 === "" || !relative5.startsWith("..") && !path6.isAbsolute(relative5);
3063
+ }
3064
+
3065
+ // src/languages/execute.ts
3066
+ var MAX_INTERNAL_SOURCE_BYTES = 15e5;
3067
+ async function* executeLanguagePlan(options) {
3068
+ const { plan, workspace } = options;
3069
+ if (options.signal.aborted) {
3070
+ return terminalResult(options, "cancelled", options.signal.reason);
3071
+ }
3072
+ const profile = languageProfileRegistry.get(plan.profileId);
3073
+ if (!profile) return unavailableResult(options, `Unknown language profile: ${plan.profileId}`);
3074
+ if (workspace.id !== plan.workspaceId || workspace.language !== plan.profileId) {
3075
+ return unavailableResult(options, "Plan and workspace identity do not match.");
3076
+ }
3077
+ const validation = validateCommandPlan(plan, profile, options.projectRoot);
3078
+ if (validation.length > 0) {
3079
+ return unavailableResult(options, `Plan failed execution validation: ${validation.join("; ")}`);
3080
+ }
3081
+ const containmentError = await validatePlanRealpaths(plan, options.projectRoot);
3082
+ if (containmentError) return unavailableResult(options, containmentError);
3083
+ if (plan.operation.startsWith("package-") || plan.network) {
3084
+ return terminalNonPackageResult(
3085
+ options,
3086
+ "Package and network plans require the separate language_package tool (Phase 3)."
3087
+ );
3088
+ }
3089
+ const startedAt = Date.now();
3090
+ yield {
3091
+ type: "log",
3092
+ text: plan.kind === "internal" ? `Running ${plan.parser}\u2026` : `${plan.command} ${plan.args.join(" ")}`,
3093
+ data: { language: plan.profileId, operation: plan.operation, workspace: workspace.root }
3094
+ };
3095
+ if (plan.kind === "internal") {
3096
+ try {
3097
+ return await executeInternal(options, startedAt);
3098
+ } catch (error) {
3099
+ return unavailableResult(options, error instanceof Error ? error.message : String(error));
3100
+ }
3101
+ }
3102
+ const timeoutController = new AbortController();
3103
+ const timer = setTimeout(
3104
+ () => timeoutController.abort(new Error("language plan timed out")),
3105
+ plan.timeoutMs
3106
+ );
3107
+ timer.unref?.();
3108
+ const signal = AbortSignal.any([options.signal, timeoutController.signal]);
3109
+ let spawned;
3110
+ try {
3111
+ const stream = spawnStream({
3112
+ cmd: plan.command,
3113
+ args: [...plan.args],
3114
+ cwd: plan.cwd,
3115
+ signal,
3116
+ maxBytes: plan.outputLimitBytes
3117
+ });
3118
+ for (; ; ) {
3119
+ const next = await stream.next();
3120
+ if (next.done) {
3121
+ spawned = next.value;
3122
+ break;
3123
+ }
3124
+ yield next.value;
3125
+ }
3126
+ } catch (error) {
3127
+ const timedOut2 = timeoutController.signal.aborted && !options.signal.aborted;
3128
+ const cancelled2 = options.signal.aborted;
3129
+ return {
3130
+ status: timedOut2 ? "timed_out" : cancelled2 ? "cancelled" : "failed",
3131
+ language: plan.profileId,
3132
+ workspace,
3133
+ plan,
3134
+ exitCode: null,
3135
+ durationMs: Date.now() - startedAt,
3136
+ diagnostics: Object.freeze([]),
3137
+ omittedDiagnostics: 0,
3138
+ summary: emptySummary2(),
3139
+ output: "",
3140
+ truncated: false,
3141
+ error: error instanceof Error ? error.message : String(error)
3142
+ };
3143
+ } finally {
3144
+ clearTimeout(timer);
3145
+ }
3146
+ const parsed = parseLanguageDiagnostics(
3147
+ plan.parser,
3148
+ spawned.stdout,
3149
+ spawned.stderr,
3150
+ workspace.root
3151
+ );
3152
+ const timedOut = timeoutController.signal.aborted && !options.signal.aborted;
3153
+ const cancelled = options.signal.aborted;
3154
+ const spawnUnavailable = Boolean(
3155
+ spawned.error && /ENOENT|not found|cannot find/i.test(spawned.error)
3156
+ );
3157
+ const status = timedOut ? "timed_out" : cancelled ? "cancelled" : spawnUnavailable ? "unavailable" : spawned.exitCode === 0 ? "passed" : "failed";
3158
+ const raw = [spawned.stdout, spawned.stderr, spawned.error].filter(Boolean).join("\n");
3159
+ return {
3160
+ status,
3161
+ language: plan.profileId,
3162
+ workspace,
3163
+ plan,
3164
+ exitCode: spawned.exitCode,
3165
+ durationMs: Date.now() - startedAt,
3166
+ diagnostics: parsed.diagnostics,
3167
+ omittedDiagnostics: parsed.omitted,
3168
+ summary: parsed.summary,
3169
+ output: normalizeCommandOutput(raw, { maxBytes: plan.outputLimitBytes }),
3170
+ truncated: spawned.truncated,
3171
+ ...spawned.spoolPath ? { spoolPath: spawned.spoolPath } : {},
3172
+ ...spawned.error ? { error: spawned.error } : {}
3173
+ };
3174
+ }
3175
+ async function executeInternal(options, startedAt) {
3176
+ const target = options.plan.evidence.find((item) => item.kind === "target")?.path;
3177
+ if (!target) return unavailableResult(options, "Internal syntax plan has no target evidence.");
3178
+ const safeTarget = await assertContainedFile(target, options.projectRoot);
3179
+ const stat5 = await fs4.stat(safeTarget);
3180
+ if (stat5.size > MAX_INTERNAL_SOURCE_BYTES) {
3181
+ return unavailableResult(
3182
+ options,
3183
+ `Internal syntax target exceeds ${MAX_INTERNAL_SOURCE_BYTES} bytes.`
3184
+ );
3185
+ }
3186
+ const source = await fs4.readFile(safeTarget, "utf8");
3187
+ const parsed = await diagnosticsForInternalSyntax(options.plan.profileId, safeTarget, source);
3188
+ return {
3189
+ status: parsed.summary.errors > 0 ? "failed" : "passed",
3190
+ language: options.plan.profileId,
3191
+ workspace: options.workspace,
3192
+ plan: options.plan,
3193
+ exitCode: parsed.summary.errors > 0 ? 1 : 0,
3194
+ durationMs: Date.now() - startedAt,
3195
+ diagnostics: parsed.diagnostics,
3196
+ omittedDiagnostics: parsed.omitted,
3197
+ summary: parsed.summary,
3198
+ output: parsed.summary.errors > 0 ? `${parsed.summary.errors} syntax error(s) found.` : "Syntax check passed.",
3199
+ truncated: false
3200
+ };
3201
+ }
3202
+ async function validatePlanRealpaths(plan, projectRoot) {
3203
+ const realRoot = await fs4.realpath(projectRoot);
3204
+ let realCwd;
3205
+ try {
3206
+ realCwd = await fs4.realpath(plan.cwd);
3207
+ } catch (error) {
3208
+ return `Plan cwd is unavailable: ${error instanceof Error ? error.message : String(error)}`;
3209
+ }
3210
+ if (!isRealInside(realCwd, realRoot)) return "Plan cwd resolves outside project root.";
3211
+ for (const argument of plan.args) {
3212
+ if (!path7.isAbsolute(argument)) continue;
3213
+ try {
3214
+ const realArgument = await fs4.realpath(argument);
3215
+ if (!isRealInside(realArgument, realRoot)) {
3216
+ return `Plan argument resolves outside project root: ${argument}`;
3217
+ }
3218
+ } catch (error) {
3219
+ if (error.code !== "ENOENT") {
3220
+ return `Plan argument could not be resolved safely: ${argument}`;
3221
+ }
3222
+ }
3223
+ }
3224
+ return void 0;
3225
+ }
3226
+ function isRealInside(candidate, root) {
3227
+ const relative5 = path7.relative(root, candidate);
3228
+ return relative5 === "" || !relative5.startsWith("..") && !path7.isAbsolute(relative5);
3229
+ }
3230
+ async function assertContainedFile(candidate, projectRoot) {
3231
+ const realRoot = await fs4.realpath(projectRoot);
3232
+ const realTarget = await fs4.realpath(candidate);
3233
+ const relative5 = path7.relative(realRoot, realTarget);
3234
+ if (relative5.startsWith("..") || path7.isAbsolute(relative5)) {
3235
+ throw new Error(`Internal syntax target resolves outside project root: ${candidate}`);
3236
+ }
3237
+ return realTarget;
3238
+ }
3239
+ function terminalResult(options, status, reason) {
3240
+ const message = reason instanceof Error ? reason.message : reason ? String(reason) : status;
3241
+ return {
3242
+ status,
3243
+ language: options.plan.profileId,
3244
+ workspace: options.workspace,
3245
+ plan: options.plan,
3246
+ exitCode: null,
3247
+ durationMs: 0,
3248
+ diagnostics: Object.freeze([]),
3249
+ omittedDiagnostics: 0,
3250
+ summary: emptySummary2(),
3251
+ output: "",
3252
+ truncated: false,
3253
+ error: message
3254
+ };
3255
+ }
3256
+ function unavailableResult(options, reason) {
3257
+ return {
3258
+ status: "unavailable",
3259
+ language: options.plan.profileId,
3260
+ workspace: options.workspace,
3261
+ plan: options.plan,
3262
+ exitCode: null,
3263
+ durationMs: 0,
3264
+ diagnostics: Object.freeze([]),
3265
+ omittedDiagnostics: 0,
3266
+ summary: emptySummary2(),
3267
+ output: reason,
3268
+ truncated: false,
3269
+ error: reason
3270
+ };
3271
+ }
3272
+ function emptySummary2() {
3273
+ return { errors: 0, warnings: 0, infos: 0 };
3274
+ }
3275
+ function terminalNonPackageResult(options, reason) {
3276
+ return {
3277
+ status: "unavailable",
3278
+ language: options.plan.profileId,
3279
+ workspace: options.workspace,
3280
+ plan: options.plan,
3281
+ exitCode: null,
3282
+ durationMs: 0,
3283
+ diagnostics: Object.freeze([]),
3284
+ omittedDiagnostics: 0,
3285
+ summary: emptySummary2(),
3286
+ output: reason,
3287
+ truncated: false,
3288
+ error: reason
3289
+ };
3290
+ }
3291
+
3292
+ // src/languages/legacy-bridge.ts
3293
+ var NON_JS_MARKERS = [
3294
+ { filename: "go.mod", language: "go" },
3295
+ { filename: "go.work", language: "go" },
3296
+ { filename: "Cargo.toml", language: "rust" },
3297
+ { filename: "composer.json", language: "php" }
3298
+ ];
3299
+ var NON_JS_SUFFIXES = [
3300
+ { suffix: ".csproj", language: "csharp" },
3301
+ { suffix: ".fsproj", language: "csharp" }
3302
+ ];
3303
+ async function detectNonJsEcosystem(cwd, projectRoot) {
3304
+ try {
3305
+ let dir = path8.resolve(cwd);
3306
+ const root = path8.resolve(projectRoot);
3307
+ for (let depth = 0; depth <= 4 && dir.startsWith(root); depth++) {
3308
+ for (const marker of NON_JS_MARKERS) {
3309
+ try {
3310
+ const s = await fs5.stat(path8.join(dir, marker.filename));
3311
+ if (s.isFile()) return marker.language;
3312
+ } catch {
3313
+ }
3314
+ }
3315
+ try {
3316
+ const entries = await fs5.readdir(dir);
3317
+ for (const entry of entries) {
3318
+ for (const suffix of NON_JS_SUFFIXES) {
3319
+ if (entry.toLowerCase().endsWith(suffix.suffix)) return suffix.language;
3320
+ }
3321
+ }
3322
+ } catch {
3323
+ }
3324
+ if (dir === root) break;
3325
+ dir = path8.dirname(dir);
3326
+ }
3327
+ } catch {
3328
+ }
3329
+ return null;
3330
+ }
3331
+ async function tryLegacyCodeOperation(operation, ctx) {
3332
+ const language = await detectNonJsEcosystem(ctx.cwd, ctx.projectRoot);
3333
+ if (!language) return null;
3334
+ const planResult = await planLanguageOperation({
3335
+ projectRoot: ctx.projectRoot,
3336
+ cwd: ctx.cwd,
3337
+ operation,
3338
+ language,
3339
+ ...ctx.target ? { target: ctx.target } : {},
3340
+ signal: ctx.signal
3341
+ });
3342
+ if (planResult.status !== "planned") return null;
3343
+ const runner = executeLanguagePlan({
3344
+ projectRoot: ctx.projectRoot,
3345
+ workspace: planResult.workspace,
3346
+ plan: planResult.plan,
3347
+ signal: ctx.signal
3348
+ });
3349
+ for (; ; ) {
3350
+ const next = await runner.next();
3351
+ if (next.done) return { language, run: next.value };
3352
+ }
3353
+ }
3354
+
990
3355
  // src/format.ts
991
3356
  var formatTool = {
992
3357
  name: "format",
@@ -1030,6 +3395,28 @@ var formatTool = {
1030
3395
  async *executeStream(input, ctx, opts) {
1031
3396
  const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;
1032
3397
  const fixer = input.fixer ?? "auto";
3398
+ if (fixer === "auto" && !input.files) {
3399
+ const op = input.check ? "format-check" : "format-write";
3400
+ const bridge = await tryLegacyCodeOperation(op, {
3401
+ cwd,
3402
+ projectRoot: ctx.projectRoot,
3403
+ signal: opts.signal
3404
+ });
3405
+ if (bridge?.run) {
3406
+ const run = bridge.run;
3407
+ yield {
3408
+ type: "final",
3409
+ output: {
3410
+ fixer: bridge.language,
3411
+ files_checked: 0,
3412
+ files_changed: run.summary.errors > 0 ? 0 : 1,
3413
+ output: normalizeCommandOutput(run.output || run.error || ""),
3414
+ truncated: run.truncated
3415
+ }
3416
+ };
3417
+ return;
3418
+ }
3419
+ }
1033
3420
  const detected = fixer === "auto" ? await detectFixer(cwd) : fixer;
1034
3421
  if (!detected) {
1035
3422
  yield {
@@ -1076,20 +3463,20 @@ var formatTool = {
1076
3463
  }
1077
3464
  };
1078
3465
  async function detectFixer(cwd) {
1079
- const { stat: stat2 } = await import('node:fs/promises');
3466
+ const { stat: stat5 } = await import("node:fs/promises");
1080
3467
  try {
1081
- await stat2(`${cwd}/biome.json`);
3468
+ await stat5(`${cwd}/biome.json`);
1082
3469
  return "biome";
1083
3470
  } catch {
1084
3471
  try {
1085
- await stat2(`${cwd}/.prettierrc`);
3472
+ await stat5(`${cwd}/.prettierrc`);
1086
3473
  return "prettier";
1087
3474
  } catch {
1088
3475
  return "biome";
1089
3476
  }
1090
3477
  }
1091
3478
  }
1092
-
1093
- export { formatTool };
3479
+ export {
3480
+ formatTool
3481
+ };
1094
3482
  //# sourceMappingURL=format.js.map
1095
- //# sourceMappingURL=format.js.map