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
@@ -0,0 +1,211 @@
1
+ import type { ChildProcess } from 'node:child_process'
2
+ import { execSync, spawn } from 'node:child_process'
3
+ import * as os from 'node:os'
4
+
5
+ import { fmtErr } from '../../errors.js'
6
+ import { MAX_ERROR_LENGTH, MAX_OUTPUT_LENGTH } from './constants.js'
7
+ import { ENCODING } from './platform.js'
8
+
9
+ interface ExecResult {
10
+ stdout: string
11
+ stderr: string
12
+ exitCode: number
13
+ timedOut: boolean
14
+ }
15
+
16
+ /**
17
+ * Kill a child process tree reliably across platforms.
18
+ * - On Windows: uses `taskkill /F /T` for force-kill of the process tree when possible.
19
+ * - On Unix: sends SIGTERM first, then SIGKILL after a grace period.
20
+ * - Handles missing pid and protects against thrown errors.
21
+ */
22
+ function killProcess(child: ChildProcess): NodeJS.Timeout | undefined {
23
+ try {
24
+ if (!child || typeof (child as any).pid !== 'number' || Number.isNaN((child as any).pid)) {
25
+ // Best-effort kill if pid is not available
26
+ try {
27
+ child.kill()
28
+ } catch {
29
+ /* ignore */
30
+ }
31
+ return undefined
32
+ }
33
+
34
+ if (os.platform() === 'win32') {
35
+ try {
36
+ // Use taskkill to terminate the whole process tree. If it fails, fall back to child.kill().
37
+ execSync(`taskkill /F /T /PID ${child.pid}`, {
38
+ stdio: 'ignore',
39
+ timeout: 3000,
40
+ })
41
+ } catch {
42
+ try {
43
+ child.kill()
44
+ } catch {
45
+ /* ignore */
46
+ }
47
+ }
48
+ return undefined
49
+ } else {
50
+ try {
51
+ child.kill('SIGTERM')
52
+ } catch {
53
+ /* ignore */
54
+ }
55
+ // Track timer so we can force-KILL after a grace period
56
+ const killTimer = setTimeout(() => {
57
+ try {
58
+ child.kill('SIGKILL')
59
+ } catch {
60
+ /* already exited or cannot kill */
61
+ }
62
+ }, 5000)
63
+ // Ensure we clean up the timer and the listener when the child exits
64
+ const onExit = () => {
65
+ try {
66
+ clearTimeout(killTimer)
67
+ } catch {
68
+ /* ignore */
69
+ }
70
+ try {
71
+ child.removeListener('exit', onExit)
72
+ } catch {
73
+ /* ignore */
74
+ }
75
+ }
76
+ child.on('exit', onExit)
77
+ return killTimer
78
+ }
79
+ } catch {
80
+ return undefined
81
+ }
82
+ }
83
+
84
+ function execCommand(command: string, timeout: number, cwd?: string): Promise<ExecResult> {
85
+ return new Promise(resolve => {
86
+ const isWindows = os.platform() === 'win32'
87
+ const shellExe = isWindows ? 'powershell.exe' : process.env.SHELL || '/bin/sh'
88
+ const shellArgs = isWindows
89
+ ? ['-NoProfile', '-NonInteractive', '-Command', command]
90
+ : ['-c', command]
91
+
92
+ // Protect against spawn throwing synchronously (e.g. missing shell)
93
+ let child: ChildProcess | undefined
94
+ try {
95
+ child = spawn(shellExe, shellArgs, {
96
+ cwd: cwd || process.cwd(),
97
+ env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
98
+ windowsHide: true,
99
+ })
100
+ } catch (err) {
101
+ // Return a normalized error result rather than throwing
102
+ resolve({ stdout: '', stderr: fmtErr(err), exitCode: 1, timedOut: false })
103
+ return
104
+ }
105
+
106
+ let stdout = ''
107
+ let stderr = ''
108
+ let timedOut = false
109
+
110
+ // Keep memory bounded by limiting how much we accumulate for each stream
111
+ const MAX_STDOUT_BUFFER = Math.max(MAX_OUTPUT_LENGTH * 2, 20000)
112
+ const MAX_STDERR_BUFFER = Math.max(MAX_ERROR_LENGTH * 2, 4000)
113
+ let stdoutTruncated = false
114
+ let stderrTruncated = false
115
+
116
+ // Track which encodings work for each stream (per-chunk fallback).
117
+ let stdoutEncoding: string | undefined
118
+ let stderrEncoding: string | undefined
119
+
120
+ let treeKillTimer: NodeJS.Timeout | undefined
121
+ const timer = setTimeout(() => {
122
+ timedOut = true
123
+ // Attempt to kill the process tree; capture any returned timer so we can clear it on close
124
+ treeKillTimer = killProcess(child!)
125
+ }, timeout)
126
+
127
+ child!.stdout?.on('data', (data: Buffer) => {
128
+ if (stdout.length >= MAX_STDOUT_BUFFER) {
129
+ stdoutTruncated = true
130
+ return
131
+ }
132
+ try {
133
+ const enc = stdoutEncoding || ENCODING
134
+ stdout += data.toString(enc as BufferEncoding)
135
+ stdoutEncoding = enc
136
+ } catch {
137
+ // Detected encoding failed; fall back to utf-8
138
+ try {
139
+ stdout += data.toString('utf-8')
140
+ stdoutEncoding = 'utf-8'
141
+ } catch {
142
+ // Give up on this chunk
143
+ }
144
+ }
145
+ })
146
+
147
+ child!.stderr?.on('data', (data: Buffer) => {
148
+ if (stderr.length >= MAX_STDERR_BUFFER) {
149
+ stderrTruncated = true
150
+ return
151
+ }
152
+ try {
153
+ const enc = stderrEncoding || ENCODING
154
+ stderr += data.toString(enc as BufferEncoding)
155
+ stderrEncoding = enc
156
+ } catch {
157
+ try {
158
+ stderr += data.toString('utf-8')
159
+ stderrEncoding = 'utf-8'
160
+ } catch {
161
+ // ignore
162
+ }
163
+ }
164
+ })
165
+
166
+ const finish = (code: number | null) => {
167
+ try {
168
+ clearTimeout(timer)
169
+ } catch {
170
+ /* ignore */
171
+ }
172
+ if (treeKillTimer) {
173
+ try {
174
+ clearTimeout(treeKillTimer)
175
+ } catch {
176
+ /* ignore */
177
+ }
178
+ }
179
+
180
+ // If we truncated during streaming, append a short notice so final output can indicate truncation
181
+ if (stdoutTruncated) stdout += '\n... [stdout truncated due to size limit]'
182
+ if (stderrTruncated) stderr += '\n... [stderr truncated due to size limit]'
183
+
184
+ resolve({ stdout, stderr, exitCode: code ?? 0, timedOut })
185
+ }
186
+
187
+ child!.on('close', code => finish(code))
188
+
189
+ child!.on('error', err => {
190
+ try {
191
+ clearTimeout(timer)
192
+ } catch {
193
+ /* ignore */
194
+ }
195
+ if (treeKillTimer) {
196
+ try {
197
+ clearTimeout(treeKillTimer)
198
+ } catch {
199
+ /* ignore */
200
+ }
201
+ }
202
+ // Include the spawn error message in stderr so callers can surface it
203
+ const errMsg = fmtErr(err)
204
+ if (errMsg) stderr += (stderr ? '\n' : '') + errMsg
205
+ resolve({ stdout, stderr, exitCode: 1, timedOut })
206
+ })
207
+ })
208
+ }
209
+
210
+ export type { ExecResult }
211
+ export { execCommand, killProcess }
@@ -0,0 +1,131 @@
1
+ import * as os from 'node:os'
2
+
3
+ import { fmtErr } from '../../errors.js'
4
+ import type { Tool, ToolResult } from '../../types.js'
5
+ import { MAX_OUTPUT_LENGTH } from './constants.js'
6
+ import { buildErrorMsg, extractPowerShellNativeOutput, truncateOutput } from './errors.js'
7
+ import { execCommand } from './execution.js'
8
+ import { ENCODING, env } from './platform.js'
9
+ import { checkDestructive, redactSensitive } from './security.js'
10
+ import { validateInput } from './validation.js'
11
+
12
+ export const bashTool: Tool = {
13
+ definition: {
14
+ name: 'bash',
15
+ category: 'Execute',
16
+ group: 'Shell',
17
+ description: `Execute a shell command. Returns stdout and stderr.
18
+
19
+ Environment: ${env.osInfo}
20
+ Shell: ${env.shell}
21
+ Console encoding: ${ENCODING}
22
+
23
+ ⚠️ Use this tool for READ-ONLY operations (run tests, check builds, list files).
24
+ For creating/modifying files, use the 'edit' tool instead.
25
+ For git operations (status, diff, log), use the 'git' tool instead.`,
26
+ parameters: {
27
+ command: {
28
+ type: 'string',
29
+ description: 'Shell command to execute (required, non-empty string)',
30
+ required: true,
31
+ },
32
+ description: {
33
+ type: 'string',
34
+ description:
35
+ 'Brief description (e.g. "Run unit tests"). Included in the output for traceability.',
36
+ },
37
+ timeout: {
38
+ type: 'number',
39
+ description:
40
+ 'Timeout in ms (100-600000, default: 120000). Increase for slow operations like npm install.',
41
+ },
42
+ cwd: {
43
+ type: 'string',
44
+ description:
45
+ 'Working directory path. Use when you need to run a command inside a subdirectory. Must exist.',
46
+ },
47
+ },
48
+ },
49
+ async execute(input): Promise<ToolResult> {
50
+ // ── Step 1: Validate input ──────────────────────────────────────────
51
+ const validated = validateInput(input)
52
+ if (!validated.ok) {
53
+ return { success: false, output: '', error: validated.error }
54
+ }
55
+
56
+ const { command, timeout, description, cwd } = validated
57
+
58
+ // ── Step 2: Security check ──────────────────────────────────────────
59
+ const destructiveHint = checkDestructive(command)
60
+ if (destructiveHint) {
61
+ return {
62
+ success: false,
63
+ output: '',
64
+ error: `Destructive command blocked: ${destructiveHint}.\nUse the 'edit' tool for file modifications instead.`,
65
+ }
66
+ }
67
+
68
+ try {
69
+ const { stdout, stderr, exitCode, timedOut } = await execCommand(command, timeout, cwd)
70
+
71
+ // ── Step 3: Build output ──────────────────────────────────────────
72
+ let output = ''
73
+ if (description) {
74
+ output += `[bash] ${description}\n`
75
+ }
76
+
77
+ // Redact sensitive data from output (API keys, tokens, passwords, etc.)
78
+ const stdoutTrimmed = redactSensitive(stdout.trim())
79
+ if (stdoutTrimmed) {
80
+ output += stdoutTrimmed
81
+ }
82
+
83
+ let stderrTrimmed = redactSensitive(stderr.trim())
84
+
85
+ // On Windows, PowerShell wraps native command stderr in ErrorRecord format
86
+ // even when the command succeeds (exit code 0). Detect this and extract the
87
+ // actual message content so it doesn't pollute output with "Command exited..." noise.
88
+ if (exitCode === 0 && stderrTrimmed && os.platform() === 'win32') {
89
+ const cleaned = extractPowerShellNativeOutput(stderrTrimmed)
90
+ if (cleaned) {
91
+ // Prepend a newline if there's already output
92
+ output += (output ? '\n' : '') + cleaned
93
+ stderrTrimmed = ''
94
+ }
95
+ }
96
+
97
+ if (stderrTrimmed) {
98
+ if (output) output += '\n'
99
+ output += `(stderr):\n${stderrTrimmed}`
100
+ }
101
+
102
+ // Apply global truncation
103
+ output = truncateOutput(output, MAX_OUTPUT_LENGTH, 'output')
104
+
105
+ if (!output || output === (description ? `[bash] ${description}\n` : '')) {
106
+ output = description ? `[bash] ${description}\n(no output)` : '(no output)'
107
+ }
108
+
109
+ if (timedOut) {
110
+ return {
111
+ success: false,
112
+ output,
113
+ error: buildErrorMsg(0, stderr, command, true, cwd),
114
+ }
115
+ }
116
+
117
+ if (exitCode !== 0) {
118
+ return {
119
+ success: false,
120
+ output,
121
+ error: buildErrorMsg(exitCode, stderr, command, false, cwd),
122
+ }
123
+ }
124
+
125
+ return { success: true, output }
126
+ } catch (err) {
127
+ const msg = fmtErr(err)
128
+ return { success: false, output: '', error: `Command failed unexpectedly: ${msg}` }
129
+ }
130
+ },
131
+ }
@@ -0,0 +1,65 @@
1
+ import { execSync } from 'node:child_process'
2
+ import * as os from 'node:os'
3
+
4
+ function detectEnv(): { osInfo: string; shell: string } {
5
+ const platform = os.platform()
6
+ const release = os.release()
7
+ const arch = os.arch()
8
+
9
+ if (platform === 'win32') {
10
+ return {
11
+ osInfo: `Windows ${release} (${arch})`,
12
+ shell: 'powershell.exe',
13
+ }
14
+ }
15
+
16
+ const osName = platform === 'darwin' ? 'macOS' : platform === 'linux' ? 'Linux' : platform
17
+ return {
18
+ osInfo: `${osName} ${release} (${arch})`,
19
+ shell: process.env.SHELL || '/bin/sh',
20
+ }
21
+ }
22
+
23
+ export const env = detectEnv()
24
+
25
+ /**
26
+ * Detect Windows console code page to handle non-UTF-8 output correctly.
27
+ * Falls back to 'utf-8' if detection fails.
28
+ * On non-Windows platforms, always returns 'utf-8'.
29
+ */
30
+ function detectEncoding(): string {
31
+ if (os.platform() !== 'win32') return 'utf-8'
32
+ try {
33
+ const result = execSync('[Console]::OutputEncoding.CodePage', {
34
+ stdio: ['ignore', 'pipe', 'pipe'],
35
+ timeout: 2000,
36
+ windowsHide: true,
37
+ encoding: 'utf-8',
38
+ shell: 'powershell.exe',
39
+ })
40
+ const cp = Number.parseInt(result.toString().trim(), 10)
41
+ // Common Windows code pages that need special handling
42
+ if (cp === 936) return 'gbk' // Chinese (Simplified)
43
+ if (cp === 950) return 'big5' // Chinese (Traditional)
44
+ if (cp === 932) return 'shift-jis' // Japanese
45
+ if (cp === 949) return 'euc-kr' // Korean
46
+ if (
47
+ cp === 1250 ||
48
+ cp === 1251 ||
49
+ cp === 1252 ||
50
+ cp === 1253 ||
51
+ cp === 1254 ||
52
+ cp === 1255 ||
53
+ cp === 1256 ||
54
+ cp === 1257
55
+ ) {
56
+ return `cp${cp}` // Windows code page
57
+ }
58
+ return 'utf-8'
59
+ } catch {
60
+ return 'utf-8'
61
+ }
62
+ }
63
+
64
+ // Cache encoding detection once at startup
65
+ export const ENCODING = detectEncoding()
@@ -0,0 +1,28 @@
1
+ import { DESTRUCTIVE_PATTERNS, SENSITIVE_PATTERNS } from './constants.js'
2
+
3
+ /**
4
+ * Check if a command matches destructive patterns.
5
+ * Returns a warning string if detected, null otherwise.
6
+ */
7
+ function checkDestructive(command: string): string | null {
8
+ for (const { regex, hint } of DESTRUCTIVE_PATTERNS) {
9
+ if (regex.test(command)) {
10
+ return hint
11
+ }
12
+ }
13
+ return null
14
+ }
15
+
16
+ /**
17
+ * Redact sensitive data from a string by replacing matches with "[REDACTED]".
18
+ * Returns the redacted string.
19
+ */
20
+ function redactSensitive(s: string): string {
21
+ let result = s
22
+ for (const pattern of SENSITIVE_PATTERNS) {
23
+ result = result.replace(pattern, '[REDACTED]')
24
+ }
25
+ return result
26
+ }
27
+
28
+ export { checkDestructive, redactSensitive }
@@ -0,0 +1,78 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+
4
+ import { DEFAULT_TIMEOUT, MAX_TIMEOUT, MIN_TIMEOUT } from './constants.js'
5
+
6
+ interface ValidatedInput {
7
+ ok: true
8
+ command: string
9
+ timeout: number
10
+ description: string
11
+ cwd: string | undefined
12
+ }
13
+ interface InputError {
14
+ ok: false
15
+ error: string
16
+ }
17
+ type ValidateResult = ValidatedInput | InputError
18
+
19
+ function validateInput(input: Record<string, unknown>): ValidateResult {
20
+ // command: must be a non-empty string
21
+ const command = input.command
22
+ if (command === undefined || command === null) {
23
+ return { ok: false, error: 'command is required' }
24
+ }
25
+ if (typeof command !== 'string') {
26
+ return { ok: false, error: `command must be a string, got ${typeof command}` }
27
+ }
28
+ if (command.trim() === '') {
29
+ return { ok: false, error: 'command must not be empty or whitespace-only' }
30
+ }
31
+
32
+ // timeout: must be a number (or default), within bounds
33
+ let timeout = DEFAULT_TIMEOUT
34
+ if (input.timeout !== undefined) {
35
+ if (
36
+ typeof input.timeout === 'number' &&
37
+ !Number.isNaN(input.timeout) &&
38
+ Number.isFinite(input.timeout)
39
+ ) {
40
+ timeout = Math.round(input.timeout)
41
+ if (timeout < MIN_TIMEOUT) {
42
+ timeout = MIN_TIMEOUT
43
+ } else if (timeout > MAX_TIMEOUT) {
44
+ timeout = MAX_TIMEOUT
45
+ }
46
+ }
47
+ // If timeout is not a valid number, silently use default
48
+ }
49
+
50
+ // description: optional string
51
+ const description = typeof input.description === 'string' ? input.description : ''
52
+
53
+ // cwd: optional string, must be a valid directory, no path traversal
54
+ let cwd: string | undefined
55
+ if (typeof input.cwd === 'string' && input.cwd.trim()) {
56
+ const rawCwd = input.cwd.trim()
57
+ // Path traversal protection
58
+ if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(rawCwd)) {
59
+ return {
60
+ ok: false,
61
+ error: `cwd contains path traversal (..) which is not allowed: "${rawCwd}"`,
62
+ }
63
+ }
64
+ const resolvedCwd = path.resolve(rawCwd)
65
+ if (!fs.existsSync(resolvedCwd)) {
66
+ return { ok: false, error: `cwd directory does not exist: "${resolvedCwd}"` }
67
+ }
68
+ if (!fs.statSync(resolvedCwd).isDirectory()) {
69
+ return { ok: false, error: `cwd is not a directory: "${resolvedCwd}"` }
70
+ }
71
+ cwd = resolvedCwd
72
+ }
73
+
74
+ return { ok: true, command: command.trim(), timeout, description, cwd }
75
+ }
76
+
77
+ export type { InputError, ValidatedInput, ValidateResult }
78
+ export { validateInput }