@wrongstack/tools 0.284.1 → 0.286.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 (330) hide show
  1. package/README.md +19 -0
  2. package/dist/_concurrency.d.ts +16 -0
  3. package/dist/_concurrency.d.ts.map +1 -0
  4. package/dist/_danger-detect.d.ts +54 -0
  5. package/dist/_danger-detect.d.ts.map +1 -0
  6. package/dist/_edit-match.d.ts +86 -0
  7. package/dist/_edit-match.d.ts.map +1 -0
  8. package/dist/_env.d.ts +6 -0
  9. package/dist/_env.d.ts.map +1 -0
  10. package/dist/_fetch-guard.d.ts +31 -0
  11. package/dist/_fetch-guard.d.ts.map +1 -0
  12. package/dist/_output-spool.d.ts +38 -0
  13. package/dist/_output-spool.d.ts.map +1 -0
  14. package/dist/_redact-command.d.ts +6 -0
  15. package/dist/_redact-command.d.ts.map +1 -0
  16. package/dist/_regex.d.ts +39 -0
  17. package/dist/_regex.d.ts.map +1 -0
  18. package/dist/_session-shell.d.ts +73 -0
  19. package/dist/_session-shell.d.ts.map +1 -0
  20. package/dist/_shell-pick.d.ts +158 -0
  21. package/dist/_shell-pick.d.ts.map +1 -0
  22. package/dist/_spawn-stream.d.ts +32 -0
  23. package/dist/_spawn-stream.d.ts.map +1 -0
  24. package/dist/_syntax-check.d.ts +16 -0
  25. package/dist/_syntax-check.d.ts.map +1 -0
  26. package/dist/_util.d.ts +68 -0
  27. package/dist/_util.d.ts.map +1 -0
  28. package/dist/_win32-resolve.d.ts +42 -0
  29. package/dist/_win32-resolve.d.ts.map +1 -0
  30. package/dist/audit.d.ts +4 -5
  31. package/dist/audit.d.ts.map +1 -0
  32. package/dist/audit.js +2743 -28
  33. package/dist/audit.js.map +7 -1
  34. package/dist/bash-kill-guard.d.ts +62 -0
  35. package/dist/bash-kill-guard.d.ts.map +1 -0
  36. package/dist/bash.d.ts +4 -5
  37. package/dist/bash.d.ts.map +1 -0
  38. package/dist/bash.js +59 -30
  39. package/dist/bash.js.map +7 -1
  40. package/dist/batch-tool-use.d.ts +4 -5
  41. package/dist/batch-tool-use.d.ts.map +1 -0
  42. package/dist/batch-tool-use.js +3 -3
  43. package/dist/batch-tool-use.js.map +7 -1
  44. package/dist/browser/artifacts.d.ts +13 -0
  45. package/dist/browser/artifacts.d.ts.map +1 -0
  46. package/dist/browser/index.d.ts +7 -0
  47. package/dist/browser/index.d.ts.map +1 -0
  48. package/dist/browser/index.js +1264 -0
  49. package/dist/browser/index.js.map +7 -0
  50. package/dist/browser/manager.d.ts +57 -0
  51. package/dist/browser/manager.d.ts.map +1 -0
  52. package/dist/browser/network-guard-proxy.d.ts +23 -0
  53. package/dist/browser/network-guard-proxy.d.ts.map +1 -0
  54. package/dist/browser/security.d.ts +29 -0
  55. package/dist/browser/security.d.ts.map +1 -0
  56. package/dist/browser/tools.d.ts +62 -0
  57. package/dist/browser/tools.d.ts.map +1 -0
  58. package/dist/browser/types.d.ts +60 -0
  59. package/dist/browser/types.d.ts.map +1 -0
  60. package/dist/builtin.d.ts +11 -12
  61. package/dist/builtin.d.ts.map +1 -0
  62. package/dist/builtin.js +8099 -2178
  63. package/dist/builtin.js.map +7 -1
  64. package/dist/circuit-breaker.d.ts +4 -5
  65. package/dist/circuit-breaker.d.ts.map +1 -0
  66. package/dist/circuit-breaker.js +3 -3
  67. package/dist/circuit-breaker.js.map +7 -1
  68. package/dist/codebase-index/background-indexer.d.ts +116 -0
  69. package/dist/codebase-index/background-indexer.d.ts.map +1 -0
  70. package/dist/codebase-index/bm25.d.ts +40 -0
  71. package/dist/codebase-index/bm25.d.ts.map +1 -0
  72. package/dist/codebase-index/circuit-breaker.d.ts +94 -0
  73. package/dist/codebase-index/circuit-breaker.d.ts.map +1 -0
  74. package/dist/codebase-index/codebase-index-tool.d.ts +17 -0
  75. package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -0
  76. package/dist/codebase-index/codebase-search-tool.d.ts +43 -0
  77. package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -0
  78. package/dist/codebase-index/codebase-stats-tool.d.ts +23 -0
  79. package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -0
  80. package/dist/codebase-index/gitignore.d.ts +22 -0
  81. package/dist/codebase-index/gitignore.d.ts.map +1 -0
  82. package/dist/codebase-index/go-parser.d.ts +16 -0
  83. package/dist/codebase-index/go-parser.d.ts.map +1 -0
  84. package/dist/codebase-index/index-service.d.ts +25 -0
  85. package/dist/codebase-index/index-service.d.ts.map +1 -0
  86. package/dist/codebase-index/index.d.ts +23 -271
  87. package/dist/codebase-index/index.d.ts.map +1 -0
  88. package/dist/codebase-index/index.js +136 -68
  89. package/dist/codebase-index/index.js.map +7 -1
  90. package/dist/codebase-index/indexer.d.ts +28 -0
  91. package/dist/codebase-index/indexer.d.ts.map +1 -0
  92. package/dist/codebase-index/json-parser.d.ts +8 -0
  93. package/dist/codebase-index/json-parser.d.ts.map +1 -0
  94. package/dist/codebase-index/lsp-kind.d.ts +53 -0
  95. package/dist/codebase-index/lsp-kind.d.ts.map +1 -0
  96. package/dist/codebase-index/py-parser.d.ts +16 -0
  97. package/dist/codebase-index/py-parser.d.ts.map +1 -0
  98. package/dist/codebase-index/refs-extractor.d.ts +25 -0
  99. package/dist/codebase-index/refs-extractor.d.ts.map +1 -0
  100. package/dist/codebase-index/rs-parser.d.ts +8 -0
  101. package/dist/codebase-index/rs-parser.d.ts.map +1 -0
  102. package/dist/codebase-index/schema.d.ts +82 -0
  103. package/dist/codebase-index/schema.d.ts.map +1 -0
  104. package/dist/codebase-index/ts-parser.d.ts +30 -0
  105. package/dist/codebase-index/ts-parser.d.ts.map +1 -0
  106. package/dist/codebase-index/worker-protocol.d.ts +77 -0
  107. package/dist/codebase-index/worker-protocol.d.ts.map +1 -0
  108. package/dist/codebase-index/worker.d.ts +16 -2
  109. package/dist/codebase-index/worker.d.ts.map +1 -0
  110. package/dist/codebase-index/worker.js +103 -65
  111. package/dist/codebase-index/worker.js.map +7 -1
  112. package/dist/codebase-index/writer.d.ts +189 -0
  113. package/dist/codebase-index/writer.d.ts.map +1 -0
  114. package/dist/codebase-index/yaml-parser.d.ts +8 -0
  115. package/dist/codebase-index/yaml-parser.d.ts.map +1 -0
  116. package/dist/design.d.ts +38 -0
  117. package/dist/design.d.ts.map +1 -0
  118. package/dist/design.js +270 -0
  119. package/dist/design.js.map +7 -0
  120. package/dist/diff.d.ts +4 -5
  121. package/dist/diff.d.ts.map +1 -0
  122. package/dist/diff.js +14 -12
  123. package/dist/diff.js.map +7 -1
  124. package/dist/document.d.ts +4 -5
  125. package/dist/document.d.ts.map +1 -0
  126. package/dist/document.js +8 -7
  127. package/dist/document.js.map +7 -1
  128. package/dist/e2e.d.ts +56 -0
  129. package/dist/e2e.d.ts.map +1 -0
  130. package/dist/edit.d.ts +5 -36
  131. package/dist/edit.d.ts.map +1 -0
  132. package/dist/edit.js +28 -16
  133. package/dist/edit.js.map +7 -1
  134. package/dist/exec.d.ts +67 -2
  135. package/dist/exec.d.ts.map +1 -0
  136. package/dist/exec.js +51 -26
  137. package/dist/exec.js.map +7 -1
  138. package/dist/fetch.d.ts +4 -32
  139. package/dist/fetch.d.ts.map +1 -0
  140. package/dist/fetch.js +89 -77
  141. package/dist/fetch.js.map +7 -1
  142. package/dist/format.d.ts +4 -5
  143. package/dist/format.d.ts.map +1 -0
  144. package/dist/format.js +2415 -28
  145. package/dist/format.js.map +7 -1
  146. package/dist/git.d.ts +4 -5
  147. package/dist/git.d.ts.map +1 -0
  148. package/dist/git.js +10 -9
  149. package/dist/git.js.map +7 -1
  150. package/dist/glob.d.ts +4 -5
  151. package/dist/glob.d.ts.map +1 -0
  152. package/dist/glob.js +15 -12
  153. package/dist/glob.js.map +7 -1
  154. package/dist/grep.d.ts +4 -5
  155. package/dist/grep.d.ts.map +1 -0
  156. package/dist/grep.js +13 -10
  157. package/dist/grep.js.map +7 -1
  158. package/dist/index.d.ts +21 -724
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +8955 -2866
  161. package/dist/index.js.map +7 -1
  162. package/dist/install.d.ts +4 -5
  163. package/dist/install.d.ts.map +1 -0
  164. package/dist/install.js +2680 -31
  165. package/dist/install.js.map +7 -1
  166. package/dist/json.d.ts +4 -5
  167. package/dist/json.d.ts.map +1 -0
  168. package/dist/json.js +13 -11
  169. package/dist/json.js.map +7 -1
  170. package/dist/kanban.d.ts +107 -0
  171. package/dist/kanban.d.ts.map +1 -0
  172. package/dist/kanban.js +845 -0
  173. package/dist/kanban.js.map +7 -0
  174. package/dist/languages/detect.d.ts +3 -0
  175. package/dist/languages/detect.d.ts.map +1 -0
  176. package/dist/languages/diagnostics.d.ts +15 -0
  177. package/dist/languages/diagnostics.d.ts.map +1 -0
  178. package/dist/languages/execute-tool.d.ts +34 -0
  179. package/dist/languages/execute-tool.d.ts.map +1 -0
  180. package/dist/languages/execute.d.ts +18 -0
  181. package/dist/languages/execute.d.ts.map +1 -0
  182. package/dist/languages/index.d.ts +12 -0
  183. package/dist/languages/index.d.ts.map +1 -0
  184. package/dist/languages/index.js +4457 -0
  185. package/dist/languages/index.js.map +7 -0
  186. package/dist/languages/legacy-bridge.d.ts +29 -0
  187. package/dist/languages/legacy-bridge.d.ts.map +1 -0
  188. package/dist/languages/package-tool.d.ts +29 -0
  189. package/dist/languages/package-tool.d.ts.map +1 -0
  190. package/dist/languages/plan.d.ts +4 -0
  191. package/dist/languages/plan.d.ts.map +1 -0
  192. package/dist/languages/profile-helpers.d.ts +14 -0
  193. package/dist/languages/profile-helpers.d.ts.map +1 -0
  194. package/dist/languages/profiles/additional.d.ts +3 -0
  195. package/dist/languages/profiles/additional.d.ts.map +1 -0
  196. package/dist/languages/profiles/primary.d.ts +3 -0
  197. package/dist/languages/profiles/primary.d.ts.map +1 -0
  198. package/dist/languages/registry.d.ts +11 -0
  199. package/dist/languages/registry.d.ts.map +1 -0
  200. package/dist/languages/tool.d.ts +32 -0
  201. package/dist/languages/tool.d.ts.map +1 -0
  202. package/dist/languages/types.d.ts +207 -0
  203. package/dist/languages/types.d.ts.map +1 -0
  204. package/dist/lint.d.ts +4 -5
  205. package/dist/lint.d.ts.map +1 -0
  206. package/dist/lint.js +2415 -27
  207. package/dist/lint.js.map +7 -1
  208. package/dist/logs.d.ts +4 -5
  209. package/dist/logs.d.ts.map +1 -0
  210. package/dist/logs.js +16 -10
  211. package/dist/logs.js.map +7 -1
  212. package/dist/memory.d.ts +7 -8
  213. package/dist/memory.d.ts.map +1 -0
  214. package/dist/memory.js +7 -5
  215. package/dist/memory.js.map +7 -1
  216. package/dist/mode.d.ts +4 -5
  217. package/dist/mode.d.ts.map +1 -0
  218. package/dist/mode.js +3 -3
  219. package/dist/mode.js.map +7 -1
  220. package/dist/next-steps.d.ts +5 -6
  221. package/dist/next-steps.d.ts.map +1 -0
  222. package/dist/next-steps.js +5 -4
  223. package/dist/next-steps.js.map +7 -1
  224. package/dist/outdated.d.ts +4 -5
  225. package/dist/outdated.d.ts.map +1 -0
  226. package/dist/outdated.js +4777 -68
  227. package/dist/outdated.js.map +7 -1
  228. package/dist/pack.d.ts +3 -6
  229. package/dist/pack.d.ts.map +1 -0
  230. package/dist/pack.js +8134 -2204
  231. package/dist/pack.js.map +7 -1
  232. package/dist/patch.d.ts +4 -5
  233. package/dist/patch.d.ts.map +1 -0
  234. package/dist/patch.js +18 -16
  235. package/dist/patch.js.map +7 -1
  236. package/dist/plan.d.ts +55 -0
  237. package/dist/plan.d.ts.map +1 -0
  238. package/dist/plan.js +280 -0
  239. package/dist/plan.js.map +7 -0
  240. package/dist/process-guardian.d.ts +100 -0
  241. package/dist/process-guardian.d.ts.map +1 -0
  242. package/dist/process-registry-persistent.d.ts +121 -0
  243. package/dist/process-registry-persistent.d.ts.map +1 -0
  244. package/dist/process-registry.d.ts +13 -17
  245. package/dist/process-registry.d.ts.map +1 -0
  246. package/dist/process-registry.js +15 -9
  247. package/dist/process-registry.js.map +7 -1
  248. package/dist/ps-slash.d.ts +106 -0
  249. package/dist/ps-slash.d.ts.map +1 -0
  250. package/dist/ps-slash.js +1291 -0
  251. package/dist/ps-slash.js.map +7 -0
  252. package/dist/read.d.ts +4 -5
  253. package/dist/read.d.ts.map +1 -0
  254. package/dist/read.js +13 -11
  255. package/dist/read.js.map +7 -1
  256. package/dist/replace.d.ts +4 -5
  257. package/dist/replace.d.ts.map +1 -0
  258. package/dist/replace.js +26 -13
  259. package/dist/replace.js.map +7 -1
  260. package/dist/scaffold.d.ts +4 -5
  261. package/dist/scaffold.d.ts.map +1 -0
  262. package/dist/scaffold.js +16 -14
  263. package/dist/scaffold.js.map +7 -1
  264. package/dist/search.d.ts +5 -6
  265. package/dist/search.d.ts.map +1 -0
  266. package/dist/search.js +32 -27
  267. package/dist/search.js.map +7 -1
  268. package/dist/set-working-dir.d.ts +18 -0
  269. package/dist/set-working-dir.d.ts.map +1 -0
  270. package/dist/shim/node-sqlite.d.ts +3 -0
  271. package/dist/shim/node-sqlite.d.ts.map +1 -0
  272. package/dist/skill.d.ts +45 -0
  273. package/dist/skill.d.ts.map +1 -0
  274. package/dist/skill.js +175 -0
  275. package/dist/skill.js.map +7 -0
  276. package/dist/spawn-background.d.ts +80 -0
  277. package/dist/spawn-background.d.ts.map +1 -0
  278. package/dist/task.d.ts +35 -0
  279. package/dist/task.d.ts.map +1 -0
  280. package/dist/task.js +380 -0
  281. package/dist/task.js.map +7 -0
  282. package/dist/test.d.ts +4 -5
  283. package/dist/test.d.ts.map +1 -0
  284. package/dist/test.js +2419 -27
  285. package/dist/test.js.map +7 -1
  286. package/dist/todo.d.ts +4 -5
  287. package/dist/todo.d.ts.map +1 -0
  288. package/dist/todo.js +5 -5
  289. package/dist/todo.js.map +7 -1
  290. package/dist/tool-diff.d.ts +16 -17
  291. package/dist/tool-diff.d.ts.map +1 -0
  292. package/dist/tool-diff.js +11 -3
  293. package/dist/tool-diff.js.map +7 -1
  294. package/dist/tool-help.d.ts +4 -5
  295. package/dist/tool-help.d.ts.map +1 -0
  296. package/dist/tool-help.js +3 -3
  297. package/dist/tool-help.js.map +7 -1
  298. package/dist/tool-icon-map.d.ts +46 -0
  299. package/dist/tool-icon-map.d.ts.map +1 -0
  300. package/dist/tool-icons.d.ts +5 -6
  301. package/dist/tool-icons.d.ts.map +1 -0
  302. package/dist/tool-icons.js +16 -4
  303. package/dist/tool-icons.js.map +7 -1
  304. package/dist/tool-search.d.ts +4 -5
  305. package/dist/tool-search.d.ts.map +1 -0
  306. package/dist/tool-search.js +3 -3
  307. package/dist/tool-search.js.map +7 -1
  308. package/dist/tool-summary.d.ts +4 -5
  309. package/dist/tool-summary.d.ts.map +1 -0
  310. package/dist/tool-summary.js +5 -3
  311. package/dist/tool-summary.js.map +7 -1
  312. package/dist/tool-use.d.ts +4 -5
  313. package/dist/tool-use.d.ts.map +1 -0
  314. package/dist/tool-use.js +3 -3
  315. package/dist/tool-use.js.map +7 -1
  316. package/dist/tree.d.ts +4 -5
  317. package/dist/tree.d.ts.map +1 -0
  318. package/dist/tree.js +11 -9
  319. package/dist/tree.js.map +7 -1
  320. package/dist/typecheck.d.ts +4 -5
  321. package/dist/typecheck.d.ts.map +1 -0
  322. package/dist/typecheck.js +2416 -28
  323. package/dist/typecheck.js.map +7 -1
  324. package/dist/write.d.ts +4 -5
  325. package/dist/write.d.ts.map +1 -0
  326. package/dist/write.js +21 -16
  327. package/dist/write.js.map +7 -1
  328. package/package.json +39 -6
  329. package/dist/background-indexer-BeDBxfSh.d.ts +0 -371
  330. package/dist/exec-2OKoT6tk.d.ts +0 -121
package/README.md CHANGED
@@ -42,6 +42,24 @@ pnpm add @wrongstack/tools @wrongstack/core
42
42
  | `fetch` | `auto` | no | SSRF-hardened (IPv4 + IPv6 private CIDR, redirect re-validation, http://-downgrade refused) |
43
43
  | `search` | `auto` | no | Web search via configured provider |
44
44
 
45
+ ### Browser
46
+
47
+ First-party `browser_*` tools lazily launch Playwright Chromium and use isolated per-agent
48
+ contexts. Observation tools are automatic; navigation, interaction, screenshot persistence,
49
+ upload, and evaluation require confirmation where appropriate. See
50
+ [browser automation](../../docs/browser-automation.md) for the lifecycle and security contract.
51
+ Credential entry uses `browser_type.secretEnv` so audit records retain a placeholder rather than
52
+ the secret value.
53
+
54
+ ### E2E discovery
55
+
56
+ | Tool | Permission | Mutating | Notes |
57
+ |------|------------|----------|-------|
58
+ | `e2e_plan` | `auto` | no | Statically discovers Playwright/Cypress configs, servers, specs, and exact argv without evaluating project code |
59
+
60
+ See [the browser-aware E2E runner guide](../../docs/e2e-runner.md). Process startup and test
61
+ execution remain separate permission-gated steps.
62
+
45
63
  ### Project lifecycle
46
64
 
47
65
  | Tool | Permission | Notes |
@@ -87,6 +105,7 @@ const all = new ToolRegistry(builtinTools);
87
105
  - **`bash` / `exec` child env sanitized** to a fixed allowlist + secret-substring strip (`TOKEN` / `SECRET` / `PASSWORD` / `AUTH` / `BEARER` / `COOKIE` / `PRIVATE` / `KEY`). Opt-out via `WRONGSTACK_BASH_ENV_PASSTHROUGH=1`.
88
106
  - **`bash` POSIX process-group kill** with `SIGTERM → 800 ms → SIGKILL`. Runaway grandchildren can't survive the timeout.
89
107
  - **`fetch` SSRF defenses**: numeric CIDR checks for IPv4 (`10/8`, `127/8`, `169.254/16`, `100.64/10`, `224/4`, `240/4`, …) and 8-group-expanded IPv6 (including Node's compressed `::ffff:7f00:1` form for v4-mapped addresses). Redirect target re-validated on every hop. `http://` downgrade refused.
108
+ - **`browser_*` isolation and SSRF defenses**: one context per agent/session, private-host and URL-credential rejection, guarded subrequests, disabled downloads, project-root-confined uploads, bounded/redacted evidence, and abort/run-disposal cleanup.
90
109
  - **`patch` diff-target validation**: every `+++` target post-strip is resolved against `projectRoot` before GNU patch sees the diff. `strip` clamped to `≥1`. Temp diff written to a `0700 mkdtemp` directory. Run with `LANG=C` / `LC_ALL=C`.
91
110
  - **`replace` / `grep` symlink-safe**: lstat + realpath + projectRoot revalidation; symlinks skipped, not followed.
92
111
  - **User-regex ReDoS guard** (`_regex.ts`): 512-char cap, nested-quantifier rejection (`(a+)+`, `(?:x+)*`), 64 KB subject-line cap. Applied to `grep`, `replace`, `logs`.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Bounded-concurrency async map.
3
+ *
4
+ * Runs `fn` over `items` with at most `limit` in-flight promises. Order of
5
+ * results matches input order. Errors reject the returned promise on the first
6
+ * failure — the remaining in-flight work is not awaited; this matches
7
+ * `Promise.all` semantics for callers that want fail-fast behavior.
8
+ *
9
+ * Lives here (rather than imported from `@wrongstack/bench`) because:
10
+ * 1. tools sits below bench in the dependency graph — bench is a consumer of
11
+ * tools, never the other way around.
12
+ * 2. The semantics used by grep/glob are deliberately simple (fail-fast,
13
+ * no cancellation, no progress) and don't need bench's richer options.
14
+ */
15
+ export declare function mapWithConcurrency<T, R>(items: readonly T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]>;
16
+ //# sourceMappingURL=_concurrency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_concurrency.d.ts","sourceRoot":"","sources":["../src/_concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC3C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,EAAE,CAAC,CAgBd"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Heuristic danger detection for `exec` tool commands.
3
+ *
4
+ * Layered on top of `BLOCKED_ARG_PATTERNS` (which is a hard-deny list for
5
+ * clear sandbox escapes) and `bash-kill-guard.ts` (which protects WrongStack
6
+ * itself from kill). This module assigns a danger level to a command/arg
7
+ * pair so the caller can decide whether to:
8
+ *
9
+ * - 'safe' → execute normally
10
+ * - 'caution' → execute and emit a warning line to the tool output
11
+ * - 'destructive' → route through the existing confirm flow
12
+ * (`execTool.permission === 'confirm'`) instead of
13
+ * hard-deny, so the user can still proceed if intentional
14
+ *
15
+ * Design constraints:
16
+ * - Deterministic: no randomness, no I/O, no time. Same input → same output.
17
+ * - No LLM calls. Patterns are regex / exact-match.
18
+ * - Per-rule `id` so config can override specific rules via
19
+ * `tools.exec.danger.bypass`.
20
+ * - Reasons are human-readable, joined with "; " for the confirm prompt.
21
+ *
22
+ * Caution rules are deliberately permissive — they execute and emit a
23
+ * warning rather than blocking. The rationale: many of these patterns
24
+ * (python -c, sudo) are part of legitimate dev workflows,
25
+ * so a hard deny would block too much. A warning gives the user a
26
+ * chance to notice "wait, I didn't mean to do that" without forcing
27
+ * them to add a config override for every script.
28
+ */
29
+ export type DangerLevel = 'safe' | 'caution' | 'destructive';
30
+ export interface DangerAssessment {
31
+ level: DangerLevel;
32
+ reasons: string[];
33
+ /** Stable id of the matched rule, for tests and config-override. */
34
+ matchedRule?: string;
35
+ }
36
+ /**
37
+ * Evaluate the danger level of a (cmd, args) pair.
38
+ *
39
+ * Returns 'safe' if no rule fires, otherwise the highest level among all
40
+ * matching rules. The 'matchedRule' field is the *last* rule that fired
41
+ * (stable, since rules are evaluated in declaration order).
42
+ *
43
+ * Optional `bypass` argument: a set of rule ids that should be SKIPPED
44
+ * even if they would otherwise match. Wired from
45
+ * `config.tools.exec.danger.bypass` (see `ExecDangerConfig` in
46
+ * `@wrongstack/core/src/types/config.ts`). Unknown ids are silently
47
+ * ignored — forward-compat: a rule added in a future version can be
48
+ * referenced before the user upgrades their config schema.
49
+ *
50
+ * This function is the single source of truth for danger classification;
51
+ * it is pure (no side effects) and unit-tested in `danger-detect.test.ts`.
52
+ */
53
+ export declare function detectDanger(cmd: string, args: readonly string[], bypass?: ReadonlySet<string>): DangerAssessment;
54
+ //# sourceMappingURL=_danger-detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_danger-detect.d.ts","sourceRoot":"","sources":["../src/_danger-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA2TD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,gBAAgB,CAqBlB"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Matching ladder for the `edit` tool.
3
+ *
4
+ * Models routinely hallucinate whitespace when reproducing code they read
5
+ * many turns ago: trailing spaces appear, indentation shifts by a level,
6
+ * tabs become spaces. A strict exact-match `old_string` fails on all of
7
+ * these even though the intended target is unambiguous. This module
8
+ * implements a graded fallback chain — each tier is strictly more permissive
9
+ * and strictly lower-confidence than the previous one:
10
+ *
11
+ * 1. `exact` — substring match (the classic behavior).
12
+ * 2. `trailing-whitespace` — whole-line window match ignoring trailing
13
+ * whitespace per line. Zero ambiguity risk
14
+ * beyond exact (trailing blanks never carry
15
+ * meaning in the languages we edit).
16
+ * 3. `whitespace-normalized` — whole-line window match ignoring leading
17
+ * AND trailing whitespace per line. The
18
+ * replacement is re-indented to the file's
19
+ * actual indentation.
20
+ * 4. `fuzzy` — block-anchor match: first and last lines
21
+ * must match (trimmed), interior lines are
22
+ * compared by Levenshtein similarity. Only a
23
+ * single candidate with a clear margin over
24
+ * the runner-up is accepted.
25
+ *
26
+ * The caller (edit.ts) enforces per-tier uniqueness, restricts `replace_all`
27
+ * to tiers 1–2, and surfaces the tier + confidence in the tool output so the
28
+ * model and the session log both see when a fallback fired.
29
+ */
30
+ export type MatchTier = 'exact' | 'trailing-whitespace' | 'whitespace-normalized' | 'fuzzy';
31
+ export interface LadderMatch {
32
+ /** Char offset (inclusive) of the match start in the LF-normalized file. */
33
+ start: number;
34
+ /** Char offset (exclusive) of the match end. */
35
+ end: number;
36
+ /** 1-based line number of the first matched line (for error messages). */
37
+ startLine: number;
38
+ }
39
+ export interface LadderResult {
40
+ tier: MatchTier;
41
+ matches: LadderMatch[];
42
+ /** Similarity score of the best candidate — only set for the fuzzy tier. */
43
+ score?: number | undefined;
44
+ /**
45
+ * Fuzzy tier only: true when two candidates scored within the ambiguity
46
+ * margin of each other, so no single target can be trusted. `matches`
47
+ * then contains the rival candidates for the error message.
48
+ */
49
+ ambiguous?: boolean | undefined;
50
+ }
51
+ /** Human-readable label per tier, used in notes and error messages. */
52
+ export declare const TIER_LABEL: Record<MatchTier, string>;
53
+ /** Confidence per tier, recorded in the tool output note. */
54
+ export declare const TIER_CONFIDENCE: Record<MatchTier, 'exact' | 'high' | 'medium' | 'low'>;
55
+ /**
56
+ * Walk the ladder and return the first tier that produces at least one
57
+ * match. Returns `undefined` when no tier matches.
58
+ */
59
+ export declare function findLadderMatches(fileLf: string, oldLf: string): LadderResult | undefined;
60
+ /** Normalized similarity in [0, 1]: 1 − levenshtein / max-length. */
61
+ export declare function similarity(a: string, b: string): number;
62
+ /** Leading whitespace (spaces/tabs) of the first non-empty line, or ''. */
63
+ export declare function firstLineIndent(text: string): string;
64
+ /**
65
+ * Shift `newLf` from the needle's indentation to the matched block's actual
66
+ * indentation. Only clean prefix relationships are adjusted (purely adding
67
+ * or removing leading whitespace); a tabs-vs-spaces mismatch is left alone
68
+ * rather than guessed at — the diff in the tool output makes the result
69
+ * visible either way.
70
+ */
71
+ export declare function adjustIndent(newLf: string, fromIndent: string, toIndent: string): {
72
+ text: string;
73
+ adjusted: boolean;
74
+ };
75
+ /**
76
+ * Best-effort locator for the no-match error: find the window whose trimmed
77
+ * lines are most similar to the needle (cheap common-prefix scoring — this
78
+ * runs on the failure path where the needle by definition doesn't match, so
79
+ * precision matters less than never being slow). Returns the 1-based line
80
+ * and a short snippet the model can use to correct itself without re-reading.
81
+ */
82
+ export declare function nearestMatchHint(fileLf: string, oldLf: string): {
83
+ line: number;
84
+ snippet: string;
85
+ } | undefined;
86
+ //# sourceMappingURL=_edit-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_edit-match.d.ts","sourceRoot":"","sources":["../src/_edit-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,qBAAqB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AAE5F,MAAM,WAAW,WAAW;IAC1B,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACjC;AAED,uEAAuE;AACvE,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMhD,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAKlF,CAAC;AAYF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CA+BzF;AA6GD,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAUvD;AAyBD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAkBrC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA0B/C"}
package/dist/_env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Re-exported from @wrongstack/core. This file exists for backward
3
+ * compatibility — tools/src/bash.ts and exec.ts import './_env.js'.
4
+ */
5
+ export { buildChildEnv } from '@wrongstack/core';
6
+ //# sourceMappingURL=_env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_env.d.ts","sourceRoot":"","sources":["../src/_env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,31 @@
1
+ export declare const ALLOW_PRIVATE: boolean;
2
+ type LookupCallback = (err: NodeJS.ErrnoException | null, address?: string | Array<{
3
+ address: string | undefined;
4
+ family: number;
5
+ }>, family?: number | undefined) => void;
6
+ /**
7
+ * DNS lookup used by the undici dispatcher below. It performs the SINGLE name
8
+ * resolution that the TCP connection actually uses, and rejects if any
9
+ * resolved address is private/loopback/link-local. Because the connection
10
+ * reuses exactly this result, there is no DNS-rebinding TOCTOU window between
11
+ * the security check and the connect — closing the gap the old code documented
12
+ * (validate with one dns.lookup, then let fetch re-resolve independently).
13
+ * TLS still validates the certificate against the hostname (SNI is set by
14
+ * undici from the URL), so pinning the IP does not weaken cert checking.
15
+ */
16
+ export declare function guardedLookup(hostname: string, options: {
17
+ all?: boolean | undefined;
18
+ family?: number | undefined;
19
+ }, callback: LookupCallback): void;
20
+ /**
21
+ * SSRF-guarded fetch with manual, per-hop-revalidated redirects, exported so
22
+ * other builtin tools (e.g. `search`) get the same protections instead of a
23
+ * weaker `redirect: 'follow'`. Every hop is re-checked against private/loopback
24
+ * ranges and the connection is pinned to the validated IP via the undici
25
+ * dispatcher (no DNS-rebinding TOCTOU). `headers` defaults to the plain `fetch`
26
+ * tool's; callers may override (e.g. a browser User-Agent for search engines).
27
+ */
28
+ export declare function guardedFetch(url: string, maxRedirects: number, signal: AbortSignal, headers?: Record<string, string>): Promise<Response>;
29
+ export declare function assertNotPrivate(hostname: string): Promise<void>;
30
+ export {};
31
+ //# sourceMappingURL=_fetch-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_fetch-guard.d.ts","sourceRoot":"","sources":["../src/_fetch-guard.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,aAAa,SAAwD,CAAC;AAUnF,KAAK,cAAc,GAAG,CACpB,GAAG,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,EACjC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACzE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,KACxB,IAAI,CAAC;AAEV;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EACnE,QAAQ,EAAE,cAAc,GACvB,IAAI,CAsCN;AAwCD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG7B,GACA,OAAO,CAAC,QAAQ,CAAC,CAuDnB;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDtE"}
@@ -0,0 +1,38 @@
1
+ /** Directory for spooled command output (under the wstack global root). */
2
+ export declare function toolOutputDir(): string;
3
+ /** Reset module state — test hook (per-process sweep memo + nothing else). */
4
+ export declare function _resetOutputSpoolForTests(): void;
5
+ export interface SpoolInfo {
6
+ /** Absolute path of the spool file. */
7
+ path: string;
8
+ /** Total bytes of output produced (including what reached the file). */
9
+ bytes: number;
10
+ /** Bytes dropped due to disk backpressure (0 in the normal case). */
11
+ droppedBytes: number;
12
+ }
13
+ export interface OutputSpool {
14
+ /** Feed every raw output chunk. Never throws. */
15
+ write(text: string): void;
16
+ /**
17
+ * Close the file (if one was opened) and return its info, or null when the
18
+ * output never exceeded the threshold. Idempotent.
19
+ */
20
+ finalize(): SpoolInfo | null;
21
+ }
22
+ export interface CreateOutputSpoolOptions {
23
+ /** Tool name used in the spool filename (sanitized). */
24
+ tool: string;
25
+ /**
26
+ * Output size at which the spool activates. Should match the tool's
27
+ * in-memory cap so files are only created for output the model can't
28
+ * already see in full. Default 32 KB.
29
+ */
30
+ thresholdBytes?: number | undefined;
31
+ }
32
+ /**
33
+ * Render the marker line appended to a capped tool result. Kept in one place
34
+ * so every command tool phrases it identically (and tests can match it).
35
+ */
36
+ export declare function spoolNote(info: SpoolInfo): string;
37
+ export declare function createOutputSpool(opts: CreateOutputSpoolOptions): OutputSpool;
38
+ //# sourceMappingURL=_output-spool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_output-spool.d.ts","sourceRoot":"","sources":["../src/_output-spool.ts"],"names":[],"mappings":"AAmCA,2EAA2E;AAC3E,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAwBD,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,QAAQ,IAAI,SAAS,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAIjD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,WAAW,CA+E7E"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns a display-safe copy of `cmd` with sensitive flag values replaced by [REDACTED].
3
+ * The original string is unchanged; this is pure and has no side effects.
4
+ */
5
+ export declare function redactCommand(cmd: string): string;
6
+ //# sourceMappingURL=_redact-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_redact-command.d.ts","sourceRoot":"","sources":["../src/_redact-command.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoBjD"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Compile a user-supplied regex with conservative bounds against ReDoS.
3
+ *
4
+ * Node's regex engine (V8) is backtracking-based and cannot interrupt a
5
+ * synchronous match — a pattern like `(a+)+$` against a sufficiently long
6
+ * line will pin a worker for seconds. The executor's outer `timeoutMs` only
7
+ * fires between async boundaries, so a long regex eval inside a sync loop
8
+ * is uninterruptible.
9
+ *
10
+ * We can't fully prevent ReDoS without an alternative engine (re2-wasm), but
11
+ * we can sharply limit the blast radius:
12
+ *
13
+ * 1. Cap pattern length — practically all legitimate user patterns are
14
+ * under 256 characters. A 4 KB pattern is almost certainly malicious
15
+ * or a copy-paste accident.
16
+ * 2. Reject patterns containing the most obvious super-linear structures.
17
+ * This is a coarse filter (false-positives are likely; we accept that
18
+ * for hostile-input contexts).
19
+ *
20
+ * Callers should additionally bound the *subject* length (e.g. by capping
21
+ * line size before matching).
22
+ */
23
+ export interface CompileResult {
24
+ ok: true;
25
+ regex: RegExp;
26
+ }
27
+ export interface CompileFail {
28
+ ok: false;
29
+ reason: string;
30
+ }
31
+ export declare function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail;
32
+ /**
33
+ * Truncate a subject line to a safe length for synchronous regex eval.
34
+ * The cap is conservative; tools that need exact-line matching against very
35
+ * long lines should use ripgrep externally rather than the native walker.
36
+ */
37
+ export declare const MAX_SUBJECT_LEN: number;
38
+ export declare function capSubject(line: string): string;
39
+ //# sourceMappingURL=_regex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_regex.d.ts","sourceRoot":"","sources":["../src/_regex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAkBH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,WAAW,CA2B5F;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAY,CAAC;AAEzC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Session-shell resolution for Windows.
3
+ *
4
+ * `pickShell` (`_shell-pick.ts`) decides the shell *per command* — it inspects
5
+ * each command string and routes PowerShell-looking work to PowerShell, else
6
+ * `cmd.exe`. That keeps individual commands working, but it means the shell the
7
+ * model is *told* about (the system-prompt Environment block) and the shell that
8
+ * actually runs a given command can disagree, and a plain command like
9
+ * `git status` silently lands in `cmd.exe` while `Get-ChildItem` lands in
10
+ * PowerShell. The model has no stable target to write syntax for.
11
+ *
12
+ * This module resolves ONE shell for the whole session and pins it via the
13
+ * `WRONGSTACK_SHELL` env var. Because `WRONGSTACK_SHELL` is already the top
14
+ * precedence in `pickShell`, pinning it makes every command route to the same
15
+ * shell with zero changes to `bash.ts` — and the system-prompt builder can read
16
+ * the same env var to tell the model exactly which shell + syntax to use.
17
+ *
18
+ * Resolution (Windows only — POSIX has no fixed session shell here; `bash.ts`
19
+ * routes through `/bin/bash -c` and honours an explicit `WRONGSTACK_SHELL` as a
20
+ * binary path):
21
+ * 1. A valid user-set `WRONGSTACK_SHELL` (cmd | powershell | pwsh) wins and is
22
+ * left untouched.
23
+ * 2. Otherwise prefer `pwsh` (PowerShell 7+) when `pwsh.exe` is on PATH.
24
+ * 3. Otherwise `powershell` (Windows PowerShell 5.1).
25
+ * 4. Otherwise `cmd` (only when no PowerShell is installed — effectively never
26
+ * on a real Windows box).
27
+ *
28
+ * @see {@link ../../docs/configuration.md#windows-shell-selection-wrongstackshell}
29
+ */
30
+ import type { BashShell } from './_shell-pick.js';
31
+ /**
32
+ * Map a raw `WRONGSTACK_SHELL` value (which may carry a `.exe` suffix or odd
33
+ * casing) to a canonical {@link BashShell}, or `undefined` when it is unset /
34
+ * not a known shell. Mirrors the override parsing in `pickShell`.
35
+ */
36
+ export declare function normalizeShell(value: string | undefined): BashShell | undefined;
37
+ export interface ResolveSessionShellDeps {
38
+ /**
39
+ * Returns true when `bin` (e.g. `'pwsh.exe'`) resolves on PATH. Injectable so
40
+ * the resolver is unit-testable off-Windows; defaults to `resolveWin32Command`
41
+ * (which itself short-circuits to "not found" when `process.platform` is not
42
+ * win32).
43
+ */
44
+ hasBinary?: ((bin: string) => boolean) | undefined;
45
+ }
46
+ /**
47
+ * Pure decision: which single shell should the session use on `platform`?
48
+ * Returns `undefined` on non-win32 (no fixed session shell). Does not mutate
49
+ * anything.
50
+ */
51
+ export declare function resolveSessionShell(platform: NodeJS.Platform, env: {
52
+ get(key: string): string | undefined;
53
+ }, deps?: ResolveSessionShellDeps): BashShell | undefined;
54
+ export interface EnsureSessionShellOptions {
55
+ /** Env to read/mutate. Defaults to `process.env`. */
56
+ env?: NodeJS.ProcessEnv | undefined;
57
+ /** Platform override (tests). Defaults to `process.platform`. */
58
+ platform?: NodeJS.Platform | undefined;
59
+ /** Binary-presence probe (tests). Forwarded to {@link resolveSessionShell}. */
60
+ hasBinary?: ((bin: string) => boolean) | undefined;
61
+ }
62
+ /**
63
+ * Resolve the session shell and pin it into `WRONGSTACK_SHELL` so the bash tool
64
+ * and the system-prompt builder agree on one stable value. Idempotent:
65
+ *
66
+ * - non-win32 → no-op, returns `undefined`.
67
+ * - a valid user-set `WRONGSTACK_SHELL` → returned as-is, env left untouched.
68
+ * - otherwise → resolves, writes the canonical value into the env, returns it.
69
+ *
70
+ * Call this once at process boot, before the system-prompt builder is built.
71
+ */
72
+ export declare function ensureSessionShell(opts?: EnsureSessionShellOptions): BashShell | undefined;
73
+ //# sourceMappingURL=_session-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_session-shell.d.ts","sourceRoot":"","sources":["../src/_session-shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAM/E;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;CACpD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,GAAG,EAAE;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,EAC7C,IAAI,GAAE,uBAA4B,GACjC,SAAS,GAAG,SAAS,CAYvB;AAED,MAAM,WAAW,yBAAyB;IACxC,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC;IACvC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;CACpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,yBAA8B,GAAG,SAAS,GAAG,SAAS,CAa9F"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Shell picker for the `bash` tool on Windows.
3
+ *
4
+ * Historically the bash tool always fell back to `cmd.exe` on Windows — a
5
+ * reasonable default for the small set of POSIX-style commands the tool was
6
+ * built around (`echo`, `dir`, `cd`, `set`, etc.). That breaks when the
7
+ * caller emits PowerShell-style commands (e.g. Codex on Windows routinely
8
+ * emits `Get-Content`, `Get-ChildItem`, `Set-Location`, …), which `cmd.exe`
9
+ * rejects with "'Get-Content' is not recognized as an internal or external
10
+ * command, operable program or batch file." This module decides — purely from
11
+ * the command string and a few well-known env vars — which shell should run
12
+ * the command. It returns a tagged value; bash.ts does the actual spawn.
13
+ *
14
+ * The picker never spawns anything itself, so it is safe to unit-test in
15
+ * isolation. Shell *resolution* (finding the actual binary on PATH) lives in
16
+ * `_win32-resolve.ts` and runs at spawn time.
17
+ * @see {@link ../../docs/configuration.md#windows-shell-selection-wrongstackshell} for user-facing documentation of the WRONGSTACK_SHELL env var and auto-detection behavior.
18
+ *
19
+ * Selection precedence (Windows only):
20
+ * 1. `WRONGSTACK_SHELL` env var, if it names a known shell (cmd | powershell
21
+ * | pwsh). This is the override for users who want a fixed shell.
22
+ * 2. Auto-detect: if the command "looks like" PowerShell — i.e. uses cmdlet
23
+ * verb-noun syntax, $-variables, subexpressions, here-strings, etc. —
24
+ * route to PowerShell. Prefers `pwsh` (PowerShell 7+) and falls back to
25
+ * `powershell` (Windows PowerShell 5.1) at spawn time.
26
+ * 3. Default: `cmd.exe` (preserves legacy behavior).
27
+ *
28
+ * On non-Windows the picker is a no-op — bash.ts already routes through
29
+ * `/bin/bash -c`. We return `'cmd'` as a sentinel value that means "the
30
+ * platform default"; bash.ts maps it to the right binary.
31
+ *
32
+ * See docs/configuration.md § "Windows shell selection (WRONGSTACK_SHELL)" for
33
+ * user-facing documentation of the env var and auto-detection behaviour.
34
+ */
35
+ export type BashShell = 'cmd' | 'powershell' | 'pwsh';
36
+ /** Sentinel returned on POSIX — bash.ts maps this to `/bin/bash -c`. */
37
+ export declare const POSIX_DEFAULT: BashShell;
38
+ export interface PickShellEnv {
39
+ /** Read-only env view. Tests pass `{ get: (k) => process.env[k] }`. */
40
+ get(key: string): string | undefined;
41
+ }
42
+ /**
43
+ * Decide which shell should execute `command` on `platform`. Pure function —
44
+ * no I/O, no side effects, no exceptions (invalid input returns the default).
45
+ */
46
+ export declare function pickShell(platform: NodeJS.Platform, command: string, env: PickShellEnv): BashShell;
47
+ /**
48
+ * Heuristic PowerShell detector. Conservative on purpose — false positives
49
+ * route `cmd.exe` work into PowerShell (different parsing rules, different
50
+ * exit-code semantics, sometimes very different stdout), which is more
51
+ * disruptive than a single "command not recognized" error. We only return
52
+ * true for patterns that are unambiguously PowerShell.
53
+ *
54
+ * Detected patterns:
55
+ * - cmdlet verb-noun syntax (`Get-`, `Set-`, `New-`, `Remove-`, `Add-`,
56
+ * `Clear-`, `Copy-`, `Move-`, `Rename-`, `Test-`, `Update-`, `Write-`,
57
+ * `Read-`, `Push-`, `Pop-`, `Invoke-`, `Start-`, `Stop-`, `Wait-`,
58
+ * `Out-`, `Format-`, `Group-`, `Measure-`, `Compare-`, `Resolve-`,
59
+ * `ConvertTo-`, `ConvertFrom-`, `Import-`, `Export-`, `Select-`,
60
+ * `Where-`, `ForEach-`, `Sort-`, `Tee-`, `Split-`, `Join-`, `Limit-`,
61
+ * `Skip-`, `Step-`, `Trace-`, `Debug-`, `Register-`, `Unregister-`,
62
+ * `Enable-`, `Disable-`, `Restart-`, `Suspend-`, `Resume-`, `Save-`,
63
+ * `Open-`, `Close-`, `Lock-`, `Unlock-`, `Mount-`, `Dismount-`,
64
+ * `Enter-`, `Exit-`, `Use-`, `Show-`, `Hide-`, `Find-`, `Search-`,
65
+ * `Watch-`, `Initialize-`, `Optimize-`, `Compress-`, `Expand-`,
66
+ * `Convert-`, `Merge-`, `Checkpoint-`, `Undo-`, `Redo-`, `Approve-`,
67
+ * `Deny-`, `Block-`, `Grant-`, `Revoke-`, `Assert-`, `Confirm-`,
68
+ * `Resolve-`, `Wait-`, `Receive-`, `Send-`, `Connect-`, `Disconnect-`,
69
+ * `Read-`, `Write-`).
70
+ * - $-prefixed variables (`$env:`, `$foo`, `$script:bar`, `$_`).
71
+ * - Subexpressions (`$(...)`).
72
+ * - Here-strings (`@"…"@`, `@'…'@`).
73
+ * - Splatting (`@(...)` at start of argument, `@{}` hash table).
74
+ * - PowerShell-comparison operators (`-eq`, `-ne`, `-lt`, `-gt`, `-le`,
75
+ * `-ge`, `-like`, `-notlike`, `-match`, `-notmatch`, `-contains`,
76
+ * `-in`, `-and`, `-or`, `-not`, `-band`, `-bor`, `-bxor`,
77
+ * `-replace`, `-split`, `-join`, `-is`, `-as`, `-f`).
78
+ * - `.ps1` extension mentioned in the command.
79
+ * - `&` call operator followed by a `$`-variable (`& $cmd ...`).
80
+ * - Common PowerShell aliases that don't exist in cmd.exe: `gci`, `gi`,
81
+ * `gp`, `sl`, `cd` is ambiguous (cmd.exe has `cd` too — skip), `ls`
82
+ * ambiguous (cmd.exe does NOT have `ls`), `cat`, `cp`, `mv`, `rm`,
83
+ * `echo` (echo is in cmd too), `gcm`, `gci`, `gps`, `ps`, `select`,
84
+ * `where`, `?`, `%`.
85
+ * - `#requires` directive at start of script.
86
+ * - `param()` block at start of script.
87
+ *
88
+ * The Windows-style path `C:\` alone is not a PowerShell tell — both
89
+ * shells accept it. We only flip on PS-specific syntax.
90
+ *
91
+ * The function is case-insensitive and tolerates leading whitespace.
92
+ */
93
+ export declare function looksLikePowerShell(command: string): boolean;
94
+ /**
95
+ * Extended PowerShell detection — patterns that are unambiguous but less
96
+ * common than the verb-noun cmdlets caught by the first pass.
97
+ *
98
+ * Covers: -WhatIf / -Confirm / -ErrorAction flags, trailing `2>&1` in
99
+ * combination with other PS signals, `[type]` casts, Where/ForEach/
100
+ * Select-Object pipeline, PS comment blocks, Write-* output cmdlets,
101
+ * registry paths (HKLM:/HKCU:), -AsPlainText, -PipelineVariable.
102
+ */
103
+ export declare function looksLikePowerShellExtended(command: string): boolean;
104
+ /**
105
+ * Wrap a user command for execution via `pwsh -Command -` (stdin pipe).
106
+ *
107
+ * The wrapper addresses four reliability gaps in Windows PowerShell:
108
+ *
109
+ * 1. **Console output encoding**: PS 5.1 outputs in the system codepage
110
+ * (often Windows-1252) which mojibakes non-ASCII. Setting
111
+ * `[Console]::OutputEncoding = [System.Text.Encoding]::UTF8` at the
112
+ * top of the script ensures UTF-8 output in both editions.
113
+ *
114
+ * 2. **Exit-code propagation**: Native commands (dotnet, npm, node, etc.)
115
+ * set `$LASTEXITCODE`. Without explicit propagation, `pwsh -Command -`
116
+ * exits 0 even when the native command failed. Running the command body
117
+ * and then exiting with `$LASTEXITCODE` when it is numeric preserves native
118
+ * exit codes without swallowing PowerShell output.
119
+ *
120
+ * 3. **Confirmation suppression**: `$ConfirmPreference='None'` suppresses
121
+ * interactive confirmation prompts (e.g. `-Confirm` cmdlets) so scripts
122
+ * don't block waiting for user input. `$WhatIfPreference=$false` ensures
123
+ * commands actually RUN (not just print what they would do).
124
+ *
125
+ * Do not prefix the stdin script with a UTF-8 BOM. Some PowerShell builds do
126
+ * not strip it when reading `-Command -` and treat it as part of the first
127
+ * command name (`\uFEFF[Console]::OutputEncoding`), causing every wrapped
128
+ * command to fail before the user's script runs.
129
+ */
130
+ export declare function wrapPowerShellScript(command: string): string;
131
+ /**
132
+ * Return the argv prefix for a given shell. The bash tool passes a single
133
+ * command string and expects the shell to interpret it. cmd.exe uses
134
+ * `/c <cmd>`; PowerShell uses `-NoLogo -NoProfile -NonInteractive -Command -`
135
+ * and reads the script from stdin (the `-` at the end is the documented way
136
+ * to tell PowerShell "the script is on stdin, not as an argument"). Stdin
137
+ * pipe sidesteps the entire class of quoting bugs that arise from
138
+ * interpolating multi-line / single-quoted / dollar-sign-laden scripts into
139
+ * a `-Command "..."` string.
140
+ */
141
+ export declare function shellArgs(shell: BashShell): string[];
142
+ /**
143
+ * Post-failure diagnosis: when a Windows shell command exits non-zero, scan it
144
+ * for bash/POSIX idioms the shell does not accept and return a short, targeted
145
+ * hint (with the right replacement) so the model self-corrects on the next turn.
146
+ *
147
+ * Deliberately **failure-coupled** — the bash tool only calls this on a
148
+ * non-zero exit. That keeps it noise-free (PowerShell aliases `ls`/`cat`/`rm`
149
+ * succeed, so they never reach here) and side-steps shell-version nuance: `&&`
150
+ * works in PowerShell 7 and cmd.exe (so those commands succeed and are never
151
+ * flagged) but breaks in Windows PowerShell 5.1 (where the command fails and is
152
+ * flagged). Never mutates or blocks — advisory only.
153
+ *
154
+ * Returns `undefined` when nothing actionable is found (including any POSIX
155
+ * call — bash idioms are correct there).
156
+ */
157
+ export declare function diagnoseBashism(command: string, shell: BashShell): string | undefined;
158
+ //# sourceMappingURL=_shell-pick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_shell-pick.d.ts","sourceRoot":"","sources":["../src/_shell-pick.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,YAAY,GAAG,MAAM,CAAC;AAEtD,wEAAwE;AACxE,eAAO,MAAM,aAAa,EAAE,SAAiB,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,YAAY,GAChB,SAAS,CAgBX;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA2D5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAuCpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU5D;AAyCD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,CAKpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAgDrF"}
@@ -0,0 +1,32 @@
1
+ import type { ToolProgressEvent } from '@wrongstack/core';
2
+ export interface SpawnStreamResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ exitCode: number;
6
+ truncated: boolean;
7
+ error?: string | undefined;
8
+ /** When the output exceeded maxBytes, the FULL output was spooled here. */
9
+ spoolPath?: string | undefined;
10
+ /** Total output bytes produced (only set when spooled). */
11
+ spoolBytes?: number | undefined;
12
+ }
13
+ export interface SpawnStreamOptions {
14
+ cmd: string;
15
+ args: string[];
16
+ cwd: string;
17
+ signal: AbortSignal;
18
+ maxBytes?: number | undefined;
19
+ /** Bytes of new stdout/stderr to accumulate before yielding a `partial_output` event. */
20
+ flushBytes?: number | undefined;
21
+ /** Maximum chunks to buffer before applying backpressure to the child. Default 500. */
22
+ maxQueueSize?: number | undefined;
23
+ }
24
+ /**
25
+ * Spawn a child process and yield `partial_output` progress events as
26
+ * stdout/stderr arrive (batched by byte threshold), then return the full
27
+ * buffered result. Shared between install/lint/format/typecheck/test/audit
28
+ * so the TUI live tail sees consistent progress regardless of which tool
29
+ * is running.
30
+ */
31
+ export declare function spawnStream(opts: SpawnStreamOptions): AsyncGenerator<ToolProgressEvent, SpawnStreamResult>;
32
+ //# sourceMappingURL=_spawn-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_spawn-stream.d.ts","sourceRoot":"","sources":["../src/_spawn-stream.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAS1D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAuB,WAAW,CAChC,IAAI,EAAE,kBAAkB,GACvB,cAAc,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAuNtD"}