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,203 @@
1
+ import { loadTokenUsage } from '../config/tokens.js'
2
+ import { Box, Container, Text } from '../pi-tui/index.js'
3
+ import { fetchDeepSeekBalance, isDeepSeekOfficial } from './balance.js'
4
+ import { colors, listPlans, loadTodos, plansToItems } from './components/index.js'
5
+
6
+ export interface TuiContext {
7
+ tui: any
8
+ editor: any
9
+ loader: any
10
+ footer: any
11
+ chatMarkdown: any
12
+ plansList: any
13
+ todoPanel: any
14
+ session: any
15
+ config: any
16
+ output: any
17
+
18
+ chatContent: string
19
+ isRunning: boolean
20
+ pendingConfirmResolve: ((approved: boolean) => void) | null
21
+
22
+ plansOverlayHandle: any
23
+ helpOverlayHandle: any
24
+ todoPanelHandle: any
25
+
26
+ plansDetailMode: boolean
27
+
28
+ planCb: () => void
29
+ }
30
+
31
+ export function showTodoPanel(ctx: TuiContext): void {
32
+ ctx.todoPanel.refresh()
33
+ const box = new Box(0, 0, colors.bgDark)
34
+ box.addChild(ctx.todoPanel)
35
+ ctx.todoPanelHandle = ctx.tui.showOverlay(box, {
36
+ anchor: 'top-right',
37
+ offsetY: 2,
38
+ width: 36,
39
+ maxHeight: '70%',
40
+ offsetX: -1,
41
+ nonCapturing: true,
42
+ visible: (w: number) => w >= 110,
43
+ })
44
+ }
45
+
46
+ export function showPlansOverlay(ctx: TuiContext): void {
47
+ if (ctx.plansOverlayHandle?.isHidden() === false) {
48
+ ctx.plansOverlayHandle.hide()
49
+ ctx.plansOverlayHandle = null
50
+ ctx.plansDetailMode = false
51
+ return
52
+ }
53
+ ctx.plansDetailMode = false
54
+ const plans = listPlans(ctx.config.cwd)
55
+ ctx.plansList.refresh(plansToItems(plans))
56
+
57
+ const headerText = new Text(
58
+ ` ${colors.accent('\u25B6')} Plans (${plans.length}) ${colors.dim('Enter=view')}`,
59
+ 1,
60
+ 0,
61
+ colors.headerBg,
62
+ )
63
+ const container = new Container()
64
+ container.addChild(headerText)
65
+ if (plans.length > 0) {
66
+ container.addChild(ctx.plansList)
67
+ } else {
68
+ container.addChild(new Text(' (no plans yet)', 1, 0, colors.dim))
69
+ }
70
+
71
+ const box = new Box(1, 1, colors.bgDark)
72
+ box.addChild(container)
73
+
74
+ ctx.plansOverlayHandle = ctx.tui.showOverlay(box, {
75
+ anchor: 'right-center',
76
+ width: 45,
77
+ maxHeight: '70%',
78
+ offsetX: -1,
79
+ })
80
+ }
81
+
82
+ export function showPlanDetail(ctx: TuiContext): void {
83
+ if (!ctx.plansOverlayHandle || ctx.plansOverlayHandle.isHidden()) return
84
+ const sel = ctx.plansList.getSelectedItem()
85
+ if (!sel) return
86
+
87
+ ctx.plansDetailMode = true
88
+ const plans = listPlans(ctx.config.cwd)
89
+ const plan = plans.find(p => p.name === sel.value)
90
+ if (!plan) return
91
+
92
+ const todos = loadTodos(plan.fullPath)
93
+ const headerText = new Text(
94
+ ` ${colors.accent('\u25B6')} ${colors.warn(plan.name)} ${colors.dim('Esc=back')}`,
95
+ 1,
96
+ 0,
97
+ colors.headerBg,
98
+ )
99
+ const todosText = new Text(`\n ${todos}\n`, 1, 0)
100
+ const container = new Container()
101
+ container.addChild(headerText)
102
+ container.addChild(todosText)
103
+
104
+ const box = new Box(1, 1, colors.bgDark)
105
+ box.addChild(container)
106
+
107
+ ctx.plansOverlayHandle.hide()
108
+ ctx.plansOverlayHandle = ctx.tui.showOverlay(box, {
109
+ anchor: 'right-center',
110
+ width: 50,
111
+ maxHeight: '80%',
112
+ offsetX: -1,
113
+ })
114
+ }
115
+
116
+ export function showHelpOverlay(ctx: TuiContext): void {
117
+ if (ctx.helpOverlayHandle?.isHidden() === false) {
118
+ ctx.helpOverlayHandle.hide()
119
+ ctx.helpOverlayHandle = null
120
+ return
121
+ }
122
+ const helpContent =
123
+ colors.accent('\u2501').repeat(20) +
124
+ '\n' +
125
+ ` ${colors.accent('lonny')} ${colors.dim('TUI Help')}\n` +
126
+ colors.accent('\u2501').repeat(20) +
127
+ '\n\n' +
128
+ ` ${colors.dim('Commands:')}\n` +
129
+ ` ${colors.inputPrompt('/mode')} code|plan|ask|loop ${colors.dim('Switch mode')}\n` +
130
+ ` ${colors.inputPrompt('/model')} <name> ${colors.dim('Switch model')}\n` +
131
+ ` ${colors.inputPrompt('/plans')} ${colors.dim('Show plans overlay')}\n` +
132
+ ` ${colors.inputPrompt('/sessions')} ${colors.dim('List saved sessions')}\n` +
133
+ ` ${colors.inputPrompt('/session')} [id] ${colors.dim('Session info / delete / title')}\n` +
134
+ ` ${colors.inputPrompt('/fork')} ${colors.dim('Branch a new session')}\n` +
135
+ ` ${colors.inputPrompt('/new')} ${colors.dim('Start a new session')}\n` +
136
+ ` ${colors.inputPrompt('/prompts')} ${colors.dim('List prompt templates')}\n` +
137
+ ` ${colors.inputPrompt('/skills')} ${colors.dim('List active skills')}\n` +
138
+ ` ${colors.inputPrompt('/init')} ${colors.dim('Create .lonny/skills/ & prompts/')}\n` +
139
+ ` ${colors.inputPrompt('/stop')} ${colors.dim('Stop the running agent')}\n` +
140
+ ` ${colors.inputPrompt('/exit')} ${colors.dim('Exit')}\n` +
141
+ ` ${colors.inputPrompt('/help')} ${colors.dim('This help')}\n\n` +
142
+ ` ${colors.dim('Keyboard:')}\n` +
143
+ ` ${colors.dim('Enter')} ${colors.dim('Send message')}\n` +
144
+ ` ${colors.dim('↑/↓')} ${colors.dim('Navigate history')}\n` +
145
+ ` ${colors.dim('Tab')} ${colors.dim('Autocomplete')}\n` +
146
+ ` ${colors.dim('?')} ${colors.dim('Toggle this help')}\n\n` +
147
+ colors.accent('\u2501').repeat(20)
148
+ const helpText = new Text(helpContent, 1, 0)
149
+ const helpBox = new Box(1, 1, colors.bgDark)
150
+ helpBox.addChild(helpText)
151
+ ctx.helpOverlayHandle = ctx.tui.showOverlay(helpBox, {
152
+ anchor: 'center',
153
+ width: 46,
154
+ maxHeight: 22,
155
+ })
156
+ }
157
+
158
+ export function updateFooter(ctx: TuiContext): void {
159
+ const plans = listPlans(ctx.config.cwd)
160
+ ctx.footer.setAgentStatus(ctx.isRunning ? 'running' : 'idle')
161
+ ctx.footer.setMode(
162
+ ctx.session?.config.mode === 'plan'
163
+ ? 'plan'
164
+ : ctx.session?.config.mode === 'ask'
165
+ ? 'ask'
166
+ : ctx.session?.config.mode === 'loop'
167
+ ? 'loop'
168
+ : 'code',
169
+ )
170
+ ctx.footer.setModel(ctx.config.model, ctx.config.provider)
171
+ const tokenStats = loadTokenUsage(ctx.config.cwd)
172
+ ctx.footer.setTokenUsage(
173
+ tokenStats.totalInputTokens,
174
+ tokenStats.totalOutputTokens,
175
+ tokenStats.totalApiCalls,
176
+ )
177
+ ctx.tui.requestRender(true)
178
+ }
179
+
180
+ export function refreshPlans(ctx: TuiContext): void {
181
+ const plans = listPlans(ctx.config.cwd)
182
+ ctx.plansList.refresh(plansToItems(plans))
183
+ ctx.todoPanel.refresh()
184
+ updateFooter(ctx)
185
+ }
186
+
187
+ export async function refreshBalance(ctx: TuiContext): Promise<void> {
188
+ try {
189
+ if (isDeepSeekOfficial(ctx.config.baseUrl) && ctx.config.apiKey) {
190
+ const balance = await fetchDeepSeekBalance(ctx.config.apiKey)
191
+ if (balance.isAvailable && balance.display) {
192
+ ctx.footer.setBalance(balance.display)
193
+ ctx.footer.setWebBalance(balance.webDisplay)
194
+ } else {
195
+ ctx.footer.setBalance('')
196
+ ctx.footer.setWebBalance('')
197
+ }
198
+ ctx.tui.requestRender(true)
199
+ }
200
+ } catch {
201
+ // Silently ignore
202
+ }
203
+ }
@@ -0,0 +1,41 @@
1
+ import type { EditorTheme, MarkdownTheme, SelectListTheme } from '../pi-tui/index.js'
2
+ import { colors } from './components/index.js'
3
+ import { highlightLine } from './highlight.js'
4
+
5
+ export const markdownTheme: MarkdownTheme = {
6
+ heading: t => `\x1b[38;2;0;170;255m\x1b[1m${t}\x1b[0m`,
7
+ link: t => `\x1b[38;2;0;170;255m\x1b[4m${t}\x1b[0m`,
8
+ linkUrl: t => `\x1b[38;2;90;90;90m\x1b[4m${t}\x1b[0m`,
9
+ code: t => `\x1b[38;2;255;180;50m${t}\x1b[0m`,
10
+ codeBlock: t => `\x1b[38;2;200;200;200m${t}\x1b[0m`,
11
+ codeBlockBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
12
+ highlightCode: (code: string, lang?: string) => {
13
+ if (lang && lang.trim()) {
14
+ const lines = code.split('\n')
15
+ return lines.map(line => highlightLine(line, lang))
16
+ }
17
+ return code.split('\n')
18
+ },
19
+ codeBlockIndent: ' ',
20
+ quote: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
21
+ quoteBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
22
+ hr: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
23
+ listBullet: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
24
+ bold: t => `\x1b[1m${t}\x1b[0m`,
25
+ italic: t => `\x1b[3m${t}\x1b[0m`,
26
+ strikethrough: t => `\x1b[9m${t}\x1b[0m`,
27
+ underline: t => `\x1b[4m${t}\x1b[0m`,
28
+ }
29
+
30
+ export const selectTheme: SelectListTheme = {
31
+ selectedPrefix: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m ${t}\x1b[0m`,
32
+ selectedText: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m${t}\x1b[0m`,
33
+ description: t => `\x1b[90m${t}\x1b[0m`,
34
+ scrollInfo: t => `\x1b[90m${t}\x1b[0m`,
35
+ noMatch: t => `\x1b[38;2;255;100;100m${t}\x1b[0m`,
36
+ }
37
+
38
+ export const editorTheme: EditorTheme = {
39
+ borderColor: (str: string) => colors.accent(str),
40
+ selectList: selectTheme,
41
+ }
package/src/web/index.ts CHANGED
@@ -3,15 +3,13 @@ import * as http from 'node:http'
3
3
  import * as path from 'node:path'
4
4
  import * as url from 'node:url'
5
5
  import { type WebSocket, WebSocketServer } from 'ws'
6
+ import { type CommandUI, dispatchCommand } from '../agent/commands.js'
6
7
  import { resetGlobalEventBus } from '../agent/event-bus.js'
7
- import { ensurePromptsDir, loadPromptTemplates } from '../agent/prompt-templates.js'
8
8
  import { Session, type SessionOutput } from '../agent/session.js'
9
- import { ensureSkillsDir, loadSkills } from '../agent/skills.js'
10
9
  import type { Config } from '../config/index.js'
11
- import { resetTokenUsage } from '../config/tokens.js'
12
10
  import { fmtErr } from '../tools/errors.js'
13
11
  import { fetchDeepSeekBalance, isDeepSeekOfficial } from '../tui/balance.js'
14
- import { listPlans, type PlanEntry } from '../tui/components.js'
12
+ import { listPlans, type PlanEntry } from '../tui/components/index.js'
15
13
  import { startSessionBridge, type WsMessage } from './session-bridge.js'
16
14
 
17
15
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
@@ -95,8 +93,8 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
95
93
  // Reset event bus for new session
96
94
  resetGlobalEventBus()
97
95
 
98
- const session = (await Session.load(config)) || new Session(config)
99
96
  let bridge: ReturnType<typeof startSessionBridge> | null = null
97
+ let sessionWithOutput!: Session
100
98
 
101
99
  // Track output to forward to WebSocket
102
100
  // Strip ANSI codes since they are meaningless in the browser
@@ -149,12 +147,8 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
149
147
  },
150
148
  }
151
149
 
152
- // Re-create session with output
153
- let sessionWithOutput = (await Session.load(config, output)) || new Session(config, output)
154
- sessionWithOutput.messages = session.messages
155
- sessionWithOutput.totalInputTokens = session.totalInputTokens
156
- sessionWithOutput.totalOutputTokens = session.totalOutputTokens
157
- sessionWithOutput.totalApiCalls = session.totalApiCalls
150
+ // Load or create session with output for WebSocket forwarding
151
+ sessionWithOutput = (await Session.load(config, output)) || new Session(config, output)
158
152
 
159
153
  // Helper: read todos from a specific plan file (or first plan if no name given)
160
154
  function loadPlanTodos(
@@ -171,18 +165,11 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
171
165
  try {
172
166
  const content = fs.readFileSync(plan.fullPath, 'utf-8')
173
167
  const lines = content.split('\n')
174
- let inTodo = false
175
- for (const line of lines) {
176
- if (line.startsWith('## Todo List')) {
177
- inTodo = true
178
- continue
179
- }
180
- if (inTodo && line.startsWith('## ')) break
181
- if (inTodo) {
182
- const m = line.trim().match(/^- \[([ x])\]\s+(.+)/)
183
- if (m) {
184
- todos.push({ text: m[2], done: m[1] === 'x' })
185
- }
168
+ for (const raw of lines) {
169
+ const line = raw.trim()
170
+ const m = line.match(/^- \[([ x])\]\s+(.+)/)
171
+ if (m) {
172
+ todos.push({ text: m[2], done: m[1] === 'x' })
186
173
  }
187
174
  }
188
175
  } catch {
@@ -238,47 +225,32 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
238
225
  const cmd = parts[0]
239
226
  const arg = parts.slice(1).join(' ')
240
227
 
241
- if (cmd === 'mode' && (arg === 'code' || arg === 'plan' || arg === 'ask')) {
242
- await sessionWithOutput.setMode(arg as 'code' | 'plan' | 'ask')
243
- ws.send(JSON.stringify({ type: 'mode_changed', mode: arg }))
244
- return
245
- }
246
-
247
- if (cmd === 'model' && arg) {
248
- sessionWithOutput.config.model = arg
249
- await sessionWithOutput.setMode(sessionWithOutput.config.mode)
250
- ws.send(JSON.stringify({ type: 'model_changed', model: arg }))
228
+ // Web-specific commands
229
+ if (cmd === 'help') {
230
+ ws.send(
231
+ JSON.stringify({
232
+ type: 'help',
233
+ commands: [
234
+ '/mode <code|plan|ask|loop> - Switch mode',
235
+ '/model <name> - Switch model',
236
+ '/new - Start a new session',
237
+ '/stop - Stop the running agent',
238
+ '/skills - List active skills',
239
+ '/prompts - List prompt templates',
240
+ '/init - Create .lonny/skills/ & prompts/',
241
+ '/help - Show this help',
242
+ ],
243
+ }),
244
+ )
251
245
  return
252
246
  }
253
247
 
254
- if (cmd === 'new') {
255
- // Stop any running agent first
256
- sessionWithOutput.stop()
257
- // Close old bridge
258
- if (bridge) bridge.close()
259
- // Clear persisted state
260
- Session.clearSavedSession(config.cwd)
261
- resetTokenUsage(config.cwd)
262
- resetGlobalEventBus()
263
- // Create fresh session with empty context
264
- sessionWithOutput = new Session(config, output)
265
- sessionWithOutput.onPlanWritten = (display: string) => {
266
- ws.send(JSON.stringify({ type: 'plan_written', display }))
267
- sendPlanData()
268
- }
269
- // Create new bridge for the fresh session
270
- bridge = startSessionBridge(sessionWithOutput, config, (msg: WsMessage) => {
271
- if (ws.readyState === ws.OPEN) {
272
- ws.send(JSON.stringify(msg))
273
- }
274
- })
275
- // Reset pending confirmation state
276
- pendingConfirm = null
277
- ws.send(JSON.stringify({ type: 'session_cleared' }))
278
- ws.send(JSON.stringify({ type: 'done', reason: 'stop' }))
248
+ if (cmd === 'exit' || cmd === 'quit') {
249
+ ws.close()
279
250
  return
280
251
  }
281
252
 
253
+ // Web-specific stop (always works, no isRunning check)
282
254
  if (cmd === 'stop') {
283
255
  sessionWithOutput.stop()
284
256
  ws.send(JSON.stringify({ type: 'chunk', text: '\nStopped.\n' }))
@@ -286,82 +258,46 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
286
258
  return
287
259
  }
288
260
 
289
- if (cmd === 'skills') {
290
- const skills = loadSkills(config.cwd)
291
- if (skills.length === 0) {
292
- ws.send(
293
- JSON.stringify({
294
- type: 'chunk',
295
- text: 'No skills loaded. Create .md files in .lonny/skills/ or run install_superpowers.',
296
- }),
297
- )
298
- } else {
299
- let msg = `Active Skills (${skills.length}):\n`
300
- for (const s of skills) {
301
- msg += ` \u2022 ${s.name}`
302
- if (s.description) msg += ` - ${s.description}`
303
- msg += '\n'
261
+ // Shared commands via CommandUI
262
+ const ui: CommandUI = {
263
+ write: (text: string) => {
264
+ ws.send(JSON.stringify({ type: 'chunk', text: stripAnsi(text) }))
265
+ },
266
+ replaceContent: () => {
267
+ ws.send(JSON.stringify({ type: 'session_cleared' }))
268
+ },
269
+ onStateChange: () => {},
270
+ onNewSession: (session: Session) => {
271
+ sessionWithOutput = session
272
+ sessionWithOutput.onPlanWritten = (display: string) => {
273
+ ws.send(JSON.stringify({ type: 'plan_written', display }))
274
+ sendPlanData()
304
275
  }
305
- ws.send(JSON.stringify({ type: 'chunk', text: msg }))
306
- }
307
- ws.send(JSON.stringify({ type: 'done', reason: 'stop' }))
308
- return
276
+ if (bridge) bridge.close()
277
+ bridge = startSessionBridge(sessionWithOutput, config, (bridgeMsg: WsMessage) => {
278
+ if (ws.readyState === ws.OPEN) {
279
+ ws.send(JSON.stringify(bridgeMsg))
280
+ }
281
+ })
282
+ pendingConfirm = null
283
+ },
309
284
  }
310
-
311
- if (cmd === 'prompts') {
312
- const templates = loadPromptTemplates(config.cwd)
313
- if (templates.length === 0) {
314
- ws.send(
315
- JSON.stringify({
316
- type: 'chunk',
317
- text: 'No prompt templates found. Create .md files in .lonny/prompts/.',
318
- }),
319
- )
320
- } else {
321
- let msg = `Prompt Templates (${templates.length}):\n`
322
- for (const t of templates) {
323
- msg += ` \u2022 ${t.name}`
324
- if (t.description) msg += ` - ${t.description}`
325
- msg += '\n'
326
- }
327
- ws.send(JSON.stringify({ type: 'chunk', text: msg }))
285
+ const handled = await dispatchCommand(
286
+ { session: sessionWithOutput, config, ui, isRunning: false },
287
+ cmd,
288
+ arg,
289
+ )
290
+
291
+ if (handled) {
292
+ if (cmd === 'mode') {
293
+ ws.send(JSON.stringify({ type: 'mode_changed', mode: arg }))
294
+ } else if (cmd === 'model') {
295
+ ws.send(JSON.stringify({ type: 'model_changed', model: arg }))
328
296
  }
329
297
  ws.send(JSON.stringify({ type: 'done', reason: 'stop' }))
330
298
  return
331
299
  }
332
300
 
333
- if (cmd === 'init') {
334
- ensureSkillsDir(config.cwd)
335
- ensurePromptsDir(config.cwd)
336
- ws.send(
337
- JSON.stringify({
338
- type: 'chunk',
339
- text: 'Initialized .lonny/skills/ and .lonny/prompts/.',
340
- }),
341
- )
342
- ws.send(JSON.stringify({ type: 'done', reason: 'stop' }))
343
- return
344
- }
345
-
346
- if (cmd === 'help') {
347
- ws.send(
348
- JSON.stringify({
349
- type: 'help',
350
- commands: [
351
- '/mode code|plan|ask - Switch mode',
352
- '/model <name> - Switch model',
353
- '/new - Start a new session',
354
- '/stop - Stop the running agent',
355
- '/skills - List active skills',
356
- '/prompts - List prompt templates',
357
- '/init - Create .lonny/skills/ & prompts/',
358
- '/help - Show this help',
359
- ],
360
- }),
361
- )
362
- return
363
- }
364
-
365
301
  ws.send(JSON.stringify({ type: 'error', message: `Unknown command: ${cmd}` }))
366
302
  return
367
303
  }
@@ -459,6 +395,7 @@ export async function startWebUi(config: Config, port: number): Promise<void> {
459
395
  }
460
396
  return m
461
397
  })
398
+ console.log(`[Web UI] Sending session_history: ${historyMessages.length} messages `)
462
399
  if (historyMessages.length > 0) {
463
400
  ws.send(
464
401
  JSON.stringify({
@@ -51,7 +51,7 @@
51
51
  <div class="input-container">
52
52
  <div id="slash-hint" class="slash-hint hidden">
53
53
  <div class="slash-hint-header">Commands</div>
54
- <div class="slash-hint-item" data-cmd="mode code|plan|ask">/mode <span class="hint-arg">code|plan|ask</span> <span class="hint-desc">Switch mode</span></div>
54
+ <div class="slash-hint-item" data-cmd="mode code|plan|ask|loop">/mode <span class="hint-arg">code|plan|ask|loop</span> <span class="hint-desc">Switch mode</span></div>
55
55
  <div class="slash-hint-item" data-cmd="model ">/model <span class="hint-arg">&lt;name&gt;</span> <span class="hint-desc">Switch model</span></div>
56
56
  <div class="slash-hint-item" data-cmd="new">/new <span class="hint-desc">Start a new session</span></div>
57
57
  <div class="slash-hint-item" data-cmd="stop">/stop <span class="hint-desc">Stop the running agent</span></div>
@@ -318,36 +318,52 @@ export function hideThinking() {
318
318
 
319
319
  export function renderSessionHistory(messages) {
320
320
  if (!Array.isArray(messages)) return
321
+ let rendered = 0
322
+ let errors = 0
321
323
  for (const msg of messages) {
322
- if (msg.role === 'user') {
323
- addUserMessage(msg.content || '')
324
- } else if (msg.role === 'assistant') {
325
- startAssistantMessage()
326
- if (msg.reasoning_content) {
327
- showThinking(msg.reasoning_content)
328
- hideThinking()
329
- }
330
- if (msg.content) {
331
- state.streamingText = msg.content
332
- const body = state.streamingMsgEl.querySelector('.message-body')
333
- body.innerHTML = renderMarkdown(msg.content)
334
- }
335
- if (msg.tool_calls && msg.tool_calls.length > 0) {
336
- for (const tc of msg.tool_calls) {
337
- addToolCall(tc.name, tc.input)
324
+ try {
325
+ if (msg.role === 'user') {
326
+ addUserMessage(msg.content || '')
327
+ } else if (msg.role === 'assistant') {
328
+ startAssistantMessage()
329
+ if (msg.reasoning_content) {
330
+ showThinking(msg.reasoning_content)
331
+ hideThinking()
332
+ }
333
+ if (msg.content) {
334
+ state.streamingText = msg.content
335
+ const body = state.streamingMsgEl.querySelector('.message-body')
336
+ body.innerHTML = renderMarkdown(msg.content)
337
+ }
338
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
339
+ for (const tc of msg.tool_calls) {
340
+ addToolCall(tc.name, tc.input)
341
+ }
342
+ }
343
+ finalizeAssistantMessage()
344
+ } else if (msg.role === 'tool') {
345
+ const content = msg.content || ''
346
+ const isError = content.startsWith('ERROR: ')
347
+ if (isError) {
348
+ addToolResult(msg.name || '', false, content.replace(/^ERROR:\s*/, ''))
349
+ } else {
350
+ addToolResult(msg.name || '', true, content)
338
351
  }
339
352
  }
340
- finalizeAssistantMessage()
341
- } else if (msg.role === 'tool') {
342
- const content = msg.content || ''
343
- const isError = content.startsWith('ERROR: ')
344
- if (isError) {
345
- addToolResult(msg.name || '', false, content.replace(/^ERROR:\s*/, ''))
346
- } else {
347
- addToolResult(msg.name || '', true, content)
348
- }
353
+ rendered++
354
+ } catch (err) {
355
+ errors++
356
+ console.error(
357
+ '[renderSessionHistory] Error rendering message #' + rendered + ' role=' + msg.role + ':',
358
+ err,
359
+ )
349
360
  }
350
361
  }
362
+ if (errors > 0) {
363
+ console.warn(
364
+ '[renderSessionHistory] Rendered ' + rendered + ' messages with ' + errors + ' errors',
365
+ )
366
+ }
351
367
  }
352
368
 
353
369
  export function addTokenStats(