lonny-agent 0.2.6 → 0.2.7

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 (475) hide show
  1. package/.github/workflows/ci.yml +21 -0
  2. package/AGENTS.md +106 -0
  3. package/CHANGELOG.md +24 -0
  4. package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
  5. package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
  6. package/dist/agent/__tests__/session-restore.test.js +260 -0
  7. package/dist/agent/__tests__/session-restore.test.js.map +1 -0
  8. package/dist/agent/commands.d.ts +26 -0
  9. package/dist/agent/commands.d.ts.map +1 -0
  10. package/dist/agent/commands.js +253 -0
  11. package/dist/agent/commands.js.map +1 -0
  12. package/dist/agent/compaction.js +8 -1
  13. package/dist/agent/compaction.js.map +1 -1
  14. package/dist/agent/index.d.ts +2 -1
  15. package/dist/agent/index.d.ts.map +1 -1
  16. package/dist/agent/index.js +2 -2
  17. package/dist/agent/index.js.map +1 -1
  18. package/dist/agent/memory.d.ts +12 -0
  19. package/dist/agent/memory.d.ts.map +1 -0
  20. package/dist/agent/memory.js +51 -0
  21. package/dist/agent/memory.js.map +1 -0
  22. package/dist/agent/prompt-builder.d.ts +6 -1
  23. package/dist/agent/prompt-builder.d.ts.map +1 -1
  24. package/dist/agent/prompt-builder.js +89 -26
  25. package/dist/agent/prompt-builder.js.map +1 -1
  26. package/dist/agent/providers/openai.d.ts +1 -0
  27. package/dist/agent/providers/openai.d.ts.map +1 -1
  28. package/dist/agent/providers/openai.js +16 -3
  29. package/dist/agent/providers/openai.js.map +1 -1
  30. package/dist/agent/session-chat.d.ts +3 -0
  31. package/dist/agent/session-chat.d.ts.map +1 -0
  32. package/dist/agent/session-chat.js +384 -0
  33. package/dist/agent/session-chat.js.map +1 -0
  34. package/dist/agent/session-display.d.ts +29 -0
  35. package/dist/agent/session-display.d.ts.map +1 -0
  36. package/dist/agent/session-display.js +179 -0
  37. package/dist/agent/session-display.js.map +1 -0
  38. package/dist/agent/session-persistence.d.ts +55 -0
  39. package/dist/agent/session-persistence.d.ts.map +1 -0
  40. package/dist/agent/session-persistence.js +101 -0
  41. package/dist/agent/session-persistence.js.map +1 -0
  42. package/dist/agent/session-utils.d.ts +5 -0
  43. package/dist/agent/session-utils.d.ts.map +1 -0
  44. package/dist/agent/session-utils.js +51 -0
  45. package/dist/agent/session-utils.js.map +1 -0
  46. package/dist/agent/session.d.ts +15 -17
  47. package/dist/agent/session.d.ts.map +1 -1
  48. package/dist/agent/session.js +226 -608
  49. package/dist/agent/session.js.map +1 -1
  50. package/dist/cli/index.d.ts +4 -0
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +67 -2
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/config/index.d.ts +2 -2
  55. package/dist/config/index.d.ts.map +1 -1
  56. package/dist/index.js +24 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/tools/__tests__/bash.test.js +168 -1
  59. package/dist/tools/__tests__/bash.test.js.map +1 -1
  60. package/dist/tools/__tests__/edit.test.js +253 -6
  61. package/dist/tools/__tests__/edit.test.js.map +1 -1
  62. package/dist/tools/__tests__/fetch.test.js +1 -1
  63. package/dist/tools/__tests__/fetch.test.js.map +1 -1
  64. package/dist/tools/__tests__/glob.test.js +1 -1
  65. package/dist/tools/__tests__/glob.test.js.map +1 -1
  66. package/dist/tools/__tests__/grep.test.js +1 -1
  67. package/dist/tools/__tests__/grep.test.js.map +1 -1
  68. package/dist/tools/__tests__/ls.test.js +1 -1
  69. package/dist/tools/__tests__/ls.test.js.map +1 -1
  70. package/dist/tools/__tests__/read.test.js +1 -1
  71. package/dist/tools/__tests__/read.test.js.map +1 -1
  72. package/dist/tools/__tests__/registry.test.js +313 -0
  73. package/dist/tools/__tests__/registry.test.js.map +1 -1
  74. package/dist/tools/__tests__/write_plan.test.js +1 -1
  75. package/dist/tools/__tests__/write_plan.test.js.map +1 -1
  76. package/dist/tools/bash.d.ts.map +1 -1
  77. package/dist/tools/bash.js +659 -39
  78. package/dist/tools/bash.js.map +1 -1
  79. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
  80. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
  81. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
  82. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
  83. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
  84. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
  85. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
  86. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
  87. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
  88. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
  89. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
  90. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
  91. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
  92. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
  93. package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
  94. package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
  95. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
  96. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
  97. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
  98. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
  99. package/dist/tools/codebase/ast/adapter.d.ts +23 -0
  100. package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
  101. package/dist/tools/codebase/ast/adapter.js +11 -0
  102. package/dist/tools/codebase/ast/adapter.js.map +1 -0
  103. package/dist/tools/codebase/ast/tools.d.ts +3 -0
  104. package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
  105. package/dist/tools/codebase/ast/tools.js +286 -0
  106. package/dist/tools/codebase/ast/tools.js.map +1 -0
  107. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
  108. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
  109. package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
  110. package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
  111. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
  112. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
  113. package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
  114. package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
  115. package/dist/tools/codebase/ast/types.d.ts +103 -0
  116. package/dist/tools/codebase/ast/types.d.ts.map +1 -0
  117. package/dist/tools/codebase/ast/types.js +2 -0
  118. package/dist/tools/codebase/ast/types.js.map +1 -0
  119. package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  120. package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  121. package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  122. package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  123. package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  124. package/dist/tools/codebase/find.d.ts +3 -0
  125. package/dist/tools/codebase/find.d.ts.map +1 -0
  126. package/dist/tools/codebase/find.js +61 -0
  127. package/dist/tools/codebase/find.js.map +1 -0
  128. package/dist/tools/codebase/glob.d.ts +3 -0
  129. package/dist/tools/codebase/glob.d.ts.map +1 -0
  130. package/dist/tools/codebase/glob.js +37 -0
  131. package/dist/tools/codebase/glob.js.map +1 -0
  132. package/dist/tools/codebase/grep.d.ts +3 -0
  133. package/dist/tools/codebase/grep.d.ts.map +1 -0
  134. package/dist/tools/codebase/grep.js +169 -0
  135. package/dist/tools/codebase/grep.js.map +1 -0
  136. package/dist/tools/codebase/ls.d.ts +3 -0
  137. package/dist/tools/codebase/ls.d.ts.map +1 -0
  138. package/dist/tools/codebase/ls.js +39 -0
  139. package/dist/tools/codebase/ls.js.map +1 -0
  140. package/dist/tools/codebase/read.d.ts +10 -0
  141. package/dist/tools/codebase/read.d.ts.map +1 -0
  142. package/dist/tools/codebase/read.js +81 -0
  143. package/dist/tools/codebase/read.js.map +1 -0
  144. package/dist/tools/delete_memory.d.ts +3 -0
  145. package/dist/tools/delete_memory.d.ts.map +1 -0
  146. package/dist/tools/delete_memory.js +46 -0
  147. package/dist/tools/delete_memory.js.map +1 -0
  148. package/dist/tools/edit/diff-compute.d.ts +4 -0
  149. package/dist/tools/edit/diff-compute.d.ts.map +1 -0
  150. package/dist/tools/edit/diff-compute.js +18 -0
  151. package/dist/tools/edit/diff-compute.js.map +1 -0
  152. package/dist/tools/edit/diff-render.d.ts +18 -0
  153. package/dist/tools/edit/diff-render.d.ts.map +1 -0
  154. package/dist/tools/edit/diff-render.js +128 -0
  155. package/dist/tools/edit/diff-render.js.map +1 -0
  156. package/dist/tools/edit/edit.d.ts +4 -0
  157. package/dist/tools/edit/edit.d.ts.map +1 -0
  158. package/dist/tools/edit/edit.js +440 -0
  159. package/dist/tools/edit/edit.js.map +1 -0
  160. package/dist/tools/edit/matcher.d.ts +24 -0
  161. package/dist/tools/edit/matcher.d.ts.map +1 -0
  162. package/dist/tools/edit/matcher.js +66 -0
  163. package/dist/tools/edit/matcher.js.map +1 -0
  164. package/dist/tools/edit/parser.d.ts +10 -0
  165. package/dist/tools/edit/parser.d.ts.map +1 -0
  166. package/dist/tools/edit/parser.js +120 -0
  167. package/dist/tools/edit/parser.js.map +1 -0
  168. package/dist/tools/edit/types.d.ts +16 -0
  169. package/dist/tools/edit/types.d.ts.map +1 -0
  170. package/dist/tools/edit/types.js +2 -0
  171. package/dist/tools/edit/types.js.map +1 -0
  172. package/dist/tools/edit/write_plan.d.ts +5 -0
  173. package/dist/tools/edit/write_plan.d.ts.map +1 -0
  174. package/dist/tools/edit/write_plan.js +98 -0
  175. package/dist/tools/edit/write_plan.js.map +1 -0
  176. package/dist/tools/edit.d.ts.map +1 -1
  177. package/dist/tools/edit.js +66 -1
  178. package/dist/tools/edit.js.map +1 -1
  179. package/dist/tools/execute/bash/constants.d.ts +24 -0
  180. package/dist/tools/execute/bash/constants.d.ts.map +1 -0
  181. package/dist/tools/execute/bash/constants.js +88 -0
  182. package/dist/tools/execute/bash/constants.js.map +1 -0
  183. package/dist/tools/execute/bash/errors.d.ts +39 -0
  184. package/dist/tools/execute/bash/errors.d.ts.map +1 -0
  185. package/dist/tools/execute/bash/errors.js +249 -0
  186. package/dist/tools/execute/bash/errors.js.map +1 -0
  187. package/dist/tools/execute/bash/execution.d.ts +18 -0
  188. package/dist/tools/execute/bash/execution.d.ts.map +1 -0
  189. package/dist/tools/execute/bash/execution.js +207 -0
  190. package/dist/tools/execute/bash/execution.js.map +1 -0
  191. package/dist/tools/execute/bash/index.d.ts +3 -0
  192. package/dist/tools/execute/bash/index.d.ts.map +1 -0
  193. package/dist/tools/execute/bash/index.js +117 -0
  194. package/dist/tools/execute/bash/index.js.map +1 -0
  195. package/dist/tools/execute/bash/platform.d.ts +6 -0
  196. package/dist/tools/execute/bash/platform.d.ts.map +1 -0
  197. package/dist/tools/execute/bash/platform.js +64 -0
  198. package/dist/tools/execute/bash/platform.js.map +1 -0
  199. package/dist/tools/execute/bash/security.d.ts +12 -0
  200. package/dist/tools/execute/bash/security.d.ts.map +1 -0
  201. package/dist/tools/execute/bash/security.js +26 -0
  202. package/dist/tools/execute/bash/security.js.map +1 -0
  203. package/dist/tools/execute/bash/validation.d.ts +16 -0
  204. package/dist/tools/execute/bash/validation.d.ts.map +1 -0
  205. package/dist/tools/execute/bash/validation.js +57 -0
  206. package/dist/tools/execute/bash/validation.js.map +1 -0
  207. package/dist/tools/execute/bash.d.ts +3 -0
  208. package/dist/tools/execute/bash.d.ts.map +1 -0
  209. package/dist/tools/execute/bash.js +728 -0
  210. package/dist/tools/execute/bash.js.map +1 -0
  211. package/dist/tools/execute/git.d.ts +3 -0
  212. package/dist/tools/execute/git.d.ts.map +1 -0
  213. package/dist/tools/execute/git.js +183 -0
  214. package/dist/tools/execute/git.js.map +1 -0
  215. package/dist/tools/execute/task_complete.d.ts +7 -0
  216. package/dist/tools/execute/task_complete.d.ts.map +1 -0
  217. package/dist/tools/execute/task_complete.js +38 -0
  218. package/dist/tools/execute/task_complete.js.map +1 -0
  219. package/dist/tools/fetch.d.ts.map +1 -1
  220. package/dist/tools/fetch.js +2 -0
  221. package/dist/tools/fetch.js.map +1 -1
  222. package/dist/tools/find.d.ts.map +1 -1
  223. package/dist/tools/find.js +2 -0
  224. package/dist/tools/find.js.map +1 -1
  225. package/dist/tools/git.d.ts.map +1 -1
  226. package/dist/tools/git.js +2 -0
  227. package/dist/tools/git.js.map +1 -1
  228. package/dist/tools/glob.d.ts.map +1 -1
  229. package/dist/tools/glob.js +2 -0
  230. package/dist/tools/glob.js.map +1 -1
  231. package/dist/tools/grep.d.ts.map +1 -1
  232. package/dist/tools/grep.js +2 -0
  233. package/dist/tools/grep.js.map +1 -1
  234. package/dist/tools/install/clawhub.d.ts +26 -0
  235. package/dist/tools/install/clawhub.d.ts.map +1 -0
  236. package/dist/tools/install/clawhub.js +182 -0
  237. package/dist/tools/install/clawhub.js.map +1 -0
  238. package/dist/tools/install/constants.d.ts +5 -0
  239. package/dist/tools/install/constants.d.ts.map +1 -0
  240. package/dist/tools/install/constants.js +5 -0
  241. package/dist/tools/install/constants.js.map +1 -0
  242. package/dist/tools/install/install_skill.d.ts +3 -0
  243. package/dist/tools/install/install_skill.d.ts.map +1 -0
  244. package/dist/tools/install/install_skill.js +132 -0
  245. package/dist/tools/install/install_skill.js.map +1 -0
  246. package/dist/tools/install/npm.d.ts +8 -0
  247. package/dist/tools/install/npm.d.ts.map +1 -0
  248. package/dist/tools/install/npm.js +52 -0
  249. package/dist/tools/install/npm.js.map +1 -0
  250. package/dist/tools/install/skill-content.d.ts +9 -0
  251. package/dist/tools/install/skill-content.d.ts.map +1 -0
  252. package/dist/tools/install/skill-content.js +89 -0
  253. package/dist/tools/install/skill-content.js.map +1 -0
  254. package/dist/tools/install_skill.d.ts.map +1 -1
  255. package/dist/tools/install_skill.js +2 -0
  256. package/dist/tools/install_skill.js.map +1 -1
  257. package/dist/tools/list_memory.d.ts +3 -0
  258. package/dist/tools/list_memory.d.ts.map +1 -0
  259. package/dist/tools/list_memory.js +55 -0
  260. package/dist/tools/list_memory.js.map +1 -0
  261. package/dist/tools/ls.d.ts.map +1 -1
  262. package/dist/tools/ls.js +2 -0
  263. package/dist/tools/ls.js.map +1 -1
  264. package/dist/tools/memory/delete_memory.d.ts +3 -0
  265. package/dist/tools/memory/delete_memory.d.ts.map +1 -0
  266. package/dist/tools/memory/delete_memory.js +46 -0
  267. package/dist/tools/memory/delete_memory.js.map +1 -0
  268. package/dist/tools/memory/list_memory.d.ts +3 -0
  269. package/dist/tools/memory/list_memory.d.ts.map +1 -0
  270. package/dist/tools/memory/list_memory.js +55 -0
  271. package/dist/tools/memory/list_memory.js.map +1 -0
  272. package/dist/tools/memory/save_memory.d.ts +3 -0
  273. package/dist/tools/memory/save_memory.d.ts.map +1 -0
  274. package/dist/tools/memory/save_memory.js +45 -0
  275. package/dist/tools/memory/save_memory.js.map +1 -0
  276. package/dist/tools/read.d.ts.map +1 -1
  277. package/dist/tools/read.js +2 -0
  278. package/dist/tools/read.js.map +1 -1
  279. package/dist/tools/registry.d.ts +7 -2
  280. package/dist/tools/registry.d.ts.map +1 -1
  281. package/dist/tools/registry.js +112 -17
  282. package/dist/tools/registry.js.map +1 -1
  283. package/dist/tools/save_memory.d.ts +3 -0
  284. package/dist/tools/save_memory.d.ts.map +1 -0
  285. package/dist/tools/save_memory.js +45 -0
  286. package/dist/tools/save_memory.js.map +1 -0
  287. package/dist/tools/search.d.ts.map +1 -1
  288. package/dist/tools/search.js +2 -0
  289. package/dist/tools/search.js.map +1 -1
  290. package/dist/tools/tree.d.ts +12 -0
  291. package/dist/tools/tree.d.ts.map +1 -0
  292. package/dist/tools/tree.js +87 -0
  293. package/dist/tools/tree.js.map +1 -0
  294. package/dist/tools/types.d.ts +12 -0
  295. package/dist/tools/types.d.ts.map +1 -1
  296. package/dist/tools/web/fetch.d.ts +3 -0
  297. package/dist/tools/web/fetch.d.ts.map +1 -0
  298. package/dist/tools/web/fetch.js +49 -0
  299. package/dist/tools/web/fetch.js.map +1 -0
  300. package/dist/tools/web/search.d.ts +3 -0
  301. package/dist/tools/web/search.d.ts.map +1 -0
  302. package/dist/tools/web/search.js +134 -0
  303. package/dist/tools/web/search.js.map +1 -0
  304. package/dist/tools/write_plan.d.ts.map +1 -1
  305. package/dist/tools/write_plan.js +2 -0
  306. package/dist/tools/write_plan.js.map +1 -1
  307. package/dist/tui/commands.d.ts +4 -0
  308. package/dist/tui/commands.d.ts.map +1 -0
  309. package/dist/tui/commands.js +118 -0
  310. package/dist/tui/commands.js.map +1 -0
  311. package/dist/tui/components/colors.d.ts +36 -0
  312. package/dist/tui/components/colors.d.ts.map +1 -0
  313. package/dist/tui/components/colors.js +48 -0
  314. package/dist/tui/components/colors.js.map +1 -0
  315. package/dist/tui/components/header-bar.d.ts +26 -0
  316. package/dist/tui/components/header-bar.d.ts.map +1 -0
  317. package/dist/tui/components/header-bar.js +78 -0
  318. package/dist/tui/components/header-bar.js.map +1 -0
  319. package/dist/tui/components/index.d.ts +9 -0
  320. package/dist/tui/components/index.d.ts.map +1 -0
  321. package/dist/tui/components/index.js +9 -0
  322. package/dist/tui/components/index.js.map +1 -0
  323. package/dist/tui/components/landing-screen.d.ts +12 -0
  324. package/dist/tui/components/landing-screen.d.ts.map +1 -0
  325. package/dist/tui/components/landing-screen.js +53 -0
  326. package/dist/tui/components/landing-screen.js.map +1 -0
  327. package/dist/tui/components/pixel-logo.d.ts +5 -0
  328. package/dist/tui/components/pixel-logo.d.ts.map +1 -0
  329. package/dist/tui/components/pixel-logo.js +27 -0
  330. package/dist/tui/components/pixel-logo.js.map +1 -0
  331. package/dist/tui/components/plan-utils.d.ts +11 -0
  332. package/dist/tui/components/plan-utils.d.ts.map +1 -0
  333. package/dist/tui/components/plan-utils.js +62 -0
  334. package/dist/tui/components/plan-utils.js.map +1 -0
  335. package/dist/tui/components/plans-list.d.ts +18 -0
  336. package/dist/tui/components/plans-list.d.ts.map +1 -0
  337. package/dist/tui/components/plans-list.js +46 -0
  338. package/dist/tui/components/plans-list.js.map +1 -0
  339. package/dist/tui/components/rich-footer.d.ts +29 -0
  340. package/dist/tui/components/rich-footer.d.ts.map +1 -0
  341. package/dist/tui/components/rich-footer.js +124 -0
  342. package/dist/tui/components/rich-footer.js.map +1 -0
  343. package/dist/tui/components/todo-panel.d.ts +14 -0
  344. package/dist/tui/components/todo-panel.d.ts.map +1 -0
  345. package/dist/tui/components/todo-panel.js +83 -0
  346. package/dist/tui/components/todo-panel.js.map +1 -0
  347. package/dist/tui/components.d.ts.map +1 -1
  348. package/dist/tui/components.js +14 -7
  349. package/dist/tui/components.js.map +1 -1
  350. package/dist/tui/index.d.ts +2 -1
  351. package/dist/tui/index.d.ts.map +1 -1
  352. package/dist/tui/index.js +99 -505
  353. package/dist/tui/index.js.map +1 -1
  354. package/dist/tui/overlays.d.ts +28 -0
  355. package/dist/tui/overlays.d.ts.map +1 -0
  356. package/dist/tui/overlays.js +154 -0
  357. package/dist/tui/overlays.js.map +1 -0
  358. package/dist/tui/themes.d.ts +5 -0
  359. package/dist/tui/themes.d.ts.map +1 -0
  360. package/dist/tui/themes.js +38 -0
  361. package/dist/tui/themes.js.map +1 -0
  362. package/dist/web/index.d.ts.map +1 -1
  363. package/dist/web/index.js +60 -122
  364. package/dist/web/index.js.map +1 -1
  365. package/dist/web/public/index.html +1 -1
  366. package/dist/web/public/messages.js +41 -25
  367. package/dist/web/public/state.js +72 -72
  368. package/dist/web/public/style.css +1126 -996
  369. package/dist/web/public/websocket.js +267 -267
  370. package/dist/web/session-bridge.d.ts.map +1 -1
  371. package/dist/web/session-bridge.js +82 -3
  372. package/dist/web/session-bridge.js.map +1 -1
  373. package/docs/ast-tools.md +117 -0
  374. package/package.json +60 -55
  375. package/scripts/check-line-count.mjs +53 -0
  376. package/scripts/copy-ast-wasm.mjs +32 -0
  377. package/src/agent/__tests__/session-restore.test.ts +284 -0
  378. package/src/agent/commands.ts +274 -0
  379. package/src/agent/compaction.ts +9 -2
  380. package/src/agent/index.ts +30 -25
  381. package/src/agent/memory.ts +60 -0
  382. package/src/agent/prompt-builder.ts +100 -26
  383. package/src/agent/providers/anthropic.ts +179 -179
  384. package/src/agent/providers/google.ts +210 -210
  385. package/src/agent/providers/ollama.ts +186 -186
  386. package/src/agent/providers/openai.ts +18 -3
  387. package/src/agent/session-chat.ts +480 -0
  388. package/src/agent/session-display.ts +195 -0
  389. package/src/agent/session-persistence.ts +146 -0
  390. package/src/agent/session-utils.ts +54 -0
  391. package/src/agent/session.ts +261 -718
  392. package/src/cli/index.ts +73 -4
  393. package/src/config/index.ts +342 -342
  394. package/src/index.ts +30 -3
  395. package/src/tools/__tests__/bash.test.ts +187 -1
  396. package/src/tools/__tests__/edit.test.ts +275 -11
  397. package/src/tools/__tests__/fetch.test.ts +1 -1
  398. package/src/tools/__tests__/glob.test.ts +1 -1
  399. package/src/tools/__tests__/grep.test.ts +1 -1
  400. package/src/tools/__tests__/ls.test.ts +1 -1
  401. package/src/tools/__tests__/read.test.ts +1 -1
  402. package/src/tools/__tests__/registry.test.ts +332 -0
  403. package/src/tools/__tests__/write_plan.test.ts +1 -1
  404. package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
  405. package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
  406. package/src/tools/codebase/ast/adapter.ts +44 -0
  407. package/src/tools/codebase/ast/tools.ts +322 -0
  408. package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
  409. package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
  410. package/src/tools/codebase/ast/types.ts +102 -0
  411. package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  412. package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  413. package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  414. package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  415. package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  416. package/src/tools/{find.ts → codebase/find.ts} +4 -2
  417. package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
  418. package/src/tools/{grep.ts → codebase/grep.ts} +4 -2
  419. package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
  420. package/src/tools/{read.ts → codebase/read.ts} +5 -3
  421. package/src/tools/edit/diff-compute.ts +21 -0
  422. package/src/tools/edit/diff-render.ts +145 -0
  423. package/src/tools/edit/edit.ts +468 -0
  424. package/src/tools/edit/matcher.ts +70 -0
  425. package/src/tools/edit/parser.ts +131 -0
  426. package/src/tools/edit/types.ts +19 -0
  427. package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
  428. package/src/tools/execute/bash/constants.ts +93 -0
  429. package/src/tools/execute/bash/errors.ts +304 -0
  430. package/src/tools/execute/bash/execution.ts +211 -0
  431. package/src/tools/execute/bash/index.ts +131 -0
  432. package/src/tools/execute/bash/platform.ts +65 -0
  433. package/src/tools/execute/bash/security.ts +28 -0
  434. package/src/tools/execute/bash/validation.ts +78 -0
  435. package/src/tools/execute/git.ts +209 -0
  436. package/src/tools/execute/task_complete.ts +40 -0
  437. package/src/tools/install/clawhub.ts +222 -0
  438. package/src/tools/install/constants.ts +4 -0
  439. package/src/tools/install/install_skill.ts +150 -0
  440. package/src/tools/install/npm.ts +60 -0
  441. package/src/tools/install/skill-content.ts +108 -0
  442. package/src/tools/memory/delete_memory.ts +46 -0
  443. package/src/tools/memory/list_memory.ts +53 -0
  444. package/src/tools/memory/save_memory.ts +45 -0
  445. package/src/tools/registry.ts +119 -19
  446. package/src/tools/tree.ts +103 -0
  447. package/src/tools/types.ts +13 -0
  448. package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
  449. package/src/tools/{search.ts → web/search.ts} +4 -2
  450. package/src/tui/commands.ts +135 -0
  451. package/src/tui/components/colors.ts +52 -0
  452. package/src/tui/components/header-bar.ts +88 -0
  453. package/src/tui/components/index.ts +8 -0
  454. package/src/tui/components/landing-screen.ts +67 -0
  455. package/src/tui/components/pixel-logo.ts +33 -0
  456. package/src/tui/components/plan-utils.ts +69 -0
  457. package/src/tui/components/plans-list.ts +55 -0
  458. package/src/tui/components/rich-footer.ts +146 -0
  459. package/src/tui/components/todo-panel.ts +95 -0
  460. package/src/tui/index.ts +335 -786
  461. package/src/tui/overlays.ts +203 -0
  462. package/src/tui/themes.ts +41 -0
  463. package/src/web/index.ts +64 -127
  464. package/src/web/public/index.html +1 -1
  465. package/src/web/public/messages.js +41 -25
  466. package/src/web/public/state.js +72 -72
  467. package/src/web/public/style.css +1126 -996
  468. package/src/web/public/websocket.js +267 -267
  469. package/src/web/session-bridge.ts +272 -193
  470. package/AGENT.md +0 -99
  471. package/src/tools/bash.ts +0 -125
  472. package/src/tools/edit.ts +0 -747
  473. package/src/tools/git.ts +0 -76
  474. package/src/tools/install_skill.ts +0 -539
  475. package/src/tui/components.ts +0 -635
@@ -1,6 +1,74 @@
1
- import { spawn } from 'node:child_process';
1
+ import { execSync, spawn } from 'node:child_process';
2
+ import * as fs from 'node:fs';
2
3
  import * as os from 'node:os';
4
+ import * as path from 'node:path';
3
5
  import { fmtErr } from './errors.js';
6
+ // ── Constants ────────────────────────────────────────────────────────────────
7
+ /** Maximum output length (chars) to prevent context overflow. */
8
+ const MAX_OUTPUT_LENGTH = 10_000;
9
+ /** Maximum stderr length included in error messages */
10
+ const MAX_ERROR_LENGTH = 2_000;
11
+ /** Maximum allowed timeout (10 minutes) */
12
+ const MAX_TIMEOUT = 600_000;
13
+ /** Minimum allowed timeout (100ms) */
14
+ const MIN_TIMEOUT = 100;
15
+ /** Default timeout (2 minutes) */
16
+ const DEFAULT_TIMEOUT = 120_000;
17
+ /**
18
+ * Patterns that indicate a destructive command.
19
+ * If matched, the tool will warn the user instead of executing.
20
+ */
21
+ const DESTRUCTIVE_PATTERNS = [
22
+ // ═══════════════════════
23
+ // Unix destructive
24
+ // ═══════════════════════
25
+ { regex: /\brm\s+-(?:rf|fr)\s+\/$/i, hint: 'rm -rf / would destroy the system' },
26
+ { regex: /\brm\s+-(?:rf|fr)\s+\/\*/i, hint: 'rm -rf /* would destroy the system' },
27
+ { regex: /\brm\s+-(?:rf|fr)\s+~$/i, hint: 'rm -rf ~ would delete the home directory' },
28
+ { regex: /\b(?:mkfs|dd)\s+(?:\/dev|\\.)/i, hint: 'mkfs/dd would destroy a disk device' },
29
+ { regex: /\bchmod\s+-R\s+0{4}\b/, hint: 'chmod -R 0000 would make files inaccessible' },
30
+ {
31
+ regex: /\b(?:shutdown|reboot|halt|poweroff)\s+(?:-h|-r|-now|now)\b/i,
32
+ hint: 'shutdown/reboot would halt the system',
33
+ },
34
+ {
35
+ regex: /\b(?:killall|pkill)\s+-9\s+/i,
36
+ hint: 'killall -9 would terminate all matching processes',
37
+ },
38
+ // ═══════════════════════
39
+ // Windows destructive
40
+ // ═══════════════════════
41
+ {
42
+ regex: /\b(?:Remove-Item|rmdir|rd)\s+.*(?:-Recurse|-R)\s+.*(?:-Force|-F)\b/i,
43
+ hint: 'recursive force delete would destroy data',
44
+ },
45
+ {
46
+ regex: /\b(?:Remove-Item|rmdir|rd)\s+\w:\s*[\\/]/i,
47
+ hint: 'recursive delete on drive root would destroy data',
48
+ },
49
+ {
50
+ regex: /\b(?:del|erase)\s+.*(?:\/F|\/S)\b/i,
51
+ hint: 'force delete would destroy files irreversibly',
52
+ },
53
+ { regex: /\bformat\s+\w:\s*\/[Qq]/i, hint: 'format would destroy a drive' },
54
+ { regex: /\b(?:diskpart|clean-all)\b/i, hint: 'diskpart clean-all would destroy all partitions' },
55
+ { regex: /\bClear-Item\s+/i, hint: 'Clear-Item would delete content irreversibly' },
56
+ { regex: /\bClear-Content\s+/i, hint: 'Clear-Content would delete file content irreversibly' },
57
+ { regex: /\bRemove-ItemProperty\s+/i, hint: 'Remove-ItemProperty would delete registry keys' },
58
+ { regex: /\bRemove-Variable\s+-Force\b/i, hint: 'Remove-Variable -Force would delete variables' },
59
+ { regex: /\breg\s+delete\s+/i, hint: 'reg delete would destroy registry entries' },
60
+ { regex: /\bwmic\s+\w+\s+delete\b/i, hint: 'wmic delete would destroy system resources' },
61
+ { regex: /\b(?:cipher|sdelete)\s+\/w:/i, hint: 'secure delete would irreversibly wipe data' },
62
+ {
63
+ regex: /\[System\.IO\.Directory\]::Delete\s*\(/i,
64
+ hint: 'Directory.Delete would destroy data irreversibly',
65
+ },
66
+ {
67
+ regex: /\[System\.IO\.File\]::Delete\s*\(/i,
68
+ hint: 'File.Delete would destroy data irreversibly',
69
+ },
70
+ ];
71
+ // ── Platform detection ───────────────────────────────────────────────────────
4
72
  function detectEnv() {
5
73
  const platform = os.platform();
6
74
  const release = os.release();
@@ -18,90 +86,642 @@ function detectEnv() {
18
86
  };
19
87
  }
20
88
  const env = detectEnv();
21
- function execCommand(command, timeout) {
89
+ function validateInput(input) {
90
+ // command: must be a non-empty string
91
+ const command = input.command;
92
+ if (command === undefined || command === null) {
93
+ return { ok: false, error: 'command is required' };
94
+ }
95
+ if (typeof command !== 'string') {
96
+ return { ok: false, error: `command must be a string, got ${typeof command}` };
97
+ }
98
+ if (command.trim() === '') {
99
+ return { ok: false, error: 'command must not be empty or whitespace-only' };
100
+ }
101
+ // timeout: must be a number (or default), within bounds
102
+ let timeout = DEFAULT_TIMEOUT;
103
+ if (input.timeout !== undefined) {
104
+ if (typeof input.timeout === 'number' &&
105
+ !Number.isNaN(input.timeout) &&
106
+ Number.isFinite(input.timeout)) {
107
+ timeout = Math.round(input.timeout);
108
+ if (timeout < MIN_TIMEOUT) {
109
+ timeout = MIN_TIMEOUT;
110
+ }
111
+ else if (timeout > MAX_TIMEOUT) {
112
+ timeout = MAX_TIMEOUT;
113
+ }
114
+ }
115
+ // If timeout is not a valid number, silently use default
116
+ }
117
+ // description: optional string
118
+ const description = typeof input.description === 'string' ? input.description : '';
119
+ // cwd: optional string, must be a valid directory, no path traversal
120
+ let cwd;
121
+ if (typeof input.cwd === 'string' && input.cwd.trim()) {
122
+ const rawCwd = input.cwd.trim();
123
+ // Path traversal protection
124
+ if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(rawCwd)) {
125
+ return {
126
+ ok: false,
127
+ error: `cwd contains path traversal (..) which is not allowed: "${rawCwd}"`,
128
+ };
129
+ }
130
+ const resolvedCwd = path.resolve(rawCwd);
131
+ if (!fs.existsSync(resolvedCwd)) {
132
+ return { ok: false, error: `cwd directory does not exist: "${resolvedCwd}"` };
133
+ }
134
+ if (!fs.statSync(resolvedCwd).isDirectory()) {
135
+ return { ok: false, error: `cwd is not a directory: "${resolvedCwd}"` };
136
+ }
137
+ cwd = resolvedCwd;
138
+ }
139
+ return { ok: true, command: command.trim(), timeout, description, cwd };
140
+ }
141
+ // ── Security check ───────────────────────────────────────────────────────────
142
+ /**
143
+ * Check if a command matches destructive patterns.
144
+ * Returns a warning string if detected, null otherwise.
145
+ */
146
+ function checkDestructive(command) {
147
+ for (const { regex, hint } of DESTRUCTIVE_PATTERNS) {
148
+ if (regex.test(command)) {
149
+ return hint;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ // ── Encoding detection (Windows) ─────────────────────────────────────────────
155
+ /**
156
+ * Detect Windows console code page to handle non-UTF-8 output correctly.
157
+ * Falls back to 'utf-8' if detection fails.
158
+ * On non-Windows platforms, always returns 'utf-8'.
159
+ */
160
+ function detectEncoding() {
161
+ if (os.platform() !== 'win32')
162
+ return 'utf-8';
163
+ try {
164
+ const result = execSync('[Console]::OutputEncoding.CodePage', {
165
+ stdio: ['ignore', 'pipe', 'pipe'],
166
+ timeout: 2000,
167
+ windowsHide: true,
168
+ encoding: 'utf-8',
169
+ shell: 'powershell.exe',
170
+ });
171
+ const cp = Number.parseInt(result.toString().trim(), 10);
172
+ // Common Windows code pages that need special handling
173
+ if (cp === 936)
174
+ return 'gbk'; // Chinese (Simplified)
175
+ if (cp === 950)
176
+ return 'big5'; // Chinese (Traditional)
177
+ if (cp === 932)
178
+ return 'shift-jis'; // Japanese
179
+ if (cp === 949)
180
+ return 'euc-kr'; // Korean
181
+ if (cp === 1250 ||
182
+ cp === 1251 ||
183
+ cp === 1252 ||
184
+ cp === 1253 ||
185
+ cp === 1254 ||
186
+ cp === 1255 ||
187
+ cp === 1256 ||
188
+ cp === 1257) {
189
+ return `cp${cp}`; // Windows code page
190
+ }
191
+ return 'utf-8';
192
+ }
193
+ catch {
194
+ return 'utf-8';
195
+ }
196
+ }
197
+ // Cache encoding detection once at startup
198
+ const ENCODING = detectEncoding();
199
+ // ── Process killing ──────────────────────────────────────────────────────────
200
+ /**
201
+ * Kill a child process tree reliably across platforms.
202
+ * - On Windows: uses `taskkill /F /T` for force-kill of the process tree when possible.
203
+ * - On Unix: sends SIGTERM first, then SIGKILL after a grace period.
204
+ * - Handles missing pid and protects against thrown errors.
205
+ */
206
+ function killProcess(child) {
207
+ try {
208
+ if (!child || typeof child.pid !== 'number' || Number.isNaN(child.pid)) {
209
+ // Best-effort kill if pid is not available
210
+ try {
211
+ child.kill();
212
+ }
213
+ catch {
214
+ /* ignore */
215
+ }
216
+ return undefined;
217
+ }
218
+ if (os.platform() === 'win32') {
219
+ try {
220
+ // Use taskkill to terminate the whole process tree. If it fails, fall back to child.kill().
221
+ execSync(`taskkill /F /T /PID ${child.pid}`, {
222
+ stdio: 'ignore',
223
+ timeout: 3000,
224
+ });
225
+ }
226
+ catch {
227
+ try {
228
+ child.kill();
229
+ }
230
+ catch {
231
+ /* ignore */
232
+ }
233
+ }
234
+ return undefined;
235
+ }
236
+ else {
237
+ try {
238
+ child.kill('SIGTERM');
239
+ }
240
+ catch {
241
+ /* ignore */
242
+ }
243
+ // Track timer so we can force-KILL after a grace period
244
+ const killTimer = setTimeout(() => {
245
+ try {
246
+ child.kill('SIGKILL');
247
+ }
248
+ catch {
249
+ /* already exited or cannot kill */
250
+ }
251
+ }, 5000);
252
+ // Ensure we clean up the timer and the listener when the child exits
253
+ const onExit = () => {
254
+ try {
255
+ clearTimeout(killTimer);
256
+ }
257
+ catch {
258
+ /* ignore */
259
+ }
260
+ try {
261
+ child.removeListener('exit', onExit);
262
+ }
263
+ catch {
264
+ /* ignore */
265
+ }
266
+ };
267
+ child.on('exit', onExit);
268
+ return killTimer;
269
+ }
270
+ }
271
+ catch {
272
+ return undefined;
273
+ }
274
+ }
275
+ function execCommand(command, timeout, cwd) {
22
276
  return new Promise(resolve => {
23
277
  const isWindows = os.platform() === 'win32';
24
278
  const shellExe = isWindows ? 'powershell.exe' : process.env.SHELL || '/bin/sh';
25
279
  const shellArgs = isWindows
26
280
  ? ['-NoProfile', '-NonInteractive', '-Command', command]
27
281
  : ['-c', command];
28
- const child = spawn(shellExe, shellArgs, {
29
- cwd: process.cwd(),
30
- env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
31
- windowsHide: true,
32
- });
282
+ // Protect against spawn throwing synchronously (e.g. missing shell)
283
+ let child;
284
+ try {
285
+ child = spawn(shellExe, shellArgs, {
286
+ cwd: cwd || process.cwd(),
287
+ env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
288
+ windowsHide: true,
289
+ });
290
+ }
291
+ catch (err) {
292
+ // Return a normalized error result rather than throwing
293
+ resolve({ stdout: '', stderr: fmtErr(err), exitCode: 1, timedOut: false });
294
+ return;
295
+ }
33
296
  let stdout = '';
34
297
  let stderr = '';
298
+ let timedOut = false;
299
+ // Keep memory bounded by limiting how much we accumulate for each stream
300
+ const MAX_STDOUT_BUFFER = Math.max(MAX_OUTPUT_LENGTH * 2, 20000);
301
+ const MAX_STDERR_BUFFER = Math.max(MAX_ERROR_LENGTH * 2, 4000);
302
+ let stdoutTruncated = false;
303
+ let stderrTruncated = false;
304
+ // Track which encodings work for each stream (per-chunk fallback).
305
+ let stdoutEncoding;
306
+ let stderrEncoding;
307
+ let treeKillTimer;
35
308
  const timer = setTimeout(() => {
36
- child.kill('SIGTERM');
37
- setTimeout(() => {
309
+ timedOut = true;
310
+ // Attempt to kill the process tree; capture any returned timer so we can clear it on close
311
+ treeKillTimer = killProcess(child);
312
+ }, timeout);
313
+ child.stdout?.on('data', (data) => {
314
+ if (stdout.length >= MAX_STDOUT_BUFFER) {
315
+ stdoutTruncated = true;
316
+ return;
317
+ }
318
+ try {
319
+ const enc = stdoutEncoding || ENCODING;
320
+ stdout += data.toString(enc);
321
+ stdoutEncoding = enc;
322
+ }
323
+ catch {
324
+ // Detected encoding failed; fall back to utf-8
38
325
  try {
39
- child.kill('SIGKILL');
326
+ stdout += data.toString('utf-8');
327
+ stdoutEncoding = 'utf-8';
40
328
  }
41
329
  catch {
42
- /* already exited */
330
+ // Give up on this chunk
43
331
  }
44
- }, 5000);
45
- }, timeout);
46
- child.stdout?.on('data', (data) => {
47
- stdout += data.toString('utf-8');
332
+ }
48
333
  });
49
334
  child.stderr?.on('data', (data) => {
50
- stderr += data.toString('utf-8');
51
- });
52
- child.on('close', code => {
53
- clearTimeout(timer);
54
- resolve({ stdout, stderr, exitCode: code ?? 0 });
335
+ if (stderr.length >= MAX_STDERR_BUFFER) {
336
+ stderrTruncated = true;
337
+ return;
338
+ }
339
+ try {
340
+ const enc = stderrEncoding || ENCODING;
341
+ stderr += data.toString(enc);
342
+ stderrEncoding = enc;
343
+ }
344
+ catch {
345
+ try {
346
+ stderr += data.toString('utf-8');
347
+ stderrEncoding = 'utf-8';
348
+ }
349
+ catch {
350
+ // ignore
351
+ }
352
+ }
55
353
  });
354
+ const finish = (code) => {
355
+ try {
356
+ clearTimeout(timer);
357
+ }
358
+ catch {
359
+ /* ignore */
360
+ }
361
+ if (treeKillTimer) {
362
+ try {
363
+ clearTimeout(treeKillTimer);
364
+ }
365
+ catch {
366
+ /* ignore */
367
+ }
368
+ }
369
+ // If we truncated during streaming, append a short notice so final output can indicate truncation
370
+ if (stdoutTruncated)
371
+ stdout += '\n... [stdout truncated due to size limit]';
372
+ if (stderrTruncated)
373
+ stderr += '\n... [stderr truncated due to size limit]';
374
+ resolve({ stdout, stderr, exitCode: code ?? 0, timedOut });
375
+ };
376
+ child.on('close', code => finish(code));
56
377
  child.on('error', err => {
57
- clearTimeout(timer);
58
- resolve({ stdout, stderr: stderr + fmtErr(err), exitCode: 1 });
378
+ try {
379
+ clearTimeout(timer);
380
+ }
381
+ catch {
382
+ /* ignore */
383
+ }
384
+ if (treeKillTimer) {
385
+ try {
386
+ clearTimeout(treeKillTimer);
387
+ }
388
+ catch {
389
+ /* ignore */
390
+ }
391
+ }
392
+ // Include the spawn error message in stderr so callers can surface it
393
+ const errMsg = fmtErr(err);
394
+ if (errMsg)
395
+ stderr += (stderr ? '\n' : '') + errMsg;
396
+ resolve({ stdout, stderr, exitCode: 1, timedOut });
59
397
  });
60
398
  });
61
399
  }
400
+ // ── Output helpers ───────────────────────────────────────────────────────────
401
+ /**
402
+ * Truncate a string to maxLength chars. If truncated, appends a warning.
403
+ * Returns the (possibly truncated) string.
404
+ */
405
+ function truncateOutput(s, maxLength, label) {
406
+ if (s.length <= maxLength)
407
+ return s;
408
+ const truncated = s.slice(0, maxLength);
409
+ const warning = `\n... [${label} truncated at ${maxLength} chars, original ${s.length} chars]`;
410
+ return truncated + warning;
411
+ }
412
+ // ── Sensitive data redaction ────────────────────────────────────────────
413
+ /**
414
+ * Patterns that match sensitive data (API keys, tokens, passwords, etc.)
415
+ * in command output. Matching content is replaced with "[REDACTED]".
416
+ */
417
+ const SENSITIVE_PATTERNS = [
418
+ // Generic API keys / tokens
419
+ /(?:api[_-]?key|apikey|token|secret|password|passwd|credential|auth[_-]?token)[=:]\s*['"]?[A-Za-z0-9_\-.]{16,}/gi,
420
+ // AWS keys
421
+ /(?:AKIA[0-9A-Z]{16}|[A-Za-z0-9+/]{40}\s*=[=]{0,2})/g,
422
+ // GitHub tokens
423
+ /gh[pousr]_[A-Za-z0-9_]{36,}/g,
424
+ // npm tokens
425
+ /npm_[A-Za-z0-9]{36,}/g,
426
+ // Slack tokens
427
+ /xox[baprs]-[A-Za-z0-9-]{24,}/g,
428
+ // SSH private key content (multi-line)
429
+ /-----BEGIN[ A-Z]+PRIVATE KEY-----[\s\S]*?-----END[ A-Z]+PRIVATE KEY-----/g,
430
+ // JWT tokens
431
+ /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
432
+ // Generic password-like strings in env vars
433
+ /export\s+\w*(?:PASS|SECRET|TOKEN|KEY)\w*\s*=\s*['"]?\S+['"]?/gi,
434
+ /\$env:\w*(?:PASS|SECRET|TOKEN|KEY)\w*\s*=\s*['"]?\S+['"]?/gi,
435
+ ];
436
+ /**
437
+ * Redact sensitive data from a string by replacing matches with "[REDACTED]".
438
+ * Returns the redacted string.
439
+ */
440
+ function redactSensitive(s) {
441
+ let result = s;
442
+ for (const pattern of SENSITIVE_PATTERNS) {
443
+ result = result.replace(pattern, '[REDACTED]');
444
+ }
445
+ return result;
446
+ }
447
+ /**
448
+ * Check if stderr indicates common PowerShell errors and return a helpful message.
449
+ * Returns a tip string if detected, null otherwise.
450
+ */
451
+ function checkPowerShellError(stderr, command) {
452
+ const errLower = stderr.toLowerCase();
453
+ // Execution policy errors
454
+ if (/execution.?policy/i.test(errLower) || /running scripts is disabled/i.test(errLower)) {
455
+ return 'PowerShell execution policy is blocking this command. Try: `powershell -ExecutionPolicy Bypass -Command "..."`';
456
+ }
457
+ // Module not found
458
+ if (/module.*not (found|available)/i.test(errLower) ||
459
+ /could not (find|load).*module/i.test(errLower)) {
460
+ const moduleMatch = stderr.match(/module\s+['"]?(\w+)['"]?/i);
461
+ const moduleName = moduleMatch ? moduleMatch[1] : '<unknown>';
462
+ return `PowerShell module not found: "${moduleName}". Install it with: Install-Module ${moduleName} -Force`;
463
+ }
464
+ // Cmdlet not found
465
+ if (/the term '(\w+)' is not recognized/i.test(errLower)) {
466
+ const cmdMatch = stderr.match(/the term '(\w+)'/i);
467
+ const cmdName = cmdMatch ? cmdMatch[1] : command.split(/\s+/)[0] || command;
468
+ return `Cmdlet not found: "${cmdName}". This command may need a module or be installed separately.`;
469
+ }
470
+ // Access denied
471
+ if (/access is denied/i.test(errLower) ||
472
+ /permission denied/i.test(errLower) ||
473
+ /unauthorized/i.test(errLower)) {
474
+ return 'Access denied. Try running as Administrator (on Windows) or using sudo (on Unix).';
475
+ }
476
+ // Path not found
477
+ if (/cannot find (path|drive)/i.test(errLower) || /path.*does not exist/i.test(errLower)) {
478
+ return 'The specified path or drive was not found. Check that the path exists and is accessible.';
479
+ }
480
+ return null;
481
+ }
482
+ /**
483
+ * Check if stderr output indicates a "command not found" error.
484
+ * Returns a platform-appropriate suggestion if detected, null otherwise.
485
+ */
486
+ function checkCommandNotFound(stderr, command) {
487
+ const isWindows = os.platform() === 'win32';
488
+ const errLower = stderr.toLowerCase();
489
+ if (isWindows) {
490
+ // PowerShell "not recognized" patterns
491
+ if (/not recognized/.test(errLower) ||
492
+ /is not recognized/.test(errLower) ||
493
+ /找不到/.test(errLower) ||
494
+ /command not found/.test(errLower) ||
495
+ /not found/.test(errLower) ||
496
+ /neither the command nor/.test(errLower)) {
497
+ // Extract the command name that wasn't found (first word of the command)
498
+ const cmdName = command.split(/\s+/)[0] || command;
499
+ return `Command not found: "${cmdName}". It may be a Unix command not available on Windows. Use "where" to find available commands or use PowerShell-native alternatives.`;
500
+ }
501
+ }
502
+ else {
503
+ // Unix "not found" patterns
504
+ if (/not found/.test(errLower) ||
505
+ /command not found/.test(errLower) ||
506
+ /no such file/.test(errLower)) {
507
+ const cmdName = command.split(/\s+/)[0] || command;
508
+ return `Command not found: "${cmdName}". Check that it is installed, or use "which ${cmdName}" to find its location.`;
509
+ }
510
+ }
511
+ return null;
512
+ }
513
+ /**
514
+ * Build a helpful error message for a failed command, including
515
+ * platform-specific suggestions when applicable.
516
+ */
517
+ function buildErrorMsg(exitCode, stderr, command, timedOut, cwd) {
518
+ const parts = [];
519
+ if (timedOut) {
520
+ parts.push(`Command timed out (exit code ${exitCode})`);
521
+ }
522
+ else {
523
+ parts.push(`Command exited with code ${exitCode}`);
524
+ }
525
+ // Check for "command not found" first (before including full stderr)
526
+ const notFoundMsg = checkCommandNotFound(stderr, command);
527
+ if (notFoundMsg) {
528
+ parts.push(notFoundMsg);
529
+ }
530
+ // Check for common PowerShell errors
531
+ if (os.platform() === 'win32') {
532
+ const psErrMsg = checkPowerShellError(stderr, command);
533
+ if (psErrMsg) {
534
+ parts.push(psErrMsg);
535
+ }
536
+ }
537
+ // Include stderr content (truncated and redacted)
538
+ const stderrTrimmed = stderr.trim();
539
+ if (stderrTrimmed) {
540
+ const redactedStderr = redactSensitive(stderrTrimmed);
541
+ parts.push(`stderr:\n${truncateOutput(redactedStderr, MAX_ERROR_LENGTH, 'stderr')}`);
542
+ }
543
+ // Platform-specific suggestions for common mistakes
544
+ const isWindows = os.platform() === 'win32';
545
+ if (isWindows) {
546
+ // Common Windows command mistakes
547
+ if (/ls\b/.test(command) && !/ls\b/.test(command.replace(/".*"/g, ''))) {
548
+ parts.push('💡 Tip: On Windows, use `dir` instead of `ls`, or `Get-ChildItem` in PowerShell.');
549
+ }
550
+ if (/grep\b/.test(command)) {
551
+ parts.push('💡 Tip: On Windows, use `findstr` or `Select-String` instead of `grep`.');
552
+ }
553
+ if (/touch\b/.test(command)) {
554
+ parts.push('💡 Tip: On Windows, `touch` is not available. Use `New-Item` or `edit` tool to create files.');
555
+ }
556
+ if (/ps\b/.test(command)) {
557
+ parts.push('💡 Tip: On Windows, use `Get-Process` instead of `ps` for listing processes.');
558
+ }
559
+ if (/pwd\b/.test(command)) {
560
+ parts.push('💡 Tip: On Windows, use `Get-Location` or `$pwd` instead of `pwd`.');
561
+ }
562
+ if (/chmod\b/.test(command) ||
563
+ /cp\b/.test(command) ||
564
+ /mv\b/.test(command) ||
565
+ /rm\b/.test(command) ||
566
+ /mkdir\b/.test(command)) {
567
+ parts.push('💡 Tip: On Windows, use native equivalents: `copy`, `move`, `del`, `mkdir`, `icacls` instead of Unix commands.');
568
+ }
569
+ if (/cat\b/.test(command)) {
570
+ parts.push('💡 Tip: On Windows, use `type` or `Get-Content` instead of `cat`.');
571
+ }
572
+ if (/which\b/.test(command)) {
573
+ parts.push('💡 Tip: On Windows, use `where` instead of `which` to find executable locations.');
574
+ }
575
+ if (/less\b/.test(command) || /more\b/.test(command)) {
576
+ parts.push('💡 Tip: On Windows, use `more` or `Out-Host -Paging` for paging output.');
577
+ }
578
+ if (/sort\b/.test(command) && !/sort-object/i.test(command) && !/Sort-Object/i.test(command)) {
579
+ parts.push('💡 Tip: On Windows, use `Sort-Object` instead of `sort`.');
580
+ }
581
+ if (/head\b/.test(command)) {
582
+ parts.push('💡 Tip: On Windows, use `Select-Object -First` instead of `head`.');
583
+ }
584
+ if (/tail\b/.test(command)) {
585
+ parts.push('💡 Tip: On Windows, use `Get-Content -Tail` or `Select-Object -Last` instead of `tail`.');
586
+ }
587
+ if (/diff\b/.test(command) || /cmp\b/.test(command)) {
588
+ parts.push('💡 Tip: On Windows, use `Compare-Object` instead of `diff`/`cmp`.');
589
+ }
590
+ if (/curl\b/.test(command) && !/curl\b/.test(command.replace(/".*"/g, ''))) {
591
+ parts.push('💡 Tip: On Windows, use `Invoke-WebRequest` or `Invoke-RestMethod` instead of `curl`. curl is available in newer Windows builds.');
592
+ }
593
+ if (/wget\b/.test(command)) {
594
+ parts.push('💡 Tip: On Windows, use `Invoke-WebRequest` instead of `wget`.');
595
+ }
596
+ if (/env\b/.test(command)) {
597
+ parts.push('💡 Tip: On Windows, use `Get-ChildItem Env:` to list environment variables.');
598
+ }
599
+ if (/kill\b/.test(command) && !/stop-process/i.test(command)) {
600
+ parts.push('💡 Tip: On Windows, use `Stop-Process` or `taskkill` instead of `kill`.');
601
+ }
602
+ }
603
+ else {
604
+ // Unix-specific tips
605
+ if (/dir\b/.test(command)) {
606
+ parts.push('💡 Tip: On Unix, use `ls -la` instead of `dir`.');
607
+ }
608
+ if (/findstr\b/.test(command)) {
609
+ parts.push('💡 Tip: On Unix, use `grep` instead of `findstr`.');
610
+ }
611
+ if (/type\b/.test(command) && !/type\b/.test(command.replace(/".*"/g, ''))) {
612
+ parts.push('💡 Tip: On Unix, use `cat` instead of `type`.');
613
+ }
614
+ if (/where\b/.test(command)) {
615
+ parts.push('💡 Tip: On Unix, use `which` instead of `where` to find executable locations.');
616
+ }
617
+ if (/copy\b/.test(command)) {
618
+ parts.push('💡 Tip: On Unix, use `cp` instead of `copy`.');
619
+ }
620
+ if (/move\b/.test(command) || /ren\b/.test(command)) {
621
+ parts.push('💡 Tip: On Unix, use `mv` instead of `move`/`ren`.');
622
+ }
623
+ if (/del\b/.test(command)) {
624
+ parts.push('💡 Tip: On Unix, use `rm` instead of `del`.');
625
+ }
626
+ }
627
+ if (cwd) {
628
+ parts.push(`Working directory: ${cwd}`);
629
+ }
630
+ return parts.join('\n');
631
+ }
632
+ // ── Tool definition ──────────────────────────────────────────────────────────
62
633
  export const bashTool = {
63
634
  definition: {
64
635
  name: 'bash',
636
+ category: 'Execute',
637
+ group: 'Shell',
65
638
  description: `Execute a shell command. Returns stdout and stderr.
66
639
 
67
640
  Environment: ${env.osInfo}
68
641
  Shell: ${env.shell}
642
+ Console encoding: ${ENCODING}
69
643
 
70
- ⚠️ IMPORTANT: Do NOT use this tool to create or modify files. Use the 'edit' tool for file modifications instead.`,
644
+ ⚠️ Use this tool for READ-ONLY operations (run tests, check builds, list files).
645
+ For creating/modifying files, use the 'edit' tool instead.
646
+ For git operations (status, diff, log), use the 'git' tool instead.`,
71
647
  parameters: {
72
- command: { type: 'string', description: 'Shell command to execute', required: true },
73
- description: { type: 'string', description: 'Brief description of what the command does' },
74
- timeout: { type: 'number', description: 'Timeout in milliseconds (default: 120000)' },
648
+ command: {
649
+ type: 'string',
650
+ description: 'Shell command to execute (required, non-empty string)',
651
+ required: true,
652
+ },
653
+ description: {
654
+ type: 'string',
655
+ description: 'Brief description (e.g. "Run unit tests"). Included in the output for traceability.',
656
+ },
657
+ timeout: {
658
+ type: 'number',
659
+ description: 'Timeout in ms (100-600000, default: 120000). Increase for slow operations like npm install.',
660
+ },
661
+ cwd: {
662
+ type: 'string',
663
+ description: 'Working directory path. Use when you need to run a command inside a subdirectory. Must exist.',
664
+ },
75
665
  },
76
666
  },
77
667
  async execute(input) {
78
- const command = input.command;
79
- if (!command) {
80
- return { success: false, output: '', error: 'command is required' };
668
+ // ── Step 1: Validate input ──────────────────────────────────────────
669
+ const validated = validateInput(input);
670
+ if (!validated.ok) {
671
+ return { success: false, output: '', error: validated.error };
672
+ }
673
+ const { command, timeout, description, cwd } = validated;
674
+ // ── Step 2: Security check ──────────────────────────────────────────
675
+ const destructiveHint = checkDestructive(command);
676
+ if (destructiveHint) {
677
+ return {
678
+ success: false,
679
+ output: '',
680
+ error: `Destructive command blocked: ${destructiveHint}.\nUse the 'edit' tool for file modifications instead.`,
681
+ };
81
682
  }
82
- const timeout = input.timeout || 120_000;
83
683
  try {
84
- const { stdout, stderr, exitCode } = await execCommand(command, timeout);
684
+ const { stdout, stderr, exitCode, timedOut } = await execCommand(command, timeout, cwd);
685
+ // ── Step 3: Build output ──────────────────────────────────────────
85
686
  let output = '';
86
- if (stdout.trim())
87
- output += stdout.trim();
88
- if (stderr.trim()) {
89
- output += output ? '\n' + stderr.trim() : stderr.trim();
687
+ if (description) {
688
+ output += `[bash] ${description}\n`;
689
+ }
690
+ // Redact sensitive data from output (API keys, tokens, passwords, etc.)
691
+ const stdoutTrimmed = redactSensitive(stdout.trim());
692
+ if (stdoutTrimmed) {
693
+ output += stdoutTrimmed;
694
+ }
695
+ const stderrTrimmed = redactSensitive(stderr.trim());
696
+ if (stderrTrimmed) {
697
+ if (output)
698
+ output += '\n';
699
+ output += `(stderr):\n${stderrTrimmed}`;
700
+ }
701
+ // Apply global truncation
702
+ output = truncateOutput(output, MAX_OUTPUT_LENGTH, 'output');
703
+ if (!output || output === (description ? `[bash] ${description}\n` : '')) {
704
+ output = description ? `[bash] ${description}\n(no output)` : '(no output)';
705
+ }
706
+ if (timedOut) {
707
+ return {
708
+ success: false,
709
+ output,
710
+ error: buildErrorMsg(0, stderr, command, true, cwd),
711
+ };
90
712
  }
91
- if (!output)
92
- output = '(no output)';
93
713
  if (exitCode !== 0) {
94
714
  return {
95
715
  success: false,
96
716
  output,
97
- error: `Command exited with code ${exitCode}`,
717
+ error: buildErrorMsg(exitCode, stderr, command, false, cwd),
98
718
  };
99
719
  }
100
720
  return { success: true, output };
101
721
  }
102
722
  catch (err) {
103
723
  const msg = fmtErr(err);
104
- return { success: false, output: '', error: `Command failed: ${msg}` };
724
+ return { success: false, output: '', error: `Command failed unexpectedly: ${msg}` };
105
725
  }
106
726
  },
107
727
  };