mindweave 0.0.1 → 2.1.2

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 (481) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +171 -14
  3. package/dist/alternator/chassis/cache.js +47 -0
  4. package/dist/alternator/chassis/cache.js.map +1 -0
  5. package/dist/alternator/chassis/domRefs.js +53 -0
  6. package/dist/alternator/chassis/domRefs.js.map +1 -0
  7. package/dist/alternator/chassis/extractWorker.js +65 -0
  8. package/dist/alternator/chassis/extractWorker.js.map +1 -0
  9. package/dist/alternator/chassis/graph.js +221 -0
  10. package/dist/alternator/chassis/graph.js.map +1 -0
  11. package/dist/alternator/chassis/index.js +463 -0
  12. package/dist/alternator/chassis/index.js.map +1 -0
  13. package/dist/alternator/chassis/isolation.js +336 -0
  14. package/dist/alternator/chassis/isolation.js.map +1 -0
  15. package/dist/alternator/chassis/langCases.js +69 -0
  16. package/dist/alternator/chassis/langCases.js.map +1 -0
  17. package/dist/alternator/chassis/lsp.js +469 -0
  18. package/dist/alternator/chassis/lsp.js.map +1 -0
  19. package/dist/alternator/chassis/markup.js +262 -0
  20. package/dist/alternator/chassis/markup.js.map +1 -0
  21. package/dist/alternator/chassis/provision.js +198 -0
  22. package/dist/alternator/chassis/provision.js.map +1 -0
  23. package/dist/alternator/chassis/rank.js +72 -0
  24. package/dist/alternator/chassis/rank.js.map +1 -0
  25. package/dist/alternator/chassis/servers.js +215 -0
  26. package/dist/alternator/chassis/servers.js.map +1 -0
  27. package/dist/alternator/chassis/treesitter.js +608 -0
  28. package/dist/alternator/chassis/treesitter.js.map +1 -0
  29. package/dist/alternator/chassis/types.js +24 -0
  30. package/dist/alternator/chassis/types.js.map +1 -0
  31. package/dist/alternator/lane.js +67 -0
  32. package/dist/alternator/lane.js.map +1 -0
  33. package/dist/cli/App.js +2586 -0
  34. package/dist/cli/App.js.map +1 -0
  35. package/dist/cli/altScreen.js +67 -0
  36. package/dist/cli/altScreen.js.map +1 -0
  37. package/dist/cli/approvalChannel.js +72 -0
  38. package/dist/cli/approvalChannel.js.map +1 -0
  39. package/dist/cli/attachments.js +188 -0
  40. package/dist/cli/attachments.js.map +1 -0
  41. package/dist/cli/bootstrap.js +279 -0
  42. package/dist/cli/bootstrap.js.map +1 -0
  43. package/dist/cli/chatAnchor.js +94 -0
  44. package/dist/cli/chatAnchor.js.map +1 -0
  45. package/dist/cli/commandArgs.js +79 -0
  46. package/dist/cli/commandArgs.js.map +1 -0
  47. package/dist/cli/commandRoute.js +78 -0
  48. package/dist/cli/commandRoute.js.map +1 -0
  49. package/dist/cli/compaction.js +69 -0
  50. package/dist/cli/compaction.js.map +1 -0
  51. package/dist/cli/components/ApprovalBox.js +100 -0
  52. package/dist/cli/components/ApprovalBox.js.map +1 -0
  53. package/dist/cli/components/BlockView.js +128 -0
  54. package/dist/cli/components/BlockView.js.map +1 -0
  55. package/dist/cli/components/FirstRunFrame.js +41 -0
  56. package/dist/cli/components/FirstRunFrame.js.map +1 -0
  57. package/dist/cli/components/KeyManager.js +197 -0
  58. package/dist/cli/components/KeyManager.js.map +1 -0
  59. package/dist/cli/components/KeySetup.js +125 -0
  60. package/dist/cli/components/KeySetup.js.map +1 -0
  61. package/dist/cli/components/MenuFrame.js +24 -0
  62. package/dist/cli/components/MenuFrame.js.map +1 -0
  63. package/dist/cli/components/Picker.js +82 -0
  64. package/dist/cli/components/Picker.js.map +1 -0
  65. package/dist/cli/components/PromptInput.js +509 -0
  66. package/dist/cli/components/PromptInput.js.map +1 -0
  67. package/dist/cli/components/SubagentView.js +86 -0
  68. package/dist/cli/components/SubagentView.js.map +1 -0
  69. package/dist/cli/components/ToolGroup.js +60 -0
  70. package/dist/cli/components/ToolGroup.js.map +1 -0
  71. package/dist/cli/components/ToolLine.js +123 -0
  72. package/dist/cli/components/ToolLine.js.map +1 -0
  73. package/dist/cli/components/TrustGate.js +37 -0
  74. package/dist/cli/components/TrustGate.js.map +1 -0
  75. package/dist/cli/costView.js +40 -0
  76. package/dist/cli/costView.js.map +1 -0
  77. package/dist/cli/frameRate.js +30 -0
  78. package/dist/cli/frameRate.js.map +1 -0
  79. package/dist/cli/framebuffer/paint.js +165 -0
  80. package/dist/cli/framebuffer/paint.js.map +1 -0
  81. package/dist/cli/framebuffer/parse.js +266 -0
  82. package/dist/cli/framebuffer/parse.js.map +1 -0
  83. package/dist/cli/framebuffer/screen.js +196 -0
  84. package/dist/cli/framebuffer/screen.js.map +1 -0
  85. package/dist/cli/framebuffer/writer.js +139 -0
  86. package/dist/cli/framebuffer/writer.js.map +1 -0
  87. package/dist/cli/groupReveal.js +51 -0
  88. package/dist/cli/groupReveal.js.map +1 -0
  89. package/dist/cli/help.js +55 -0
  90. package/dist/cli/help.js.map +1 -0
  91. package/dist/cli/inputView.js +105 -0
  92. package/dist/cli/inputView.js.map +1 -0
  93. package/dist/cli/keyManager.js +65 -0
  94. package/dist/cli/keyManager.js.map +1 -0
  95. package/dist/cli/keySetup.js +35 -0
  96. package/dist/cli/keySetup.js.map +1 -0
  97. package/dist/cli/keyStore.js +73 -0
  98. package/dist/cli/keyStore.js.map +1 -0
  99. package/dist/cli/markdown.js +460 -0
  100. package/dist/cli/markdown.js.map +1 -0
  101. package/dist/cli/messageQueue.js +96 -0
  102. package/dist/cli/messageQueue.js.map +1 -0
  103. package/dist/cli/modes.js +90 -0
  104. package/dist/cli/modes.js.map +1 -0
  105. package/dist/cli/mouse.js +78 -0
  106. package/dist/cli/mouse.js.map +1 -0
  107. package/dist/cli/pasteAssembler.js +44 -0
  108. package/dist/cli/pasteAssembler.js.map +1 -0
  109. package/dist/cli/pathComplete.js +39 -0
  110. package/dist/cli/pathComplete.js.map +1 -0
  111. package/dist/cli/perfLog.js +90 -0
  112. package/dist/cli/perfLog.js.map +1 -0
  113. package/dist/cli/reveal.js +32 -0
  114. package/dist/cli/reveal.js.map +1 -0
  115. package/dist/cli/revealPace.js +59 -0
  116. package/dist/cli/revealPace.js.map +1 -0
  117. package/dist/cli/sessionCarry.js +19 -0
  118. package/dist/cli/sessionCarry.js.map +1 -0
  119. package/dist/cli/smoothScroll.js +74 -0
  120. package/dist/cli/smoothScroll.js.map +1 -0
  121. package/dist/cli/startupArgs.js +96 -0
  122. package/dist/cli/startupArgs.js.map +1 -0
  123. package/dist/cli/terminalRestore.js +34 -0
  124. package/dist/cli/terminalRestore.js.map +1 -0
  125. package/dist/cli/toolDisplay.js +219 -0
  126. package/dist/cli/toolDisplay.js.map +1 -0
  127. package/dist/cli/toolItems.js +65 -0
  128. package/dist/cli/toolItems.js.map +1 -0
  129. package/dist/cli/transcript.js +351 -0
  130. package/dist/cli/transcript.js.map +1 -0
  131. package/dist/cli/trust.js +79 -0
  132. package/dist/cli/trust.js.map +1 -0
  133. package/dist/cli/updateCheck.js +107 -0
  134. package/dist/cli/updateCheck.js.map +1 -0
  135. package/dist/cli/version.js +39 -0
  136. package/dist/cli/version.js.map +1 -0
  137. package/dist/cli/viewport.js +179 -0
  138. package/dist/cli/viewport.js.map +1 -0
  139. package/dist/cli/virtualWindow.js +103 -0
  140. package/dist/cli/virtualWindow.js.map +1 -0
  141. package/dist/cli/workingVerb.js +37 -0
  142. package/dist/cli/workingVerb.js.map +1 -0
  143. package/dist/cli/wrap.js +145 -0
  144. package/dist/cli/wrap.js.map +1 -0
  145. package/dist/drivers/anthropic/cachePoints.js +77 -0
  146. package/dist/drivers/anthropic/cachePoints.js.map +1 -0
  147. package/dist/drivers/anthropic/client.js +432 -0
  148. package/dist/drivers/anthropic/client.js.map +1 -0
  149. package/dist/drivers/anthropic/index.js +10 -0
  150. package/dist/drivers/anthropic/index.js.map +1 -0
  151. package/dist/drivers/anthropic/manifest.js +221 -0
  152. package/dist/drivers/anthropic/manifest.js.map +1 -0
  153. package/dist/drivers/cerebras/client.js +69 -0
  154. package/dist/drivers/cerebras/client.js.map +1 -0
  155. package/dist/drivers/cerebras/index.js +5 -0
  156. package/dist/drivers/cerebras/index.js.map +1 -0
  157. package/dist/drivers/cerebras/manifest.js +140 -0
  158. package/dist/drivers/cerebras/manifest.js.map +1 -0
  159. package/dist/drivers/clientId.js +41 -0
  160. package/dist/drivers/clientId.js.map +1 -0
  161. package/dist/drivers/contextOverflow.js +77 -0
  162. package/dist/drivers/contextOverflow.js.map +1 -0
  163. package/dist/drivers/deepseek/client.js +168 -0
  164. package/dist/drivers/deepseek/client.js.map +1 -0
  165. package/dist/drivers/deepseek/index.js +16 -0
  166. package/dist/drivers/deepseek/index.js.map +1 -0
  167. package/dist/drivers/deepseek/inlineTools.js +46 -0
  168. package/dist/drivers/deepseek/inlineTools.js.map +1 -0
  169. package/dist/drivers/deepseek/manifest.js +166 -0
  170. package/dist/drivers/deepseek/manifest.js.map +1 -0
  171. package/dist/drivers/gemini/client.js +38 -0
  172. package/dist/drivers/gemini/client.js.map +1 -0
  173. package/dist/drivers/gemini/index.js +5 -0
  174. package/dist/drivers/gemini/index.js.map +1 -0
  175. package/dist/drivers/gemini/manifest.js +150 -0
  176. package/dist/drivers/gemini/manifest.js.map +1 -0
  177. package/dist/drivers/glm/client.js +90 -0
  178. package/dist/drivers/glm/client.js.map +1 -0
  179. package/dist/drivers/glm/index.js +9 -0
  180. package/dist/drivers/glm/index.js.map +1 -0
  181. package/dist/drivers/glm/manifest.js +183 -0
  182. package/dist/drivers/glm/manifest.js.map +1 -0
  183. package/dist/drivers/groq/client.js +80 -0
  184. package/dist/drivers/groq/client.js.map +1 -0
  185. package/dist/drivers/groq/index.js +5 -0
  186. package/dist/drivers/groq/index.js.map +1 -0
  187. package/dist/drivers/groq/manifest.js +160 -0
  188. package/dist/drivers/groq/manifest.js.map +1 -0
  189. package/dist/drivers/kimi/client.js +82 -0
  190. package/dist/drivers/kimi/client.js.map +1 -0
  191. package/dist/drivers/kimi/index.js +9 -0
  192. package/dist/drivers/kimi/index.js.map +1 -0
  193. package/dist/drivers/kimi/manifest.js +168 -0
  194. package/dist/drivers/kimi/manifest.js.map +1 -0
  195. package/dist/drivers/meta/client.js +30 -0
  196. package/dist/drivers/meta/client.js.map +1 -0
  197. package/dist/drivers/meta/index.js +5 -0
  198. package/dist/drivers/meta/index.js.map +1 -0
  199. package/dist/drivers/meta/manifest.js +101 -0
  200. package/dist/drivers/meta/manifest.js.map +1 -0
  201. package/dist/drivers/minimax/client.js +36 -0
  202. package/dist/drivers/minimax/client.js.map +1 -0
  203. package/dist/drivers/minimax/index.js +5 -0
  204. package/dist/drivers/minimax/index.js.map +1 -0
  205. package/dist/drivers/minimax/manifest.js +120 -0
  206. package/dist/drivers/minimax/manifest.js.map +1 -0
  207. package/dist/drivers/mistral/client.js +54 -0
  208. package/dist/drivers/mistral/client.js.map +1 -0
  209. package/dist/drivers/mistral/index.js +5 -0
  210. package/dist/drivers/mistral/index.js.map +1 -0
  211. package/dist/drivers/mistral/manifest.js +118 -0
  212. package/dist/drivers/mistral/manifest.js.map +1 -0
  213. package/dist/drivers/openai/client.js +290 -0
  214. package/dist/drivers/openai/client.js.map +1 -0
  215. package/dist/drivers/openai/index.js +9 -0
  216. package/dist/drivers/openai/index.js.map +1 -0
  217. package/dist/drivers/openai/manifest.js +133 -0
  218. package/dist/drivers/openai/manifest.js.map +1 -0
  219. package/dist/drivers/openaiCompat/wire.js +493 -0
  220. package/dist/drivers/openaiCompat/wire.js.map +1 -0
  221. package/dist/drivers/partialTurn.js +23 -0
  222. package/dist/drivers/partialTurn.js.map +1 -0
  223. package/dist/drivers/providerError.js +181 -0
  224. package/dist/drivers/providerError.js.map +1 -0
  225. package/dist/drivers/qwen/client.js +93 -0
  226. package/dist/drivers/qwen/client.js.map +1 -0
  227. package/dist/drivers/qwen/index.js +9 -0
  228. package/dist/drivers/qwen/index.js.map +1 -0
  229. package/dist/drivers/qwen/manifest.js +146 -0
  230. package/dist/drivers/qwen/manifest.js.map +1 -0
  231. package/dist/drivers/registry.js +177 -0
  232. package/dist/drivers/registry.js.map +1 -0
  233. package/dist/drivers/retryPolicy.js +129 -0
  234. package/dist/drivers/retryPolicy.js.map +1 -0
  235. package/dist/drivers/searchBlocks.js +61 -0
  236. package/dist/drivers/searchBlocks.js.map +1 -0
  237. package/dist/drivers/types.js +2 -0
  238. package/dist/drivers/types.js.map +1 -0
  239. package/dist/drivers/xai/client.js +63 -0
  240. package/dist/drivers/xai/client.js.map +1 -0
  241. package/dist/drivers/xai/index.js +5 -0
  242. package/dist/drivers/xai/index.js.map +1 -0
  243. package/dist/drivers/xai/manifest.js +137 -0
  244. package/dist/drivers/xai/manifest.js.map +1 -0
  245. package/dist/dynamo/_probe.tmp.js +20 -0
  246. package/dist/dynamo/_probe.tmp.js.map +1 -0
  247. package/dist/dynamo/cacheBreak.js +88 -0
  248. package/dist/dynamo/cacheBreak.js.map +1 -0
  249. package/dist/dynamo/contextWindow.js +243 -0
  250. package/dist/dynamo/contextWindow.js.map +1 -0
  251. package/dist/dynamo/engine.js +2011 -0
  252. package/dist/dynamo/engine.js.map +1 -0
  253. package/dist/dynamo/guard.js +132 -0
  254. package/dist/dynamo/guard.js.map +1 -0
  255. package/dist/dynamo/liveMeter.js +58 -0
  256. package/dist/dynamo/liveMeter.js.map +1 -0
  257. package/dist/dynamo/model.js +172 -0
  258. package/dist/dynamo/model.js.map +1 -0
  259. package/dist/dynamo/planArtifact.js +125 -0
  260. package/dist/dynamo/planArtifact.js.map +1 -0
  261. package/dist/dynamo/pricing.js +140 -0
  262. package/dist/dynamo/pricing.js.map +1 -0
  263. package/dist/dynamo/prompt.js +264 -0
  264. package/dist/dynamo/prompt.js.map +1 -0
  265. package/dist/dynamo/spend.js +45 -0
  266. package/dist/dynamo/spend.js.map +1 -0
  267. package/dist/dynamo/verify.js +483 -0
  268. package/dist/dynamo/verify.js.map +1 -0
  269. package/dist/governor/forbidden.js +181 -0
  270. package/dist/governor/forbidden.js.map +1 -0
  271. package/dist/governor/freshness.js +83 -0
  272. package/dist/governor/freshness.js.map +1 -0
  273. package/dist/governor/frontmatter.js +38 -0
  274. package/dist/governor/frontmatter.js.map +1 -0
  275. package/dist/governor/glob.js +75 -0
  276. package/dist/governor/glob.js.map +1 -0
  277. package/dist/governor/index.js +87 -0
  278. package/dist/governor/index.js.map +1 -0
  279. package/dist/governor/rules.js +80 -0
  280. package/dist/governor/rules.js.map +1 -0
  281. package/dist/governor/scope.js +91 -0
  282. package/dist/governor/scope.js.map +1 -0
  283. package/dist/governor/skills.js +176 -0
  284. package/dist/governor/skills.js.map +1 -0
  285. package/dist/governor/types.js +16 -0
  286. package/dist/governor/types.js.map +1 -0
  287. package/dist/governor/write.js +182 -0
  288. package/dist/governor/write.js.map +1 -0
  289. package/dist/index.js +89 -0
  290. package/dist/index.js.map +1 -0
  291. package/dist/mcp/catalog.js +232 -0
  292. package/dist/mcp/catalog.js.map +1 -0
  293. package/dist/mcp/config.js +116 -0
  294. package/dist/mcp/config.js.map +1 -0
  295. package/dist/mcp/configWrite.js +254 -0
  296. package/dist/mcp/configWrite.js.map +1 -0
  297. package/dist/mcp/connection.js +504 -0
  298. package/dist/mcp/connection.js.map +1 -0
  299. package/dist/mcp/deferred.js +104 -0
  300. package/dist/mcp/deferred.js.map +1 -0
  301. package/dist/mcp/discover.js +128 -0
  302. package/dist/mcp/discover.js.map +1 -0
  303. package/dist/mcp/manager.js +679 -0
  304. package/dist/mcp/manager.js.map +1 -0
  305. package/dist/mcp/paramHeaders.js +163 -0
  306. package/dist/mcp/paramHeaders.js.map +1 -0
  307. package/dist/mcp/prompts.js +136 -0
  308. package/dist/mcp/prompts.js.map +1 -0
  309. package/dist/mcp/protocol.js +171 -0
  310. package/dist/mcp/protocol.js.map +1 -0
  311. package/dist/mcp/resources.js +119 -0
  312. package/dist/mcp/resources.js.map +1 -0
  313. package/dist/mcp/resultStore.js +180 -0
  314. package/dist/mcp/resultStore.js.map +1 -0
  315. package/dist/mcp/subscriptions.js +74 -0
  316. package/dist/mcp/subscriptions.js.map +1 -0
  317. package/dist/mcp/transport/headerValue.js +40 -0
  318. package/dist/mcp/transport/headerValue.js.map +1 -0
  319. package/dist/mcp/transport/http.js +321 -0
  320. package/dist/mcp/transport/http.js.map +1 -0
  321. package/dist/mcp/transport/stdio.js +234 -0
  322. package/dist/mcp/transport/stdio.js.map +1 -0
  323. package/dist/mcp/transport/types.js +27 -0
  324. package/dist/mcp/transport/types.js.map +1 -0
  325. package/dist/mcp/trust.js +98 -0
  326. package/dist/mcp/trust.js.map +1 -0
  327. package/dist/mcp/trustStore.js +55 -0
  328. package/dist/mcp/trustStore.js.map +1 -0
  329. package/dist/memory/autoMemory.js +340 -0
  330. package/dist/memory/autoMemory.js.map +1 -0
  331. package/dist/memory/compaction.js +513 -0
  332. package/dist/memory/compaction.js.map +1 -0
  333. package/dist/memory/images.js +173 -0
  334. package/dist/memory/images.js.map +1 -0
  335. package/dist/memory/presence.js +59 -0
  336. package/dist/memory/presence.js.map +1 -0
  337. package/dist/memory/projectNotes.js +247 -0
  338. package/dist/memory/projectNotes.js.map +1 -0
  339. package/dist/memory/restore.js +72 -0
  340. package/dist/memory/restore.js.map +1 -0
  341. package/dist/memory/session.js +472 -0
  342. package/dist/memory/session.js.map +1 -0
  343. package/dist/memory/sessionMemory.js +142 -0
  344. package/dist/memory/sessionMemory.js.map +1 -0
  345. package/dist/memory/sessionMemoryCompact.js +136 -0
  346. package/dist/memory/sessionMemoryCompact.js.map +1 -0
  347. package/dist/memory/store.js +217 -0
  348. package/dist/memory/store.js.map +1 -0
  349. package/dist/memory/types.js +14 -0
  350. package/dist/memory/types.js.map +1 -0
  351. package/dist/memory/workingSet.js +383 -0
  352. package/dist/memory/workingSet.js.map +1 -0
  353. package/dist/project/context.js +425 -0
  354. package/dist/project/context.js.map +1 -0
  355. package/dist/tools/approval.js +189 -0
  356. package/dist/tools/approval.js.map +1 -0
  357. package/dist/tools/askUser.js +103 -0
  358. package/dist/tools/askUser.js.map +1 -0
  359. package/dist/tools/atomicWrite.js +151 -0
  360. package/dist/tools/atomicWrite.js.map +1 -0
  361. package/dist/tools/backgroundShells.js +468 -0
  362. package/dist/tools/backgroundShells.js.map +1 -0
  363. package/dist/tools/captureSweep.js +77 -0
  364. package/dist/tools/captureSweep.js.map +1 -0
  365. package/dist/tools/chassisMux.js +92 -0
  366. package/dist/tools/chassisMux.js.map +1 -0
  367. package/dist/tools/checkpoints.js +328 -0
  368. package/dist/tools/checkpoints.js.map +1 -0
  369. package/dist/tools/codeIntel.js +267 -0
  370. package/dist/tools/codeIntel.js.map +1 -0
  371. package/dist/tools/deferredNative.js +121 -0
  372. package/dist/tools/deferredNative.js.map +1 -0
  373. package/dist/tools/detail.js +135 -0
  374. package/dist/tools/detail.js.map +1 -0
  375. package/dist/tools/diagnostics.js +174 -0
  376. package/dist/tools/diagnostics.js.map +1 -0
  377. package/dist/tools/edit.js +167 -0
  378. package/dist/tools/edit.js.map +1 -0
  379. package/dist/tools/editCore.js +283 -0
  380. package/dist/tools/editCore.js.map +1 -0
  381. package/dist/tools/editFile.js +183 -0
  382. package/dist/tools/editFile.js.map +1 -0
  383. package/dist/tools/editRipple.js +126 -0
  384. package/dist/tools/editRipple.js.map +1 -0
  385. package/dist/tools/editTarget.js +131 -0
  386. package/dist/tools/editTarget.js.map +1 -0
  387. package/dist/tools/editWindow.js +49 -0
  388. package/dist/tools/editWindow.js.map +1 -0
  389. package/dist/tools/eol.js +56 -0
  390. package/dist/tools/eol.js.map +1 -0
  391. package/dist/tools/exitPlan.js +175 -0
  392. package/dist/tools/exitPlan.js.map +1 -0
  393. package/dist/tools/focus.js +42 -0
  394. package/dist/tools/focus.js.map +1 -0
  395. package/dist/tools/gitignore.js +148 -0
  396. package/dist/tools/gitignore.js.map +1 -0
  397. package/dist/tools/glob.js +134 -0
  398. package/dist/tools/glob.js.map +1 -0
  399. package/dist/tools/governorTools.js +258 -0
  400. package/dist/tools/governorTools.js.map +1 -0
  401. package/dist/tools/grep.js +450 -0
  402. package/dist/tools/grep.js.map +1 -0
  403. package/dist/tools/guard.js +220 -0
  404. package/dist/tools/guard.js.map +1 -0
  405. package/dist/tools/killTree.js +215 -0
  406. package/dist/tools/killTree.js.map +1 -0
  407. package/dist/tools/listDir.js +143 -0
  408. package/dist/tools/listDir.js.map +1 -0
  409. package/dist/tools/mcpAdd.js +176 -0
  410. package/dist/tools/mcpAdd.js.map +1 -0
  411. package/dist/tools/mcpResources.js +116 -0
  412. package/dist/tools/mcpResources.js.map +1 -0
  413. package/dist/tools/mcpSearch.js +123 -0
  414. package/dist/tools/mcpSearch.js.map +1 -0
  415. package/dist/tools/multiEdit.js +168 -0
  416. package/dist/tools/multiEdit.js.map +1 -0
  417. package/dist/tools/paths.js +225 -0
  418. package/dist/tools/paths.js.map +1 -0
  419. package/dist/tools/posixShell.js +123 -0
  420. package/dist/tools/posixShell.js.map +1 -0
  421. package/dist/tools/readFile.js +371 -0
  422. package/dist/tools/readFile.js.map +1 -0
  423. package/dist/tools/readSymbol.js +145 -0
  424. package/dist/tools/readSymbol.js.map +1 -0
  425. package/dist/tools/registry.js +120 -0
  426. package/dist/tools/registry.js.map +1 -0
  427. package/dist/tools/replaceSymbol.js +141 -0
  428. package/dist/tools/replaceSymbol.js.map +1 -0
  429. package/dist/tools/results.js +15 -0
  430. package/dist/tools/results.js.map +1 -0
  431. package/dist/tools/ripgrep.js +145 -0
  432. package/dist/tools/ripgrep.js.map +1 -0
  433. package/dist/tools/runCommand.js +633 -0
  434. package/dist/tools/runCommand.js.map +1 -0
  435. package/dist/tools/saveMemory.js +102 -0
  436. package/dist/tools/saveMemory.js.map +1 -0
  437. package/dist/tools/screenshot.js +200 -0
  438. package/dist/tools/screenshot.js.map +1 -0
  439. package/dist/tools/screenshotWin.js +227 -0
  440. package/dist/tools/screenshotWin.js.map +1 -0
  441. package/dist/tools/search.js +121 -0
  442. package/dist/tools/search.js.map +1 -0
  443. package/dist/tools/sessionTools.js +187 -0
  444. package/dist/tools/sessionTools.js.map +1 -0
  445. package/dist/tools/shellCheckpoint.js +123 -0
  446. package/dist/tools/shellCheckpoint.js.map +1 -0
  447. package/dist/tools/shellLint.js +102 -0
  448. package/dist/tools/shellLint.js.map +1 -0
  449. package/dist/tools/shellTools.js +203 -0
  450. package/dist/tools/shellTools.js.map +1 -0
  451. package/dist/tools/spanCore.js +78 -0
  452. package/dist/tools/spanCore.js.map +1 -0
  453. package/dist/tools/subagent.js +224 -0
  454. package/dist/tools/subagent.js.map +1 -0
  455. package/dist/tools/subagentReport.js +114 -0
  456. package/dist/tools/subagentReport.js.map +1 -0
  457. package/dist/tools/tempSweep.js +118 -0
  458. package/dist/tools/tempSweep.js.map +1 -0
  459. package/dist/tools/todo.js +117 -0
  460. package/dist/tools/todo.js.map +1 -0
  461. package/dist/tools/types.js +2 -0
  462. package/dist/tools/types.js.map +1 -0
  463. package/dist/tools/untrusted.js +46 -0
  464. package/dist/tools/untrusted.js.map +1 -0
  465. package/dist/tools/useSkill.js +62 -0
  466. package/dist/tools/useSkill.js.map +1 -0
  467. package/dist/tools/walk.js +152 -0
  468. package/dist/tools/walk.js.map +1 -0
  469. package/dist/tools/web.js +61 -0
  470. package/dist/tools/web.js.map +1 -0
  471. package/dist/tools/webFetch.js +444 -0
  472. package/dist/tools/webFetch.js.map +1 -0
  473. package/dist/tools/webSearch.js +130 -0
  474. package/dist/tools/webSearch.js.map +1 -0
  475. package/dist/tools/workspace.js +212 -0
  476. package/dist/tools/workspace.js.map +1 -0
  477. package/dist/tools/workspaceDiscover.js +216 -0
  478. package/dist/tools/workspaceDiscover.js.map +1 -0
  479. package/dist/tools/writeFile.js +162 -0
  480. package/dist/tools/writeFile.js.map +1 -0
  481. package/package.json +74 -28
@@ -0,0 +1,633 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { promises as fs, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { StringDecoder } from "node:string_decoder";
6
+ import { catastrophicCommandReason, sensitiveCommandReason } from "./guard.js";
7
+ import { forbiddenCommandReason, forbiddenCommandPatternReason } from "../governor/forbidden.js";
8
+ import { requestForbiddenLift } from "./approval.js";
9
+ import { posixShell, shellMismatchNote } from "./posixShell.js";
10
+ import { killTree, spawnManaged } from "./killTree.js";
11
+ import { captureAfterCommand, looksReadOnly, snapshotBeforeCommand } from "./shellCheckpoint.js";
12
+ import { canonicalRoot, relativize } from "./paths.js";
13
+ import { outputDetail, withOutcome } from "./detail.js";
14
+ import { powershellLintReason, powershellParseError, powershellReservedAssignmentReason } from "./shellLint.js";
15
+ import { findRunningDuplicate, findRecentUserClose, guessNotifyPolicy } from "./backgroundShells.js";
16
+ import { fail, failQuietly } from "./results.js";
17
+ const IS_WINDOWS = process.platform === "win32";
18
+ const DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes
19
+ const MAX_TIMEOUT_MS = 600_000; // 10 minutes
20
+ /**
21
+ * How much command output reaches the model, split across the two ends.
22
+ *
23
+ * Keeping the FIRST 30,000 characters and dropping the rest throws away the part
24
+ * that matters: a build or test run puts its banner and progress at the start and
25
+ * its diagnosis — the failing assertion, the stack, "3 tests failed" — at the very
26
+ * end. A verbose `tsc` or webpack run overflows the budget on progress noise alone,
27
+ * so the model would receive a wall of chatter, a truncation notice, and nothing
28
+ * about why the command failed.
29
+ *
30
+ * Both ends are kept instead, weighted toward the tail. The same total budget.
31
+ */
32
+ const HEAD_CHARS = 8_000;
33
+ const TAIL_CHARS = 22_000;
34
+ /** How long to wait after `exit` for `close` before settling anyway. See the listener
35
+ * in runShell: a surviving grandchild can hold the output pipe open forever. */
36
+ const CLOSE_GRACE_MS = 2_000;
37
+ /**
38
+ * Human label for the shell, kept in sync with the system prompt.
39
+ *
40
+ * Reports what will ACTUALLY run, resolved on this machine, rather than a fixed
41
+ * string. It used to say "the POSIX shell (sh)" everywhere off Windows, which became
42
+ * a false claim the moment bash was preferred — and a prompt that asserts something
43
+ * untrue is worse than one that says nothing, because the model believes it and
44
+ * writes to the wrong dialect.
45
+ */
46
+ export function commandShellLabel() {
47
+ if (IS_WINDOWS)
48
+ return "Windows PowerShell";
49
+ return posixShell().isBash ? "bash" : "a strict POSIX shell (sh)";
50
+ }
51
+ export const runCommand = {
52
+ name: "run_command",
53
+ readOnly: false,
54
+ // Two claims in the previous version of this description were false, and both were
55
+ // the kind a model obeys without being able to check. It promised that a backgrounded
56
+ // command would report when it finished, which is true only for notify:'on_finish'
57
+ // and flatly contradicted by 'on_failure' (the default for anything server-shaped).
58
+ // And it warned that a never-terminating command would "hang the turn", which the
59
+ // soft timeout has made untrue, while scaring the model away from the very feature
60
+ // built for it. What replaced them is what actually happens.
61
+ description: `Run a shell command in the project and return its combined output and exit ` +
62
+ `code. The shell is ${commandShellLabel()}. Every turn starts at the project root; ` +
63
+ `the working directory persists between calls WITHIN a turn (so 'cd' carries over ` +
64
+ `mid-turn) but resets to the root next turn. ` +
65
+ `A command still running after 2 minutes (or 'timeout' ms, up to 10 minutes) is ` +
66
+ `MOVED TO THE BACKGROUND, not killed: you get a shell id and the session carries on. ` +
67
+ `Read its output any time with the shells tool. WHAT YOU HEAR AFTERWARDS IS SET BY ` +
68
+ `'notify', so choose it deliberately: only 'on_finish' reports that the command ` +
69
+ `ended, and it is not the default for everything. ` +
70
+ `Pass 'run_in_background: true' to background it from the start, and do that for a ` +
71
+ `dev server, a long build or test you do not need to wait on, and anything ` +
72
+ `interactive or never-terminating. Waiting on one of those inline will not hang the ` +
73
+ `turn, but it burns the whole timeout before backgrounding itself, which is time ` +
74
+ `spent for nothing. ` +
75
+ `Write commands for ${commandShellLabel()} (see the shell section of the system prompt)` +
76
+ `${IS_WINDOWS ? "; or pass shell:'cmd' to run in cmd.exe instead (for && / || chaining or cmd-only tools)" : ""}. ` +
77
+ `Prefer Mindweave's read/edit/search tools over shelling out.`,
78
+ parameters: {
79
+ type: "object",
80
+ additionalProperties: false,
81
+ required: ["command"],
82
+ properties: {
83
+ command: {
84
+ type: "string",
85
+ description: "The shell command to run.",
86
+ },
87
+ shell: {
88
+ type: "string",
89
+ enum: ["powershell", "cmd"],
90
+ description: "Windows only (ignored elsewhere): which shell to run in. Default 'powershell'. Choose " +
91
+ "'cmd' for commands written in cmd.exe syntax — && / || chaining, or a tool that misbehaves " +
92
+ "under PowerShell — Mindweave runs them as a batch script. Note: in cmd a for-loop uses %%i, and " +
93
+ "cwd may not carry over when the command itself is a .cmd tool (chain in one call instead).",
94
+ },
95
+ timeout: {
96
+ type: "integer",
97
+ minimum: 1000,
98
+ maximum: MAX_TIMEOUT_MS,
99
+ description: `How long to wait inline before moving it to the background, in ms (default 120000, max ${MAX_TIMEOUT_MS}).`,
100
+ },
101
+ run_in_background: {
102
+ type: "boolean",
103
+ description: "Start it in the background immediately and return a shell id (don't wait).",
104
+ },
105
+ notify: {
106
+ type: "string",
107
+ enum: ["on_finish", "on_failure", "never"],
108
+ description: "What you want to be told about a backgrounded command. 'on_finish' (default for tasks): " +
109
+ "you're told when it ends, whatever the result — use it for builds, tests, installs, " +
110
+ "anything whose RESULT is the point. 'on_failure': you're told when it has come up, and if " +
111
+ "it never does, but NOT when it stops — use it for dev servers and apps, because the user " +
112
+ "closing their own app is not something to act on. 'never': you're told nothing at all. " +
113
+ "Say which; guessing from the command name gets it wrong for anything unusual.",
114
+ },
115
+ },
116
+ },
117
+ async execute(args, ctx) {
118
+ const command = typeof args.command === "string" ? args.command.trim() : "";
119
+ if (!command)
120
+ return failQuietly("`command` is required.");
121
+ // A shell can change files in ways the checkpoint net never sees (a formatter, a
122
+ // codegen step, a `git checkout`). Flag the turn so /undo says what it did NOT
123
+ // cover instead of implying the whole turn was rolled back.
124
+ ctx.checkpoints?.noteShell();
125
+ const blocked = catastrophicCommandReason(command);
126
+ if (blocked) {
127
+ return fail(`Refusing to run this command: it looks like ${blocked}.`);
128
+ }
129
+ // A shell would sidestep every per-file gate the read/write tools enforce.
130
+ const sensitive = sensitiveCommandReason(command);
131
+ if (sensitive) {
132
+ return fail(`Refusing to run this command: it would print the contents of ${sensitive} into ` +
133
+ `the conversation. Checking whether the file exists, listing it, or copying it ` +
134
+ `is fine — reading it out is not. If you genuinely need what's inside, use ` +
135
+ `read_file, which asks the user first.`);
136
+ }
137
+ const forbidden = forbiddenCommandReason(ctx.governance?.forbidden, command);
138
+ if (forbidden) {
139
+ const lift = await requestForbiddenLift(ctx, forbidden, "this command", `it references '${forbidden}', which the user has forbidden touching.`);
140
+ if (lift)
141
+ return lift; // refused or deferred; an allow lifts it and falls through
142
+ }
143
+ // Forbidden COMMAND patterns: a command the user said never to run (e.g. `tauri
144
+ // dev`). Deterministic — the model cannot bypass it; only the user can lift it
145
+ // (same approval channel as forbidden paths, session-only).
146
+ const forbiddenCmd = forbiddenCommandPatternReason(ctx.governance?.forbidden, command);
147
+ if (forbiddenCmd) {
148
+ const lift = await requestForbiddenLift(ctx, forbiddenCmd, "this command", `the user has forbidden running '${forbiddenCmd}'.`, "forbidden command");
149
+ if (lift)
150
+ return lift;
151
+ }
152
+ let timeout = DEFAULT_TIMEOUT_MS;
153
+ if (typeof args.timeout === "number" && Number.isFinite(args.timeout)) {
154
+ timeout = Math.min(MAX_TIMEOUT_MS, Math.max(1000, Math.floor(args.timeout)));
155
+ }
156
+ const shell = args.shell === "cmd" ? "cmd" : "powershell";
157
+ // Already-running guard: don't start a second copy of something already running in
158
+ // the background. Two dev servers collide on a port and the model then burns a long
159
+ // loop fighting the conflict it created; two of anything else is nearly always a
160
+ // mistake too.
161
+ //
162
+ // This deliberately does NOT ask whether the command looks like a server. It used to,
163
+ // which meant the guard only covered the names in one regex: `cargo run`,
164
+ // `docker compose up`, `flask run` and a plain path to a binary were all unprotected.
165
+ // "Is this exact command already running" needs no such guess and covers everything.
166
+ if (ctx.backgroundShells) {
167
+ const dup = findRunningDuplicate(ctx.backgroundShells.running(), command);
168
+ if (dup) {
169
+ return {
170
+ output: `\`${clip(command)}\` is already running in the background as shell #${dup.id}, so I'm not ` +
171
+ `starting a second copy — a second one would collide with it. It's already up; if you want a ` +
172
+ `fresh start, call kill_shell(${dup.id}) first, then relaunch.`,
173
+ isError: true,
174
+ summary: `already running as shell #${dup.id}`,
175
+ };
176
+ }
177
+ // Reopen guard: don't relaunch an app the USER just closed. Without this, an agent
178
+ // that reopens a closed window turns into a loop — the user shuts their app, it comes
179
+ // straight back, they shut it again, and so on. The match is scoped to apps that came
180
+ // up and were closed WITHOUT a kill_shell (`findRecentUserClose`), so the agent's own
181
+ // kill-then-relaunch is untouched and a server that failed to start can still be
182
+ // retried. The cooldown lets a restart the user asks for later through.
183
+ const closed = findRecentUserClose(ctx.backgroundShells.list(), command, Date.now());
184
+ if (closed) {
185
+ const secs = closed.finishedAt ? Math.round((Date.now() - closed.finishedAt) / 1000) : 0;
186
+ return {
187
+ output: `The user closed \`${clip(command)}\` themselves ${secs}s ago (shell #${closed.id}, after it had come up), ` +
188
+ `so I'm not reopening it. Reopening an app the user just shut turns into a loop where they keep closing ` +
189
+ `it and it keeps coming back. Leave it down. If it genuinely needs to be running, ask the user first and ` +
190
+ `let them decide — don't relaunch it on your own.`,
191
+ isError: true,
192
+ summary: `user closed #${closed.id} ${secs}s ago — not reopening`,
193
+ };
194
+ }
195
+ }
196
+ // Pre-execution gate: don't waste a run on a guaranteed PowerShell parse error
197
+ // (`&&`/`||`) or an assignment to a read-only automatic variable (`$pid = …`). Catch
198
+ // it here and hand back the fix so the model corrects in one step instead of running,
199
+ // failing, and re-reading the error (the wall that spawned a long flailing loop).
200
+ if (IS_WINDOWS && shell === "powershell") {
201
+ const parseError = powershellParseError(command);
202
+ if (parseError)
203
+ return fail(parseError);
204
+ const reserved = powershellReservedAssignmentReason(command);
205
+ if (reserved)
206
+ return fail(reserved);
207
+ }
208
+ const declared = args.notify === "on_finish" || args.notify === "on_failure" || args.notify === "never"
209
+ ? args.notify
210
+ : undefined;
211
+ // Bring shell-caused changes into /undo. Snapshot the read ledger first, run, then
212
+ // check in whatever moved — this is the one mutation path that had no checkpoint at
213
+ // all, which mattered precisely because improvising with a script is a capability we
214
+ // rely on. Skipped for obviously read-only commands, and for background ones, whose
215
+ // writes land long after this call has returned. See shellCheckpoint.ts for the
216
+ // bounds and for what is honestly NOT covered.
217
+ const background = args.run_in_background === true;
218
+ const watch = !background && !looksReadOnly(command);
219
+ const before = watch ? await snapshotBeforeCommand(ctx) : undefined;
220
+ const result = await runShell(command, ctx, timeout, background, shell, declared);
221
+ if (before && before.size > 0) {
222
+ // Never let bookkeeping fail a command that already ran and succeeded.
223
+ await captureAfterCommand(ctx, before).catch(() => undefined);
224
+ }
225
+ return result;
226
+ },
227
+ };
228
+ async function runShell(command, ctx, timeoutMs, background, shell, declaredNotify) {
229
+ // A unique temp file the wrapped command writes its final cwd into.
230
+ const cwdFile = join(tmpdir(), `mindweave-cwd-${randomBytes(6).toString("hex")}.txt`);
231
+ // Where we stood before the command ran — applyCwd may move ctx.cwd, and the model
232
+ // needs telling when it does (see cwdChangeNote). CANONICALISED, because that
233
+ // comparison is a string equality and the two sides otherwise come from different
234
+ // places: this one from the session, the other from whatever form the shell prints.
235
+ // On Windows those differ for any user whose name is over eight characters, since
236
+ // one side carries the 8.3 short form, and the session then reports a move on every
237
+ // command that never left the directory.
238
+ const cwdBefore = await canonicalRoot(ctx.cwd);
239
+ const { bin, args, wrapped, tempFile } = buildInvocation(command, cwdFile, shell);
240
+ const child = spawnManaged(bin, [...args, wrapped], {
241
+ cwd: ctx.cwd,
242
+ env: {
243
+ ...process.env,
244
+ GIT_EDITOR: "true", // never drop into an interactive editor and hang
245
+ GIT_PAGER: "cat",
246
+ PAGER: "cat",
247
+ },
248
+ });
249
+ const mgr = ctx.backgroundShells;
250
+ // Explicit background: hand off immediately, don't wait for it.
251
+ //
252
+ // The abort listener below is only wired for the FOREGROUND path, so this branch
253
+ // has to check the signal itself. Without it an interrupted turn still adopts the
254
+ // process, and because backgrounding deliberately outlives the turn, Esc would
255
+ // leave a dev server running that the user believed they had cancelled.
256
+ if (background && mgr) {
257
+ if (ctx.abortSignal?.aborted) {
258
+ killTree(child.pid);
259
+ void fs.rm(cwdFile, { force: true }).catch(() => { });
260
+ if (tempFile)
261
+ void fs.rm(tempFile, { force: true }).catch(() => { });
262
+ return {
263
+ output: "Command interrupted before it started.",
264
+ isError: true,
265
+ summary: `interrupted \`${clip(command)}\``,
266
+ };
267
+ }
268
+ // Declared policy wins; the name guess is only the default when nothing was said.
269
+ const notify = declaredNotify ?? guessNotifyPolicy(command);
270
+ const info = mgr.adopt(child, { command, cwd: ctx.cwd, cwdFile, tempFile, notify });
271
+ return backgroundedResult(info.id, command, `Started in the background as shell #${info.id}`, notify);
272
+ }
273
+ return new Promise((resolve) => {
274
+ let head = "";
275
+ let tail = "";
276
+ let dropped = 0;
277
+ let timedOut = false;
278
+ let settled = false;
279
+ // Decode ACROSS chunks. A chunk boundary can fall inside a multi-byte UTF-8
280
+ // sequence, and decoding each Buffer on its own turns that character into a
281
+ // replacement glyph — which shows up in any non-English output and in the box
282
+ // drawing most test runners use. The decoder holds the partial bytes back until
283
+ // the rest arrives.
284
+ const decoder = new StringDecoder("utf8");
285
+ const collect = (chunk) => {
286
+ let rest = decoder.write(chunk);
287
+ if (!rest)
288
+ return;
289
+ if (head.length < HEAD_CHARS) {
290
+ const room = HEAD_CHARS - head.length;
291
+ head += rest.slice(0, room);
292
+ rest = rest.slice(room);
293
+ }
294
+ if (!rest)
295
+ return;
296
+ tail += rest;
297
+ if (tail.length > TAIL_CHARS) {
298
+ dropped += tail.length - TAIL_CHARS;
299
+ tail = tail.slice(tail.length - TAIL_CHARS);
300
+ }
301
+ };
302
+ /** Everything kept, with the gap named where it happened. */
303
+ const collected = () => composeOutput(head, tail, dropped);
304
+ child.stdout?.on("data", collect);
305
+ child.stderr?.on("data", collect);
306
+ const timer = setTimeout(() => {
307
+ if (settled)
308
+ return;
309
+ // Soft timeout: move the LIVE process to the background (preferred), so a long
310
+ // test/build keeps running instead of being lost. With no manager (bare tests)
311
+ // fall back to the old behavior — kill the tree.
312
+ if (mgr) {
313
+ settled = true;
314
+ detachAbort();
315
+ child.stdout?.off("data", collect);
316
+ child.stderr?.off("data", collect);
317
+ // Auto-backgrounded after running too long inline. Somebody was waiting on this,
318
+ // so its completion is the point unless the caller said otherwise.
319
+ const notify = declaredNotify ?? "on_finish";
320
+ const info = mgr.adopt(child, { command, cwd: ctx.cwd, initial: collected(), cwdFile, tempFile, notify });
321
+ resolve(backgroundedResult(info.id, command, `Still running after ${Math.round(timeoutMs / 1000)}s — moved to the background as shell #${info.id}`, notify));
322
+ }
323
+ else {
324
+ timedOut = true;
325
+ killTree(child.pid);
326
+ }
327
+ }, timeoutMs);
328
+ // Esc / interrupt: if the turn is aborted while this command is still running,
329
+ // kill the whole process tree and settle immediately. Without this a hung command
330
+ // (an installer waiting on a GUI, an interactive prompt) freezes the agent — the
331
+ // engine only re-checks the abort signal BETWEEN steps, never mid-tool-call, so it
332
+ // would stay blocked on this promise forever.
333
+ const signal = ctx.abortSignal;
334
+ const detachAbort = () => signal?.removeEventListener("abort", onAbort);
335
+ function onAbort() {
336
+ if (settled)
337
+ return;
338
+ settled = true;
339
+ clearTimeout(timer);
340
+ child.stdout?.off("data", collect);
341
+ child.stderr?.off("data", collect);
342
+ killTree(child.pid);
343
+ void fs.rm(cwdFile, { force: true }).catch(() => { });
344
+ if (tempFile)
345
+ void fs.rm(tempFile, { force: true }).catch(() => { });
346
+ const body = collected().trim();
347
+ resolve({
348
+ output: body ? `${body}\n\n[interrupted]` : "Command interrupted before it finished.",
349
+ isError: true,
350
+ summary: `interrupted \`${clip(command)}\``,
351
+ });
352
+ }
353
+ if (signal?.aborted)
354
+ return void onAbort();
355
+ signal?.addEventListener("abort", onAbort);
356
+ const finish = async (exitCode, signal) => {
357
+ if (settled)
358
+ return;
359
+ settled = true;
360
+ clearTimeout(timer);
361
+ detachAbort();
362
+ await applyCwd(cwdFile, ctx);
363
+ if (tempFile)
364
+ await fs.rm(tempFile, { force: true }).catch(() => { });
365
+ resolve(format(command, ctx, collected(), dropped > 0, timedOut, exitCode, signal, timeoutMs, shell, cwdBefore, child.pid));
366
+ };
367
+ child.on("error", (error) => {
368
+ if (settled)
369
+ return;
370
+ settled = true;
371
+ clearTimeout(timer);
372
+ detachAbort();
373
+ void fs.rm(cwdFile, { force: true }).catch(() => { });
374
+ if (tempFile)
375
+ void fs.rm(tempFile, { force: true }).catch(() => { });
376
+ resolve(fail(`could not start the command: ${error.message}`));
377
+ });
378
+ child.on("close", (code, signal) => void finish(code, signal));
379
+ // `close` waits for every stdio stream to close, which a surviving grandchild (a
380
+ // daemon the command started, inheriting stdout) holds open indefinitely. Without a
381
+ // fallback the call sits here until the timeout and only then gets backgrounded, so a
382
+ // command that merely starts something looks like it took two minutes. `exit` fires
383
+ // when the process itself goes; the short grace lets `close` win normally, so no
384
+ // ordinary command pays for this.
385
+ child.on("exit", (code, signal) => {
386
+ const grace = setTimeout(() => void finish(code, signal), CLOSE_GRACE_MS);
387
+ grace.unref?.();
388
+ });
389
+ });
390
+ }
391
+ /**
392
+ * The result returned when a command is (or becomes) a background shell.
393
+ *
394
+ * The text has to match what will ACTUALLY happen, and that differs by what was
395
+ * started. A finite task notifies on completion, so telling the model to promise a
396
+ * report is correct. A server or app does NOT: its stop is suppressed on purpose,
397
+ * because a user closing their own app is not something to act on. Telling the model
398
+ * it would be notified either way made it promise a report that never came, which is
399
+ * the prompt asserting a capability that does not exist.
400
+ */
401
+ function backgroundedResult(id, command, lead, notify) {
402
+ const tail = notify === "never"
403
+ ? `Nothing further will be reported about it. Say in ONE short line that it's running, then STOP ` +
404
+ `(end your turn). Use shells({id: ${id}}) to inspect it and kill_shell(${id}) to stop it.`
405
+ : notify === "on_failure"
406
+ ? `You WILL be told once it has come up, so you can report that. You will NOT be told when it ` +
407
+ `stops, because the user closing their own app is not an event to act on — so never restart ` +
408
+ `it on your own. Say in ONE short line that it's starting, then STOP (end your turn). Use ` +
409
+ `shells({id: ${id}}) to inspect it and kill_shell(${id}) to stop it.`
410
+ : `You will be notified AUTOMATICALLY the moment it finishes, so do NOT poll it. Say in ONE ` +
411
+ `short line that it started and that you'll report back when it's done, then STOP (end your ` +
412
+ `turn). Only call shells({id: ${id}}) if you have a specific reason to inspect partial ` +
413
+ `output; use kill_shell(${id}) to stop it.`;
414
+ return {
415
+ output: `${lead}. ${tail}`,
416
+ // UI-only, never sent to the model (`tail` above already told IT the real
417
+ // notification policy). A backgrounded command has produced NO output yet, so
418
+ // there is nothing to put on the output rail — which is what this used to do,
419
+ // rendering Mindweave's own note as if the command had printed it. It is a
420
+ // one-line verdict on the call, which is what the ⎿ branch is for, and it now
421
+ // reads the same way a write's "whole file · 19 lines" does.
422
+ summary: `Backgrounded as shell #${id}`,
423
+ };
424
+ }
425
+ /** Build the shell invocation: which binary, its flags, and the wrapped command. */
426
+ function buildInvocation(command, cwdFile, shell) {
427
+ if (IS_WINDOWS && shell === "cmd") {
428
+ // cmd.exe can't run a multi-line /c string (it executes only the first line), so
429
+ // we materialize a tiny .bat: the command, then capture its exit code and final
430
+ // cwd. Run line-by-line, so `cd` persists and `&&`/`||` chains work natively.
431
+ // ComSpec is the reliable path to cmd.exe (a bare name may not resolve).
432
+ const batFile = join(tmpdir(), `mindweave-run-${randomBytes(6).toString("hex")}.bat`);
433
+ const script = `@echo off\r\n` +
434
+ `${command}\r\n` +
435
+ `set __ec=%ERRORLEVEL%\r\n` +
436
+ `cd > "${cwdFile}"\r\n` +
437
+ `exit /b %__ec%`;
438
+ writeFileSync(batFile, script, "utf8");
439
+ return {
440
+ bin: process.env.ComSpec || "cmd.exe",
441
+ args: ["/d", "/s", "/c"],
442
+ wrapped: batFile,
443
+ tempFile: batFile,
444
+ };
445
+ }
446
+ if (IS_WINDOWS) {
447
+ // After the command, record the final location and preserve its exit code.
448
+ //
449
+ // `$?` has to be captured on the very FIRST line after the command, because every
450
+ // statement sets it — including an assignment, which always succeeds. Read it
451
+ // second and you are reading whether `$__ec = …` worked, which is always true.
452
+ //
453
+ // Both signals are needed because they cover different halves of PowerShell.
454
+ // `$LASTEXITCODE` is set ONLY by native executables, so a failing cmdlet
455
+ // (`Get-Content` on a missing file, a failed `Remove-Item` — most of what a model
456
+ // writes) leaves it null, which used to be coerced to 0 and reported to the model
457
+ // as SUCCESS. `$?` catches those. It is not enough on its own either: it says
458
+ // whether something failed, never with which code, and `exit 3` from a real
459
+ // program has to survive as 3. So: take the native code when there is one, and
460
+ // otherwise promote a cmdlet failure to 1.
461
+ const wrapped = `${command}\n` +
462
+ `$__ok = $?\n` +
463
+ `$__ec = $LASTEXITCODE; if ($null -eq $__ec) { $__ec = 0 }\n` +
464
+ `if (-not $__ok -and $__ec -eq 0) { $__ec = 1 }\n` +
465
+ `$PWD.Path | Out-File -FilePath ${psQuote(cwdFile)} -Encoding utf8\n` +
466
+ `exit $__ec`;
467
+ return {
468
+ bin: "powershell.exe",
469
+ // -ExecutionPolicy Bypass (process-scoped only) so the model can actually run
470
+ // npm/npx/tsc/jest — their Windows shims are .ps1 scripts, which a Restricted
471
+ // execution policy blocks ("npm.ps1 cannot be loaded because running scripts is
472
+ // disabled"). Without this the verify/test/build story silently can't run.
473
+ args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command"],
474
+ wrapped,
475
+ };
476
+ }
477
+ // Prefer bash. `/bin/sh` is `dash` on Debian/Ubuntu, where ordinary bash syntax a
478
+ // model writes — `[[ ]]`, `source`, arrays — is a hard syntax error. See posixShell.
479
+ //
480
+ // `pwd -P` reports the PHYSICAL path, with symlinks resolved. That is deliberate and
481
+ // it is why `canonicalRoot` exists: macOS puts /tmp and the whole of os.tmpdir()
482
+ // behind symlinks (/tmp → /private/tmp), so a logical path and a physical one for the
483
+ // same directory differ constantly. Recording the physical form on both sides is what
484
+ // keeps them comparable; recording one of each is what silently moved a session off
485
+ // its own root.
486
+ const wrapped = `${command}\n` +
487
+ `__ec=$?\n` +
488
+ `pwd -P > ${shQuote(cwdFile)} 2>/dev/null\n` +
489
+ `exit $__ec`;
490
+ return { bin: posixShell().bin, args: ["-c"], wrapped };
491
+ }
492
+ /**
493
+ * Join the kept head and tail of a long output, naming the gap (pure).
494
+ *
495
+ * The marker sits WHERE the loss happened rather than at the end, so the model can
496
+ * see that the two halves are not contiguous. A trailing "output truncated" note
497
+ * after a head-only excerpt reads as "there was a bit more", which is exactly the
498
+ * wrong impression when the missing part is the answer.
499
+ */
500
+ export function composeOutput(head, tail, dropped) {
501
+ if (dropped <= 0)
502
+ return head + tail;
503
+ return `${head}\n… [${dropped.toLocaleString("en-US")} characters omitted from the middle] …\n${tail}`;
504
+ }
505
+ /**
506
+ * The line appended to a command's output when it moved the shell (pure).
507
+ *
508
+ * `cd` persisting across calls within a turn is a real convenience, but it is also
509
+ * invisible: the model composes the next command's relative paths from the project
510
+ * root, because nothing ever told it the floor moved. That produces a doubled path
511
+ * (`code-blue/backend/code-blue/backend/manage.py`), which fails, and looks to the
512
+ * model like the file is missing rather than like it is standing somewhere else.
513
+ *
514
+ * So we say it, once, exactly when it happens. `shown` is the new location relative
515
+ * to the project root — the same form paths are addressed in everywhere else.
516
+ * Returns null when the command did not move, which is nearly every command, so
517
+ * ordinary output is untouched.
518
+ */
519
+ export function cwdChangeNote(before, after, shown) {
520
+ if (before === after)
521
+ return null;
522
+ const where = shown === "." ? "the project root" : shown;
523
+ return (`[Working directory is now ${where}. It stays there for the rest of this turn, so ` +
524
+ `relative paths in your next command resolve from ${where}, not from the project root.]`);
525
+ }
526
+ /** Read the cwd the command ended in and adopt it (if it still exists). */
527
+ async function applyCwd(cwdFile, ctx) {
528
+ try {
529
+ const text = (await fs.readFile(cwdFile, "utf8")).trim();
530
+ if (text) {
531
+ // Confirm it's a real directory before adopting — a half-written file or a
532
+ // deleted dir must not strand the session somewhere invalid.
533
+ const stat = await fs.stat(text);
534
+ // Canonicalise on the way in, so the session only ever holds ONE form of a
535
+ // path. The shell prints whatever form it was handed; adopting that verbatim is
536
+ // how a session ends up describing one directory two ways.
537
+ if (stat.isDirectory())
538
+ ctx.cwd = await canonicalRoot(text);
539
+ }
540
+ }
541
+ catch {
542
+ // No file / unreadable → command didn't change dir (or failed early); keep cwd.
543
+ }
544
+ finally {
545
+ await fs.rm(cwdFile, { force: true }).catch(() => { });
546
+ }
547
+ }
548
+ function format(command, ctx, output, truncated, timedOut, exitCode, signal, timeoutMs, shell, cwdBefore,
549
+ /** The killed process, named only when something WAS killed (see withOutcome). */
550
+ pid) {
551
+ const body = output.trim();
552
+ const parts = [];
553
+ if (timedOut) {
554
+ parts.push(`Command timed out after ${Math.round(timeoutMs / 1000)}s and was killed. ` +
555
+ `If it was a long-running or watching process, run a form that terminates.`);
556
+ }
557
+ else if (signal) {
558
+ // A process ended by a signal reports no exit code at all. Treating that as "not
559
+ // non-zero" made a killed command read as a success, and the summary said
560
+ // "exit null" — so a command someone stopped looked like a command that worked.
561
+ parts.push(`Command was terminated by ${signal} before it finished.`);
562
+ }
563
+ else if (exitCode !== 0 && exitCode !== null) {
564
+ parts.push(`Command exited with code ${exitCode}.`);
565
+ }
566
+ if (body) {
567
+ parts.push(body);
568
+ // The gap is already marked inline, at the point it happened; this only names
569
+ // the shape of what arrived so the model doesn't read the two halves as one run.
570
+ if (truncated)
571
+ parts.push("(long output: the start and the end are shown, the middle was dropped)");
572
+ }
573
+ else if (!timedOut) {
574
+ parts.push("(no output)");
575
+ }
576
+ // In PowerShell, nudge the model when it wrote a bash-ism that breaks there
577
+ // (advisory only — never blocks; the linter is conservative). Not for cmd, which
578
+ // supports && / || natively.
579
+ if (IS_WINDOWS && shell === "powershell") {
580
+ const lint = powershellLintReason(command);
581
+ if (lint)
582
+ parts.push(lint);
583
+ }
584
+ // The POSIX mirror of the same idea. Silent on any machine that has bash (nearly
585
+ // all of them), because there the mismatch cannot arise — it only speaks up on a
586
+ // minimal box where the command genuinely could not have parsed, and then it names
587
+ // the construct rather than leaving a bare `dash: syntax error` to be decoded.
588
+ if (!IS_WINDOWS) {
589
+ const mismatch = shellMismatchNote(command);
590
+ if (mismatch)
591
+ parts.push(mismatch);
592
+ }
593
+ const shown = relativize(ctx, ctx.cwd);
594
+ // Did this command move the shell? If so the model must hear it here, in the tool
595
+ // OUTPUT — the summary line below is display-only and never reaches the model.
596
+ const moved = cwdChangeNote(cwdBefore, ctx.cwd, shown);
597
+ if (moved)
598
+ parts.push(moved);
599
+ const status = timedOut ? "timed out" : signal ? `killed (${signal})` : exitCode === 0 ? "ok" : `exit ${exitCode}`;
600
+ return {
601
+ output: parts.join("\n"),
602
+ isError: timedOut || signal !== null || (exitCode !== 0 && exitCode !== null),
603
+ summary: `ran \`${clip(command)}\` in ${shown} (${status})`,
604
+ // The outcome is appended to what is SHOWN, not just to what the model reads. A
605
+ // command that printed output previously ended its row with the last line of that
606
+ // output and nothing else, so a build that failed and a build that passed looked
607
+ // identical unless you recognised the text — the exit code was known here and
608
+ // simply never displayed.
609
+ // The command leads its own block on its own row. Inline in the header it was
610
+ // clipped to 48 characters, which for a real command line lost the half that said
611
+ // what it actually did (`Run(mkdir -p ..\astra-backup; Move-Item .\astra.htm…)`).
612
+ detail: withOutcome(shellBody(command, body), timedOut, exitCode, signal, timeoutMs, pid),
613
+ detailKind: "shell",
614
+ };
615
+ }
616
+ /** The `$ command` header row above a command's captured output. */
617
+ function shellBody(command, body) {
618
+ const out = outputDetail(body);
619
+ return out ? `$ ${command}\n${out}` : `$ ${command}`;
620
+ }
621
+ /** Single-quote a string for a POSIX shell. */
622
+ function shQuote(s) {
623
+ return `'${s.split("'").join(`'\\''`)}'`;
624
+ }
625
+ /** Single-quote a string for PowerShell (double any embedded single quotes). */
626
+ function psQuote(s) {
627
+ return `'${s.split("'").join("''")}'`;
628
+ }
629
+ function clip(s, max = 60) {
630
+ const flat = s.replace(/\s+/g, " ").trim();
631
+ return flat.length <= max ? flat : flat.slice(0, max - 1) + "…";
632
+ }
633
+ //# sourceMappingURL=runCommand.js.map