lonny-agent 0.2.4 → 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 (509) hide show
  1. package/.github/workflows/ci.yml +21 -0
  2. package/AGENTS.md +106 -0
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +326 -326
  5. package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
  6. package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
  7. package/dist/agent/__tests__/session-restore.test.js +260 -0
  8. package/dist/agent/__tests__/session-restore.test.js.map +1 -0
  9. package/dist/agent/commands.d.ts +26 -0
  10. package/dist/agent/commands.d.ts.map +1 -0
  11. package/dist/agent/commands.js +253 -0
  12. package/dist/agent/commands.js.map +1 -0
  13. package/dist/agent/compaction.js +8 -1
  14. package/dist/agent/compaction.js.map +1 -1
  15. package/dist/agent/index.d.ts +2 -1
  16. package/dist/agent/index.d.ts.map +1 -1
  17. package/dist/agent/index.js +2 -2
  18. package/dist/agent/index.js.map +1 -1
  19. package/dist/agent/llm.d.ts +4 -0
  20. package/dist/agent/llm.d.ts.map +1 -1
  21. package/dist/agent/memory.d.ts +12 -0
  22. package/dist/agent/memory.d.ts.map +1 -0
  23. package/dist/agent/memory.js +51 -0
  24. package/dist/agent/memory.js.map +1 -0
  25. package/dist/agent/prompt-builder.d.ts +6 -1
  26. package/dist/agent/prompt-builder.d.ts.map +1 -1
  27. package/dist/agent/prompt-builder.js +100 -33
  28. package/dist/agent/prompt-builder.js.map +1 -1
  29. package/dist/agent/providers/google.d.ts.map +1 -1
  30. package/dist/agent/providers/google.js.map +1 -1
  31. package/dist/agent/providers/openai.d.ts +1 -0
  32. package/dist/agent/providers/openai.d.ts.map +1 -1
  33. package/dist/agent/providers/openai.js +59 -6
  34. package/dist/agent/providers/openai.js.map +1 -1
  35. package/dist/agent/session-chat.d.ts +3 -0
  36. package/dist/agent/session-chat.d.ts.map +1 -0
  37. package/dist/agent/session-chat.js +384 -0
  38. package/dist/agent/session-chat.js.map +1 -0
  39. package/dist/agent/session-display.d.ts +29 -0
  40. package/dist/agent/session-display.d.ts.map +1 -0
  41. package/dist/agent/session-display.js +179 -0
  42. package/dist/agent/session-display.js.map +1 -0
  43. package/dist/agent/session-persistence.d.ts +55 -0
  44. package/dist/agent/session-persistence.d.ts.map +1 -0
  45. package/dist/agent/session-persistence.js +101 -0
  46. package/dist/agent/session-persistence.js.map +1 -0
  47. package/dist/agent/session-utils.d.ts +5 -0
  48. package/dist/agent/session-utils.d.ts.map +1 -0
  49. package/dist/agent/session-utils.js +51 -0
  50. package/dist/agent/session-utils.js.map +1 -0
  51. package/dist/agent/session.d.ts +19 -17
  52. package/dist/agent/session.d.ts.map +1 -1
  53. package/dist/agent/session.js +234 -578
  54. package/dist/agent/session.js.map +1 -1
  55. package/dist/cli/index.d.ts +4 -0
  56. package/dist/cli/index.d.ts.map +1 -1
  57. package/dist/cli/index.js +67 -2
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/config/index.d.ts +2 -2
  60. package/dist/config/index.d.ts.map +1 -1
  61. package/dist/config/index.js +7 -1
  62. package/dist/config/index.js.map +1 -1
  63. package/dist/index.js +24 -3
  64. package/dist/index.js.map +1 -1
  65. package/dist/tools/__tests__/bash.test.js +169 -2
  66. package/dist/tools/__tests__/bash.test.js.map +1 -1
  67. package/dist/tools/__tests__/edit.test.js +1091 -340
  68. package/dist/tools/__tests__/edit.test.js.map +1 -1
  69. package/dist/tools/__tests__/fetch.test.js +1 -1
  70. package/dist/tools/__tests__/fetch.test.js.map +1 -1
  71. package/dist/tools/__tests__/glob.test.js +1 -1
  72. package/dist/tools/__tests__/glob.test.js.map +1 -1
  73. package/dist/tools/__tests__/grep.test.js +33 -1
  74. package/dist/tools/__tests__/grep.test.js.map +1 -1
  75. package/dist/tools/__tests__/ls.test.js +1 -1
  76. package/dist/tools/__tests__/ls.test.js.map +1 -1
  77. package/dist/tools/__tests__/read.test.js +1 -1
  78. package/dist/tools/__tests__/read.test.js.map +1 -1
  79. package/dist/tools/__tests__/registry.test.js +313 -0
  80. package/dist/tools/__tests__/registry.test.js.map +1 -1
  81. package/dist/tools/__tests__/sed.test.d.ts +2 -0
  82. package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
  83. package/dist/tools/__tests__/sed.test.js +228 -0
  84. package/dist/tools/__tests__/sed.test.js.map +1 -0
  85. package/dist/tools/__tests__/write_plan.test.js +1 -1
  86. package/dist/tools/__tests__/write_plan.test.js.map +1 -1
  87. package/dist/tools/bash.d.ts.map +1 -1
  88. package/dist/tools/bash.js +711 -18
  89. package/dist/tools/bash.js.map +1 -1
  90. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
  91. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
  92. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
  93. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
  94. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
  95. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
  96. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
  97. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
  98. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
  99. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
  100. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
  101. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
  102. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
  103. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
  104. package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
  105. package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
  106. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
  107. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
  108. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
  109. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
  110. package/dist/tools/codebase/ast/adapter.d.ts +23 -0
  111. package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
  112. package/dist/tools/codebase/ast/adapter.js +11 -0
  113. package/dist/tools/codebase/ast/adapter.js.map +1 -0
  114. package/dist/tools/codebase/ast/tools.d.ts +3 -0
  115. package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
  116. package/dist/tools/codebase/ast/tools.js +286 -0
  117. package/dist/tools/codebase/ast/tools.js.map +1 -0
  118. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
  119. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
  120. package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
  121. package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
  122. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
  123. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
  124. package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
  125. package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
  126. package/dist/tools/codebase/ast/types.d.ts +103 -0
  127. package/dist/tools/codebase/ast/types.d.ts.map +1 -0
  128. package/dist/tools/codebase/ast/types.js +2 -0
  129. package/dist/tools/codebase/ast/types.js.map +1 -0
  130. package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  131. package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  132. package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  133. package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  134. package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  135. package/dist/tools/codebase/find.d.ts +3 -0
  136. package/dist/tools/codebase/find.d.ts.map +1 -0
  137. package/dist/tools/codebase/find.js +61 -0
  138. package/dist/tools/codebase/find.js.map +1 -0
  139. package/dist/tools/codebase/glob.d.ts +3 -0
  140. package/dist/tools/codebase/glob.d.ts.map +1 -0
  141. package/dist/tools/codebase/glob.js +37 -0
  142. package/dist/tools/codebase/glob.js.map +1 -0
  143. package/dist/tools/codebase/grep.d.ts +3 -0
  144. package/dist/tools/codebase/grep.d.ts.map +1 -0
  145. package/dist/tools/codebase/grep.js +169 -0
  146. package/dist/tools/codebase/grep.js.map +1 -0
  147. package/dist/tools/codebase/ls.d.ts +3 -0
  148. package/dist/tools/codebase/ls.d.ts.map +1 -0
  149. package/dist/tools/codebase/ls.js +39 -0
  150. package/dist/tools/codebase/ls.js.map +1 -0
  151. package/dist/tools/codebase/read.d.ts +10 -0
  152. package/dist/tools/codebase/read.d.ts.map +1 -0
  153. package/dist/tools/codebase/read.js +81 -0
  154. package/dist/tools/codebase/read.js.map +1 -0
  155. package/dist/tools/delete_memory.d.ts +3 -0
  156. package/dist/tools/delete_memory.d.ts.map +1 -0
  157. package/dist/tools/delete_memory.js +46 -0
  158. package/dist/tools/delete_memory.js.map +1 -0
  159. package/dist/tools/edit/diff-compute.d.ts +4 -0
  160. package/dist/tools/edit/diff-compute.d.ts.map +1 -0
  161. package/dist/tools/edit/diff-compute.js +18 -0
  162. package/dist/tools/edit/diff-compute.js.map +1 -0
  163. package/dist/tools/edit/diff-render.d.ts +18 -0
  164. package/dist/tools/edit/diff-render.d.ts.map +1 -0
  165. package/dist/tools/edit/diff-render.js +128 -0
  166. package/dist/tools/edit/diff-render.js.map +1 -0
  167. package/dist/tools/edit/edit.d.ts +4 -0
  168. package/dist/tools/edit/edit.d.ts.map +1 -0
  169. package/dist/tools/edit/edit.js +440 -0
  170. package/dist/tools/edit/edit.js.map +1 -0
  171. package/dist/tools/edit/matcher.d.ts +24 -0
  172. package/dist/tools/edit/matcher.d.ts.map +1 -0
  173. package/dist/tools/edit/matcher.js +66 -0
  174. package/dist/tools/edit/matcher.js.map +1 -0
  175. package/dist/tools/edit/parser.d.ts +10 -0
  176. package/dist/tools/edit/parser.d.ts.map +1 -0
  177. package/dist/tools/edit/parser.js +120 -0
  178. package/dist/tools/edit/parser.js.map +1 -0
  179. package/dist/tools/edit/types.d.ts +16 -0
  180. package/dist/tools/edit/types.d.ts.map +1 -0
  181. package/dist/tools/edit/types.js +2 -0
  182. package/dist/tools/edit/types.js.map +1 -0
  183. package/dist/tools/edit/write_plan.d.ts +5 -0
  184. package/dist/tools/edit/write_plan.d.ts.map +1 -0
  185. package/dist/tools/edit/write_plan.js +98 -0
  186. package/dist/tools/edit/write_plan.js.map +1 -0
  187. package/dist/tools/edit.d.ts +50 -9
  188. package/dist/tools/edit.d.ts.map +1 -1
  189. package/dist/tools/edit.js +394 -159
  190. package/dist/tools/edit.js.map +1 -1
  191. package/dist/tools/execute/bash/constants.d.ts +24 -0
  192. package/dist/tools/execute/bash/constants.d.ts.map +1 -0
  193. package/dist/tools/execute/bash/constants.js +88 -0
  194. package/dist/tools/execute/bash/constants.js.map +1 -0
  195. package/dist/tools/execute/bash/errors.d.ts +39 -0
  196. package/dist/tools/execute/bash/errors.d.ts.map +1 -0
  197. package/dist/tools/execute/bash/errors.js +249 -0
  198. package/dist/tools/execute/bash/errors.js.map +1 -0
  199. package/dist/tools/execute/bash/execution.d.ts +18 -0
  200. package/dist/tools/execute/bash/execution.d.ts.map +1 -0
  201. package/dist/tools/execute/bash/execution.js +207 -0
  202. package/dist/tools/execute/bash/execution.js.map +1 -0
  203. package/dist/tools/execute/bash/index.d.ts +3 -0
  204. package/dist/tools/execute/bash/index.d.ts.map +1 -0
  205. package/dist/tools/execute/bash/index.js +117 -0
  206. package/dist/tools/execute/bash/index.js.map +1 -0
  207. package/dist/tools/execute/bash/platform.d.ts +6 -0
  208. package/dist/tools/execute/bash/platform.d.ts.map +1 -0
  209. package/dist/tools/execute/bash/platform.js +64 -0
  210. package/dist/tools/execute/bash/platform.js.map +1 -0
  211. package/dist/tools/execute/bash/security.d.ts +12 -0
  212. package/dist/tools/execute/bash/security.d.ts.map +1 -0
  213. package/dist/tools/execute/bash/security.js +26 -0
  214. package/dist/tools/execute/bash/security.js.map +1 -0
  215. package/dist/tools/execute/bash/validation.d.ts +16 -0
  216. package/dist/tools/execute/bash/validation.d.ts.map +1 -0
  217. package/dist/tools/execute/bash/validation.js +57 -0
  218. package/dist/tools/execute/bash/validation.js.map +1 -0
  219. package/dist/tools/execute/bash.d.ts +3 -0
  220. package/dist/tools/execute/bash.d.ts.map +1 -0
  221. package/dist/tools/execute/bash.js +728 -0
  222. package/dist/tools/execute/bash.js.map +1 -0
  223. package/dist/tools/execute/git.d.ts +3 -0
  224. package/dist/tools/execute/git.d.ts.map +1 -0
  225. package/dist/tools/execute/git.js +183 -0
  226. package/dist/tools/execute/git.js.map +1 -0
  227. package/dist/tools/execute/task_complete.d.ts +7 -0
  228. package/dist/tools/execute/task_complete.d.ts.map +1 -0
  229. package/dist/tools/execute/task_complete.js +38 -0
  230. package/dist/tools/execute/task_complete.js.map +1 -0
  231. package/dist/tools/fetch.d.ts.map +1 -1
  232. package/dist/tools/fetch.js +2 -0
  233. package/dist/tools/fetch.js.map +1 -1
  234. package/dist/tools/find.d.ts.map +1 -1
  235. package/dist/tools/find.js +2 -0
  236. package/dist/tools/find.js.map +1 -1
  237. package/dist/tools/git.d.ts.map +1 -1
  238. package/dist/tools/git.js +2 -0
  239. package/dist/tools/git.js.map +1 -1
  240. package/dist/tools/glob.d.ts.map +1 -1
  241. package/dist/tools/glob.js +2 -0
  242. package/dist/tools/glob.js.map +1 -1
  243. package/dist/tools/grep.d.ts.map +1 -1
  244. package/dist/tools/grep.js +56 -30
  245. package/dist/tools/grep.js.map +1 -1
  246. package/dist/tools/install/clawhub.d.ts +26 -0
  247. package/dist/tools/install/clawhub.d.ts.map +1 -0
  248. package/dist/tools/install/clawhub.js +182 -0
  249. package/dist/tools/install/clawhub.js.map +1 -0
  250. package/dist/tools/install/constants.d.ts +5 -0
  251. package/dist/tools/install/constants.d.ts.map +1 -0
  252. package/dist/tools/install/constants.js +5 -0
  253. package/dist/tools/install/constants.js.map +1 -0
  254. package/dist/tools/install/install_skill.d.ts +3 -0
  255. package/dist/tools/install/install_skill.d.ts.map +1 -0
  256. package/dist/tools/install/install_skill.js +132 -0
  257. package/dist/tools/install/install_skill.js.map +1 -0
  258. package/dist/tools/install/npm.d.ts +8 -0
  259. package/dist/tools/install/npm.d.ts.map +1 -0
  260. package/dist/tools/install/npm.js +52 -0
  261. package/dist/tools/install/npm.js.map +1 -0
  262. package/dist/tools/install/skill-content.d.ts +9 -0
  263. package/dist/tools/install/skill-content.d.ts.map +1 -0
  264. package/dist/tools/install/skill-content.js +89 -0
  265. package/dist/tools/install/skill-content.js.map +1 -0
  266. package/dist/tools/install_skill.d.ts.map +1 -1
  267. package/dist/tools/install_skill.js +2 -0
  268. package/dist/tools/install_skill.js.map +1 -1
  269. package/dist/tools/list_memory.d.ts +3 -0
  270. package/dist/tools/list_memory.d.ts.map +1 -0
  271. package/dist/tools/list_memory.js +55 -0
  272. package/dist/tools/list_memory.js.map +1 -0
  273. package/dist/tools/ls.d.ts.map +1 -1
  274. package/dist/tools/ls.js +2 -0
  275. package/dist/tools/ls.js.map +1 -1
  276. package/dist/tools/memory/delete_memory.d.ts +3 -0
  277. package/dist/tools/memory/delete_memory.d.ts.map +1 -0
  278. package/dist/tools/memory/delete_memory.js +46 -0
  279. package/dist/tools/memory/delete_memory.js.map +1 -0
  280. package/dist/tools/memory/list_memory.d.ts +3 -0
  281. package/dist/tools/memory/list_memory.d.ts.map +1 -0
  282. package/dist/tools/memory/list_memory.js +55 -0
  283. package/dist/tools/memory/list_memory.js.map +1 -0
  284. package/dist/tools/memory/save_memory.d.ts +3 -0
  285. package/dist/tools/memory/save_memory.d.ts.map +1 -0
  286. package/dist/tools/memory/save_memory.js +45 -0
  287. package/dist/tools/memory/save_memory.js.map +1 -0
  288. package/dist/tools/read.d.ts.map +1 -1
  289. package/dist/tools/read.js +2 -0
  290. package/dist/tools/read.js.map +1 -1
  291. package/dist/tools/registry.d.ts +7 -8
  292. package/dist/tools/registry.d.ts.map +1 -1
  293. package/dist/tools/registry.js +114 -46
  294. package/dist/tools/registry.js.map +1 -1
  295. package/dist/tools/save_memory.d.ts +3 -0
  296. package/dist/tools/save_memory.d.ts.map +1 -0
  297. package/dist/tools/save_memory.js +45 -0
  298. package/dist/tools/save_memory.js.map +1 -0
  299. package/dist/tools/search.d.ts.map +1 -1
  300. package/dist/tools/search.js +2 -0
  301. package/dist/tools/search.js.map +1 -1
  302. package/dist/tools/sed.d.ts +4 -0
  303. package/dist/tools/sed.d.ts.map +1 -0
  304. package/dist/tools/sed.js +121 -0
  305. package/dist/tools/sed.js.map +1 -0
  306. package/dist/tools/tree.d.ts +12 -0
  307. package/dist/tools/tree.d.ts.map +1 -0
  308. package/dist/tools/tree.js +87 -0
  309. package/dist/tools/tree.js.map +1 -0
  310. package/dist/tools/types.d.ts +12 -0
  311. package/dist/tools/types.d.ts.map +1 -1
  312. package/dist/tools/web/fetch.d.ts +3 -0
  313. package/dist/tools/web/fetch.d.ts.map +1 -0
  314. package/dist/tools/web/fetch.js +49 -0
  315. package/dist/tools/web/fetch.js.map +1 -0
  316. package/dist/tools/web/search.d.ts +3 -0
  317. package/dist/tools/web/search.d.ts.map +1 -0
  318. package/dist/tools/web/search.js +134 -0
  319. package/dist/tools/web/search.js.map +1 -0
  320. package/dist/tools/write_plan.d.ts.map +1 -1
  321. package/dist/tools/write_plan.js +2 -0
  322. package/dist/tools/write_plan.js.map +1 -1
  323. package/dist/tui/commands.d.ts +4 -0
  324. package/dist/tui/commands.d.ts.map +1 -0
  325. package/dist/tui/commands.js +118 -0
  326. package/dist/tui/commands.js.map +1 -0
  327. package/dist/tui/components/colors.d.ts +36 -0
  328. package/dist/tui/components/colors.d.ts.map +1 -0
  329. package/dist/tui/components/colors.js +48 -0
  330. package/dist/tui/components/colors.js.map +1 -0
  331. package/dist/tui/components/header-bar.d.ts +26 -0
  332. package/dist/tui/components/header-bar.d.ts.map +1 -0
  333. package/dist/tui/components/header-bar.js +78 -0
  334. package/dist/tui/components/header-bar.js.map +1 -0
  335. package/dist/tui/components/index.d.ts +9 -0
  336. package/dist/tui/components/index.d.ts.map +1 -0
  337. package/dist/tui/components/index.js +9 -0
  338. package/dist/tui/components/index.js.map +1 -0
  339. package/dist/tui/components/landing-screen.d.ts +12 -0
  340. package/dist/tui/components/landing-screen.d.ts.map +1 -0
  341. package/dist/tui/components/landing-screen.js +53 -0
  342. package/dist/tui/components/landing-screen.js.map +1 -0
  343. package/dist/tui/components/pixel-logo.d.ts +5 -0
  344. package/dist/tui/components/pixel-logo.d.ts.map +1 -0
  345. package/dist/tui/components/pixel-logo.js +27 -0
  346. package/dist/tui/components/pixel-logo.js.map +1 -0
  347. package/dist/tui/components/plan-utils.d.ts +11 -0
  348. package/dist/tui/components/plan-utils.d.ts.map +1 -0
  349. package/dist/tui/components/plan-utils.js +62 -0
  350. package/dist/tui/components/plan-utils.js.map +1 -0
  351. package/dist/tui/components/plans-list.d.ts +18 -0
  352. package/dist/tui/components/plans-list.d.ts.map +1 -0
  353. package/dist/tui/components/plans-list.js +46 -0
  354. package/dist/tui/components/plans-list.js.map +1 -0
  355. package/dist/tui/components/rich-footer.d.ts +29 -0
  356. package/dist/tui/components/rich-footer.d.ts.map +1 -0
  357. package/dist/tui/components/rich-footer.js +124 -0
  358. package/dist/tui/components/rich-footer.js.map +1 -0
  359. package/dist/tui/components/todo-panel.d.ts +14 -0
  360. package/dist/tui/components/todo-panel.d.ts.map +1 -0
  361. package/dist/tui/components/todo-panel.js +83 -0
  362. package/dist/tui/components/todo-panel.js.map +1 -0
  363. package/dist/tui/components.d.ts.map +1 -1
  364. package/dist/tui/components.js +14 -7
  365. package/dist/tui/components.js.map +1 -1
  366. package/dist/tui/index.d.ts +2 -1
  367. package/dist/tui/index.d.ts.map +1 -1
  368. package/dist/tui/index.js +99 -505
  369. package/dist/tui/index.js.map +1 -1
  370. package/dist/tui/overlays.d.ts +28 -0
  371. package/dist/tui/overlays.d.ts.map +1 -0
  372. package/dist/tui/overlays.js +154 -0
  373. package/dist/tui/overlays.js.map +1 -0
  374. package/dist/tui/themes.d.ts +5 -0
  375. package/dist/tui/themes.d.ts.map +1 -0
  376. package/dist/tui/themes.js +38 -0
  377. package/dist/tui/themes.js.map +1 -0
  378. package/dist/web/index.d.ts.map +1 -1
  379. package/dist/web/index.js +62 -101
  380. package/dist/web/index.js.map +1 -1
  381. package/dist/web/public/app.js +5 -855
  382. package/dist/web/public/confirm.js +70 -0
  383. package/dist/web/public/index.html +121 -120
  384. package/dist/web/public/input.js +107 -0
  385. package/dist/web/public/messages.js +411 -0
  386. package/dist/web/public/sidebar.js +82 -0
  387. package/dist/web/public/state.js +72 -0
  388. package/dist/web/public/style.css +247 -70
  389. package/dist/web/public/utils.js +37 -0
  390. package/dist/web/public/websocket.js +267 -0
  391. package/dist/web/public/ws.js +19 -0
  392. package/dist/web/session-bridge.d.ts.map +1 -1
  393. package/dist/web/session-bridge.js +85 -10
  394. package/dist/web/session-bridge.js.map +1 -1
  395. package/docs/ast-tools.md +117 -0
  396. package/package.json +11 -5
  397. package/scripts/check-line-count.mjs +53 -0
  398. package/scripts/copy-ast-wasm.mjs +32 -0
  399. package/scripts/copy-native.mjs +24 -0
  400. package/scripts/copy-web.mjs +15 -0
  401. package/src/agent/__tests__/session-restore.test.ts +284 -0
  402. package/src/agent/commands.ts +274 -0
  403. package/src/agent/compaction.ts +9 -2
  404. package/src/agent/index.ts +7 -2
  405. package/src/agent/llm.ts +4 -0
  406. package/src/agent/memory.ts +60 -0
  407. package/src/agent/prompt-builder.ts +111 -33
  408. package/src/agent/providers/google.ts +0 -1
  409. package/src/agent/providers/openai.ts +79 -8
  410. package/src/agent/session-chat.ts +480 -0
  411. package/src/agent/session-display.ts +195 -0
  412. package/src/agent/session-persistence.ts +146 -0
  413. package/src/agent/session-utils.ts +54 -0
  414. package/src/agent/session.ts +269 -670
  415. package/src/cli/index.ts +73 -4
  416. package/src/config/index.ts +14 -7
  417. package/src/index.ts +30 -3
  418. package/src/tools/__tests__/bash.test.ts +188 -2
  419. package/src/tools/__tests__/edit.test.ts +1216 -356
  420. package/src/tools/__tests__/fetch.test.ts +1 -1
  421. package/src/tools/__tests__/glob.test.ts +1 -1
  422. package/src/tools/__tests__/grep.test.ts +36 -1
  423. package/src/tools/__tests__/ls.test.ts +1 -1
  424. package/src/tools/__tests__/read.test.ts +1 -1
  425. package/src/tools/__tests__/registry.test.ts +332 -0
  426. package/src/tools/__tests__/write_plan.test.ts +1 -1
  427. package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
  428. package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
  429. package/src/tools/codebase/ast/adapter.ts +44 -0
  430. package/src/tools/codebase/ast/tools.ts +322 -0
  431. package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
  432. package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
  433. package/src/tools/codebase/ast/types.ts +102 -0
  434. package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  435. package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  436. package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  437. package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  438. package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  439. package/src/tools/{find.ts → codebase/find.ts} +4 -2
  440. package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
  441. package/src/tools/{grep.ts → codebase/grep.ts} +61 -32
  442. package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
  443. package/src/tools/{read.ts → codebase/read.ts} +5 -3
  444. package/src/tools/edit/diff-compute.ts +21 -0
  445. package/src/tools/edit/diff-render.ts +145 -0
  446. package/src/tools/edit/edit.ts +468 -0
  447. package/src/tools/edit/matcher.ts +70 -0
  448. package/src/tools/edit/parser.ts +131 -0
  449. package/src/tools/edit/types.ts +19 -0
  450. package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
  451. package/src/tools/execute/bash/constants.ts +93 -0
  452. package/src/tools/execute/bash/errors.ts +304 -0
  453. package/src/tools/execute/bash/execution.ts +211 -0
  454. package/src/tools/execute/bash/index.ts +131 -0
  455. package/src/tools/execute/bash/platform.ts +65 -0
  456. package/src/tools/execute/bash/security.ts +28 -0
  457. package/src/tools/execute/bash/validation.ts +78 -0
  458. package/src/tools/execute/git.ts +209 -0
  459. package/src/tools/execute/task_complete.ts +40 -0
  460. package/src/tools/install/clawhub.ts +222 -0
  461. package/src/tools/install/constants.ts +4 -0
  462. package/src/tools/install/install_skill.ts +150 -0
  463. package/src/tools/install/npm.ts +60 -0
  464. package/src/tools/install/skill-content.ts +108 -0
  465. package/src/tools/memory/delete_memory.ts +46 -0
  466. package/src/tools/memory/list_memory.ts +53 -0
  467. package/src/tools/memory/save_memory.ts +45 -0
  468. package/src/tools/registry.ts +121 -50
  469. package/src/tools/tree.ts +103 -0
  470. package/src/tools/types.ts +13 -0
  471. package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
  472. package/src/tools/{search.ts → web/search.ts} +4 -2
  473. package/src/tui/commands.ts +135 -0
  474. package/src/tui/components/colors.ts +52 -0
  475. package/src/tui/components/header-bar.ts +88 -0
  476. package/src/tui/components/index.ts +8 -0
  477. package/src/tui/components/landing-screen.ts +67 -0
  478. package/src/tui/components/pixel-logo.ts +33 -0
  479. package/src/tui/components/plan-utils.ts +69 -0
  480. package/src/tui/components/plans-list.ts +55 -0
  481. package/src/tui/components/rich-footer.ts +146 -0
  482. package/src/tui/components/todo-panel.ts +95 -0
  483. package/src/tui/index.ts +110 -561
  484. package/src/tui/overlays.ts +203 -0
  485. package/src/tui/themes.ts +41 -0
  486. package/src/web/index.ts +66 -107
  487. package/src/web/public/app.js +5 -855
  488. package/src/web/public/confirm.js +70 -0
  489. package/src/web/public/index.html +121 -120
  490. package/src/web/public/input.js +107 -0
  491. package/src/web/public/messages.js +411 -0
  492. package/src/web/public/sidebar.js +82 -0
  493. package/src/web/public/state.js +72 -0
  494. package/src/web/public/style.css +247 -70
  495. package/src/web/public/utils.js +37 -0
  496. package/src/web/public/websocket.js +267 -0
  497. package/src/web/public/ws.js +19 -0
  498. package/src/web/session-bridge.ts +90 -12
  499. package/.claude/settings.local.json +0 -14
  500. package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
  501. package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
  502. package/.lonny/plan-web-cwd-status.md +0 -38
  503. package/AGENT.md +0 -99
  504. package/src/tools/bash.ts +0 -39
  505. package/src/tools/edit.ts +0 -554
  506. package/src/tools/exec.ts +0 -348
  507. package/src/tools/git.ts +0 -76
  508. package/src/tools/install_skill.ts +0 -539
  509. package/src/tui/components.ts +0 -635
package/dist/tui/index.js CHANGED
@@ -1,18 +1,10 @@
1
- import { resetGlobalEventBus } from '../agent/event-bus.js';
2
- import { ensurePromptsDir, loadPromptTemplates } from '../agent/prompt-templates.js';
3
1
  import { formatToolInput, Session } from '../agent/session.js';
4
- import { ensureSkillsDir, loadSkills } from '../agent/skills.js';
5
- import { loadTokenUsage, resetTokenUsage } from '../config/tokens.js';
6
- import { Box, CombinedAutocompleteProvider, Container, Editor, Loader, Markdown, ProcessTerminal, Text, TUI, } from '../pi-tui/index.js';
7
- import { fmtErr } from '../tools/errors.js';
2
+ import { Box, CombinedAutocompleteProvider, Editor, Loader, Markdown, ProcessTerminal, TUI, } from '../pi-tui/index.js';
8
3
  import { fetchDeepSeekBalance, isDeepSeekOfficial } from './balance.js';
9
- import { colors, LandingScreen, listPlans, loadTodos, PlansList, plansToItems, RichFooter, TodoPanel, } from './components.js';
10
- import { highlightLine } from './highlight.js';
11
- /**
12
- * Invisible spacer that renders N empty lines.
13
- * Used to reserve space at the bottom of the chat area so the editor
14
- * overlay doesn't cover the last lines of command output.
15
- */
4
+ import { handleConfirmInput, sendMessage } from './commands.js';
5
+ import { colors, LandingScreen, listPlans, PlansList, RichFooter, TodoPanel, } from './components/index.js';
6
+ import { refreshPlans, showHelpOverlay, showPlanDetail, showPlansOverlay, showTodoPanel, updateFooter, } from './overlays.js';
7
+ import { editorTheme, markdownTheme, selectTheme } from './themes.js';
16
8
  class Spacer {
17
9
  height;
18
10
  constructor(height) {
@@ -23,84 +15,43 @@ class Spacer {
23
15
  }
24
16
  invalidate() { }
25
17
  }
26
- export async function startTui(config) {
27
- let chatContent = '';
28
- let isRunning = false;
29
- let session;
30
- // ── Create markdown theme (OpenCode-style, clean colors, with syntax highlighting) ──
31
- const markdownTheme = {
32
- heading: t => `\x1b[38;2;0;170;255m\x1b[1m${t}\x1b[0m`,
33
- link: t => `\x1b[38;2;0;170;255m\x1b[4m${t}\x1b[0m`,
34
- linkUrl: t => `\x1b[38;2;90;90;90m\x1b[4m${t}\x1b[0m`,
35
- code: t => `\x1b[38;2;255;180;50m${t}\x1b[0m`,
36
- codeBlock: t => `\x1b[38;2;200;200;200m${t}\x1b[0m`,
37
- codeBlockBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
38
- highlightCode: (code, lang) => {
39
- if (lang && lang.trim()) {
40
- const lines = code.split('\n');
41
- return lines.map(line => highlightLine(line, lang));
42
- }
43
- return code.split('\n');
44
- },
45
- codeBlockIndent: ' ',
46
- quote: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
47
- quoteBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
48
- hr: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
49
- listBullet: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
50
- bold: t => `\x1b[1m${t}\x1b[0m`,
51
- italic: t => `\x1b[3m${t}\x1b[0m`,
52
- strikethrough: t => `\x1b[9m${t}\x1b[0m`,
53
- underline: t => `\x1b[4m${t}\x1b[0m`,
54
- };
55
- // ── Create select list theme ──
56
- const selectTheme = {
57
- selectedPrefix: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m ${t}\x1b[0m`,
58
- selectedText: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m${t}\x1b[0m`,
59
- description: t => `\x1b[90m${t}\x1b[0m`,
60
- scrollInfo: t => `\x1b[90m${t}\x1b[0m`,
61
- noMatch: t => `\x1b[38;2;255;100;100m${t}\x1b[0m`,
62
- };
63
- // ── Create editor theme (used by Editor component) ────────────────────
64
- const editorTheme = {
65
- borderColor: (str) => colors.accent(str),
66
- selectList: selectTheme,
67
- };
18
+ export async function startTui(config, preloadedSession) {
19
+ const ctx = {};
68
20
  // ── Create terminal and TUI ────────────────────────────────────────────
69
21
  const terminal = new ProcessTerminal();
70
- // Patch terminal.write to suppress alternate screen buffer sequences.
71
- // Without this, pi-tui disables the terminal's native scrollback/scrollbar,
72
- // making it impossible to scroll through past conversation history.
73
22
  const origWrite = terminal.write.bind(terminal);
74
23
  terminal.write = (data) => {
75
24
  const filtered = data.replace(/\x1b\[\?1049[hl]/g, '');
76
25
  if (filtered)
77
26
  origWrite(filtered);
78
27
  };
79
- // Show hardware cursor by default so IME (Chinese input method) can
80
- // position its candidate window at the correct cursor location.
81
- // The cursor is hidden during agent execution via setShowHardwareCursor(false).
82
- // Note: on some terminals (Windows Terminal), showing the hardware cursor
83
- // can interfere with editor rendering layout. If you see editor border gap,
84
- // set this to false and use PI_HARDWARE_CURSOR=1 env var to enable.
85
28
  const tui = new TUI(terminal, true);
86
29
  tui.setClearOnShrink(true);
87
30
  terminal.setTitle(`lonny ${config.model} ${config.provider}`);
31
+ ctx.tui = tui;
88
32
  // ── Create components ──────────────────────────────────────────────────
89
- // Chat area (full width, no side panel) — created upfront but only added
90
- // to the TUI after the landing screen transitions to chat mode.
91
33
  const chatMarkdown = new Markdown('', 1, 0, markdownTheme);
34
+ ctx.chatMarkdown = chatMarkdown;
92
35
  const chatBox = new Box(1, 0);
93
36
  chatBox.addChild(chatMarkdown);
94
- // Reserve space at the bottom so the editor overlay doesn't cover
95
- // the last lines of command output. 13 = maxHeight(12) + offsetY(1).
96
37
  chatBox.addChild(new Spacer(13));
97
- // Chat input — Editor with multi-line support, history, and autocomplete
98
38
  const slashCommands = [
99
- { name: 'mode', description: 'Switch mode (code|plan|ask)', argumentHint: 'code|plan|ask' },
39
+ {
40
+ name: 'mode',
41
+ description: 'Switch mode (code|plan|ask|loop)',
42
+ argumentHint: 'code|plan|ask|loop',
43
+ },
100
44
  { name: 'model', description: 'Switch model', argumentHint: '<name>' },
101
45
  { name: 'plans', description: 'Show plans overlay' },
102
46
  { name: 'prompts', description: 'List prompt templates' },
103
47
  { name: 'skills', description: 'List active skills' },
48
+ { name: 'sessions', description: 'List all saved sessions' },
49
+ {
50
+ name: 'session',
51
+ description: 'Show current session info',
52
+ argumentHint: '[title <name>|delete]',
53
+ },
54
+ { name: 'fork', description: 'Fork a new session from current context' },
104
55
  { name: 'new', description: 'Start a new session' },
105
56
  { name: 'init', description: 'Create .lonny/skills/ & prompts/' },
106
57
  { name: 'help', description: 'Show help' },
@@ -109,98 +60,76 @@ export async function startTui(config) {
109
60
  { name: 'filter', description: 'Filter plans', argumentHint: '<query>' },
110
61
  ];
111
62
  const editor = new Editor(tui, editorTheme);
63
+ ctx.editor = editor;
112
64
  editor.setAutocompleteProvider(new CombinedAutocompleteProvider(slashCommands, config.cwd));
113
- // Loader (thinking indicator)
114
65
  const loader = new Loader(tui, colors.running, colors.idle, 'thinking...', { intervalMs: 80 });
115
- // Input area — the editor is shown as a bottom-anchored overlay so the
116
- // input always stays at the bottom of the terminal, regardless of how
117
- // much chat content has accumulated.
66
+ ctx.loader = loader;
118
67
  let inputOverlayHandle = null;
119
- // Rich footer (cwd | mode | tokens | model | version + command hints)
120
68
  const footer = new RichFooter(config.cwd, config.model, config.provider);
121
- // ── Build layout (landing phase) ──
122
- // In the landing phase, only the footer is shown. The chatBox, editor,
123
- // and loader are added after the first message (see landingScreen.onSubmit).
124
- //
125
- // The editor and loader are shown as a bottom-anchored overlay so the
126
- // input area always stays at the bottom of the terminal, regardless of
127
- // how much chat content has accumulated.
128
- // ── Plan written callback (defined early since it's used by session restore) ──
69
+ ctx.footer = footer;
70
+ // ── Init mutable state ─────────────────────────────────────────────────
71
+ ctx.chatContent = '';
72
+ ctx.isRunning = false;
73
+ ctx.pendingConfirmResolve = null;
74
+ ctx.plansOverlayHandle = null;
75
+ ctx.helpOverlayHandle = null;
76
+ ctx.todoPanelHandle = null;
77
+ ctx.plansDetailMode = false;
78
+ ctx.config = config;
79
+ // ── Plan written callback ──────────────────────────────────────────────
129
80
  const planCb = () => {
130
- refreshPlans();
131
- todoPanel.refresh();
132
- if (plansOverlayHandle?.isHidden() === false) {
133
- showPlansOverlay();
81
+ refreshPlans(ctx);
82
+ ctx.todoPanel.refresh();
83
+ if (ctx.plansOverlayHandle?.isHidden() === false) {
84
+ showPlansOverlay(ctx);
134
85
  }
135
86
  };
136
- // ── Session output ────────────────────────────────────────────────────
137
- // Tool call/result text flows through output.write naturally, interspersed
138
- // with assistant text in the correct order (just like non-TUI mode).
139
- // ── Tool confirmation state ──
140
- let pendingConfirmResolve = null;
141
- function handleConfirmInput(data) {
142
- if (!pendingConfirmResolve)
143
- return false;
144
- const key = data.trim().toLowerCase();
145
- if (key === 'y' || key === 'yes') {
146
- pendingConfirmResolve(true);
147
- pendingConfirmResolve = null;
148
- chatContent += 'y\n';
149
- chatMarkdown.setText(chatContent);
150
- tui.requestRender(true);
151
- return true;
152
- }
153
- else if (key === 'n' || key === 'no' || key === '\r' || key === '') {
154
- pendingConfirmResolve(false);
155
- pendingConfirmResolve = null;
156
- chatContent += 'N\n';
157
- chatMarkdown.setText(chatContent);
158
- tui.requestRender(true);
159
- return true;
160
- }
161
- return false;
162
- }
87
+ ctx.planCb = planCb;
88
+ // ── Session output ─────────────────────────────────────────────────────
163
89
  const output = {
164
90
  write: (text) => {
165
- chatContent += text;
166
- chatMarkdown.setText(chatContent);
91
+ ctx.chatContent += text;
92
+ ctx.chatMarkdown.setText(ctx.chatContent);
167
93
  },
168
94
  suppressToolOutput: false,
169
95
  confirmTool: async (toolCalls) => {
170
- chatContent += `\n ${colors.warn('Allow these tool calls?')}\n`;
96
+ ctx.chatContent += `\n ${colors.warn('Allow these tool calls?')}\n`;
171
97
  for (const tc of toolCalls) {
172
98
  const detail = formatToolInput(tc);
173
- chatContent += ` ${colors.dim('\u2022')} ${colors.accent(tc.name)}${detail ? ` ${colors.dim(detail)}` : ''}\n`;
99
+ ctx.chatContent += ` ${colors.dim('\u2022')} ${colors.accent(tc.name)}${detail ? ` ${colors.dim(detail)}` : ''}\n`;
174
100
  }
175
- chatContent += ` ${colors.inputPrompt('(y/N)')} `;
176
- chatMarkdown.setText(chatContent);
177
- tui.requestRender(true);
101
+ ctx.chatContent += ` ${colors.inputPrompt('(y/N)')} `;
102
+ ctx.chatMarkdown.setText(ctx.chatContent);
103
+ ctx.tui.requestRender(true);
178
104
  return new Promise(resolve => {
179
- pendingConfirmResolve = resolve;
105
+ ctx.pendingConfirmResolve = resolve;
180
106
  });
181
107
  },
182
108
  };
183
- // Try to restore a saved session for this directory (MUST be before landing screen setup)
109
+ ctx.output = output;
110
+ // ── Session restore ────────────────────────────────────────────────────
184
111
  let restored = false;
185
- const restoredSession = await Session.load(config, output);
112
+ let restoredSession = preloadedSession ?? null;
113
+ if (!restoredSession) {
114
+ restoredSession = await Session.load(config, output);
115
+ }
186
116
  if (restoredSession) {
187
117
  restored = true;
188
- session = restoredSession;
189
- session.onPlanWritten = planCb;
190
- // Find the last user message from the previous session
191
- const lastUserMsg = [...session.messages].reverse().find(m => m.role === 'user');
118
+ ctx.session = restoredSession;
119
+ ctx.session.onPlanWritten = planCb;
120
+ const lastUserMsg = [...ctx.session.messages].reverse().find(m => m.role === 'user');
192
121
  const lastQuestion = lastUserMsg && typeof lastUserMsg.content === 'string' ? lastUserMsg.content : null;
193
- chatContent = `\n${colors.dim('\u21BA Resumed previous session')}`;
122
+ ctx.chatContent = `\n${colors.dim('\u21BA Resumed previous session')}`;
194
123
  if (lastQuestion) {
195
124
  const preview = lastQuestion.length > 80 ? `${lastQuestion.slice(0, 80)}\u2026` : lastQuestion;
196
- chatContent += ` \u2014 ${colors.userLabel(preview)}`;
125
+ ctx.chatContent += ` \u2014 ${colors.userLabel(preview)}`;
197
126
  }
198
- chatContent += '\n\n';
199
- chatMarkdown.setText(chatContent);
127
+ ctx.chatContent += '\n\n';
128
+ ctx.chatMarkdown.setText(ctx.chatContent);
200
129
  }
201
130
  else {
202
- session = new Session(config, output);
203
- session.onPlanWritten = planCb;
131
+ ctx.session = new Session(config, output);
132
+ ctx.session.onPlanWritten = planCb;
204
133
  }
205
134
  // ── Fetch DeepSeek balance at startup (non-blocking)
206
135
  ;
@@ -214,8 +143,8 @@ export async function startTui(config) {
214
143
  tui.requestRender(true);
215
144
  }
216
145
  else if (balance.error) {
217
- chatContent += `\n${colors.warn('\u26A0')} Balance fetch failed: ${balance.error}\n`;
218
- chatMarkdown.setText(chatContent);
146
+ ctx.chatContent += `\n${colors.warn('\u26A0')} Balance fetch failed: ${balance.error}\n`;
147
+ ctx.chatMarkdown.setText(ctx.chatContent);
219
148
  }
220
149
  }
221
150
  }
@@ -223,10 +152,9 @@ export async function startTui(config) {
223
152
  // Silently ignore balance fetch errors
224
153
  }
225
154
  })();
226
- // ── Landing screen (centered overlay with pixel logo) ────────────────
155
+ // ── Landing screen ────────────────────────────────────────────────────
227
156
  const landingScreen = new LandingScreen(config.model, config.provider);
228
157
  let landingOverlayHandle = null;
229
- // Only show the landing screen if no session was restored
230
158
  if (!restored) {
231
159
  landingOverlayHandle = tui.showOverlay(landingScreen, {
232
160
  anchor: 'center',
@@ -235,9 +163,7 @@ export async function startTui(config) {
235
163
  });
236
164
  tui.setFocus(landingScreen);
237
165
  }
238
- // ── Rich footer bar ──────────────────────────────────
239
- // NOTE: must be added AFTER the landing screen overlay so it renders on
240
- // top and is not covered by the centered overlay.
166
+ // ── Rich footer bar ────────────────────────────────────────────────────
241
167
  const footerWidth = terminal.columns ?? process.stdout.columns ?? 120;
242
168
  const footerHandle = tui.showOverlay(footer, {
243
169
  anchor: 'bottom-left',
@@ -246,21 +172,7 @@ export async function startTui(config) {
246
172
  });
247
173
  // ── Persistent Todo Side Panel ────────────────────────────────────────
248
174
  const todoPanel = new TodoPanel(config.cwd);
249
- let todoPanelHandle = null;
250
- function showTodoPanel() {
251
- todoPanel.refresh();
252
- const box = new Box(0, 0, colors.bgDark);
253
- box.addChild(todoPanel);
254
- todoPanelHandle = tui.showOverlay(box, {
255
- anchor: 'top-right',
256
- offsetY: 2,
257
- width: 36,
258
- maxHeight: '70%',
259
- offsetX: -1,
260
- nonCapturing: true,
261
- visible: (w) => w >= 110,
262
- });
263
- }
175
+ ctx.todoPanel = todoPanel;
264
176
  // If a session was restored, immediately transition to chat layout
265
177
  // (skip the landing screen)
266
178
  if (restored) {
@@ -274,332 +186,23 @@ export async function startTui(config) {
274
186
  maxHeight: 12,
275
187
  nonCapturing: false,
276
188
  });
277
- showTodoPanel();
189
+ showTodoPanel(ctx);
278
190
  tui.setFocus(editor);
279
191
  }
280
192
  // ── Plans overlay components ──────────────────────────────────────────
281
193
  const plansList = new PlansList([], 15, selectTheme);
282
- let plansOverlayHandle = null;
283
- let plansDetailMode = false;
284
- function showPlansOverlay() {
285
- if (plansOverlayHandle?.isHidden() === false) {
286
- plansOverlayHandle.hide();
287
- plansOverlayHandle = null;
288
- plansDetailMode = false;
289
- return;
290
- }
291
- plansDetailMode = false;
292
- const plans = listPlans(config.cwd);
293
- plansList.refresh(plansToItems(plans));
294
- const headerText = new Text(` ${colors.accent('\u25B6')} Plans (${plans.length}) ${colors.dim('Enter=view')}`, 1, 0, colors.headerBg);
295
- const container = new Container();
296
- container.addChild(headerText);
297
- if (plans.length > 0) {
298
- container.addChild(plansList);
299
- }
300
- else {
301
- container.addChild(new Text(' (no plans yet)', 1, 0, colors.dim));
302
- }
303
- const box = new Box(1, 1, colors.bgDark);
304
- box.addChild(container);
305
- plansOverlayHandle = tui.showOverlay(box, {
306
- anchor: 'right-center',
307
- width: 45,
308
- maxHeight: '70%',
309
- offsetX: -1,
310
- });
311
- }
312
- function showPlanDetail() {
313
- if (!plansOverlayHandle || plansOverlayHandle.isHidden())
314
- return;
315
- const sel = plansList.getSelectedItem();
316
- if (!sel)
317
- return;
318
- plansDetailMode = true;
319
- const plans = listPlans(config.cwd);
320
- const plan = plans.find(p => p.name === sel.value);
321
- if (!plan)
322
- return;
323
- const todos = loadTodos(plan.fullPath);
324
- const headerText = new Text(` ${colors.accent('\u25B6')} ${colors.warn(plan.name)} ${colors.dim('Esc=back')}`, 1, 0, colors.headerBg);
325
- const todosText = new Text(`\n ${todos}\n`, 1, 0);
326
- const container = new Container();
327
- container.addChild(headerText);
328
- container.addChild(todosText);
329
- const box = new Box(1, 1, colors.bgDark);
330
- box.addChild(container);
331
- // Hide current and show detail
332
- plansOverlayHandle.hide();
333
- plansOverlayHandle = tui.showOverlay(box, {
334
- anchor: 'right-center',
335
- width: 50,
336
- maxHeight: '80%',
337
- offsetX: -1,
338
- });
339
- }
340
- // ── Help overlay ──────────────────────────────────────────────────────────
341
- let helpOverlayHandle = null;
342
- function showHelpOverlay() {
343
- if (helpOverlayHandle?.isHidden() === false) {
344
- helpOverlayHandle.hide();
345
- helpOverlayHandle = null;
346
- return;
347
- }
348
- const helpContent = colors.accent('\u2501').repeat(20) +
349
- '\n' +
350
- ` ${colors.accent('lonny')} ${colors.dim('TUI Help')}\n` +
351
- colors.accent('\u2501').repeat(20) +
352
- '\n\n' +
353
- ` ${colors.dim('Commands:')}\n` +
354
- ` ${colors.inputPrompt('/mode')} code|plan|ask ${colors.dim('Switch mode')}\n` +
355
- ` ${colors.inputPrompt('/model')} <name> ${colors.dim('Switch model')}\n` +
356
- ` ${colors.inputPrompt('/plans')} ${colors.dim('Show plans overlay')}\n` +
357
- ` ${colors.inputPrompt('/new')} ${colors.dim('Start a new session')}\n` +
358
- ` ${colors.inputPrompt('/prompts')} ${colors.dim('List prompt templates')}\n` +
359
- ` ${colors.inputPrompt('/skills')} ${colors.dim('List active skills')}\n` +
360
- ` ${colors.inputPrompt('/init')} ${colors.dim('Create .lonny/skills/ & prompts/')}\n` +
361
- ` ${colors.inputPrompt('/stop')} ${colors.dim('Stop the running agent')}\n` +
362
- ` ${colors.inputPrompt('/exit')} ${colors.dim('Exit')}\n` +
363
- ` ${colors.inputPrompt('/help')} ${colors.dim('This help')}\n\n` +
364
- ` ${colors.dim('Keyboard:')}\n` +
365
- ` ${colors.dim('Enter')} ${colors.dim('Send message')}\n` +
366
- ` ${colors.dim('↑/↓')} ${colors.dim('Navigate history')}\n` +
367
- ` ${colors.dim('Tab')} ${colors.dim('Autocomplete')}\n` +
368
- ` ${colors.dim('?')} ${colors.dim('Toggle this help')}\n\n` +
369
- colors.accent('\u2501').repeat(20);
370
- const helpText = new Text(helpContent, 1, 0);
371
- const helpBox = new Box(1, 1, colors.bgDark);
372
- helpBox.addChild(helpText);
373
- helpOverlayHandle = tui.showOverlay(helpBox, {
374
- anchor: 'center',
375
- width: 46,
376
- maxHeight: 22,
377
- });
378
- }
379
- // ── Update helpers ──────────────────────────────────────────────────────
380
- function updateFooter() {
381
- const plans = listPlans(config.cwd);
382
- footer.setAgentStatus(isRunning ? 'running' : 'idle');
383
- footer.setMode(session?.config.mode === 'plan' ? 'plan' : session?.config.mode === 'ask' ? 'ask' : 'code');
384
- footer.setModel(config.model, config.provider);
385
- const tokenStats = loadTokenUsage(config.cwd);
386
- footer.setTokenUsage(tokenStats.totalInputTokens, tokenStats.totalOutputTokens, tokenStats.totalApiCalls);
387
- tui.requestRender(true);
388
- }
389
- function refreshPlans() {
390
- const plans = listPlans(config.cwd);
391
- plansList.refresh(plansToItems(plans));
392
- todoPanel.refresh();
393
- updateFooter();
394
- }
395
- async function refreshBalance() {
396
- try {
397
- if (isDeepSeekOfficial(config.baseUrl) && config.apiKey) {
398
- const balance = await fetchDeepSeekBalance(config.apiKey);
399
- if (balance.isAvailable && balance.display) {
400
- footer.setBalance(balance.display);
401
- footer.setWebBalance(balance.webDisplay);
402
- }
403
- else {
404
- footer.setBalance('');
405
- footer.setWebBalance('');
406
- }
407
- tui.requestRender(true);
408
- }
409
- }
410
- catch {
411
- // Silently ignore
412
- }
413
- }
414
- // ── Input handling ──────────────────────────────────────────────────────
415
- async function sendMessage(text) {
416
- const trimmed = text.trim();
417
- if (!trimmed)
418
- return;
419
- editor.setText('');
420
- editor.addToHistory(trimmed);
421
- // Allow slash commands even when agent is running (critical for /stop)
422
- if (trimmed.startsWith('/')) {
423
- const parts = trimmed.slice(1).split(/\s+/);
424
- const cmd = parts[0];
425
- const arg = parts.slice(1).join(' ');
426
- if (cmd === 'exit' || cmd === 'quit') {
427
- chatContent += `\n${colors.dim('Goodbye!')}\n`;
428
- chatMarkdown.setText(chatContent);
429
- tui.stop();
430
- process.exit(0);
431
- return;
432
- }
433
- if (cmd === 'new') {
434
- // If the agent is running, stop the old session gracefully first.
435
- // Without this, the pending chat() promise would continue consuming
436
- // tokens and write stale output into the freshly cleared chat display.
437
- if (isRunning) {
438
- session.stop();
439
- isRunning = false;
440
- loader.setMessage('');
441
- tui.setShowHardwareCursor(true);
442
- }
443
- Session.clearSavedSession(config.cwd);
444
- resetTokenUsage(config.cwd);
445
- resetGlobalEventBus();
446
- session = new Session(config, output);
447
- session.onPlanWritten = planCb;
448
- chatContent = '';
449
- chatMarkdown.setText('');
450
- plansList.clearFilter();
451
- editor.undoStack.clear();
452
- editor.history = [];
453
- editor.killRing.ring = [];
454
- updateFooter();
455
- return;
456
- }
457
- if (cmd === 'mode') {
458
- if (arg === 'code' || arg === 'plan' || arg === 'ask') {
459
- await session.setMode(arg);
460
- chatContent += `\n${colors.warn('\u21E8')} Switched to ${arg === 'ask' ? colors.success(arg) : colors.warn(arg)} mode\n`;
461
- chatMarkdown.setText(chatContent);
462
- updateFooter();
463
- }
464
- else {
465
- chatContent += `\n${colors.error('\u2716')} Usage: ${colors.inputPrompt('/mode code|plan|ask')} (current: ${session.config.mode})\n`;
466
- chatMarkdown.setText(chatContent);
467
- }
468
- return;
469
- }
470
- if (cmd === 'model') {
471
- if (arg) {
472
- session.config.model = arg;
473
- // Rebuild system prompt with new model context
474
- await session.setMode(session.config.mode); // triggers rebuild
475
- chatContent += `\n${colors.warn('\u21E8')} Model switched to ${colors.warn(arg)}\n`;
476
- chatMarkdown.setText(chatContent);
477
- updateFooter();
478
- }
479
- else {
480
- chatContent += `\n${colors.inputPrompt('Current model:')} ${colors.dim(session.config.model)}\n`;
481
- chatMarkdown.setText(chatContent);
482
- }
483
- return;
484
- }
485
- if (cmd === 'prompts') {
486
- const templates = loadPromptTemplates(config.cwd);
487
- if (templates.length === 0) {
488
- chatContent += `\n${colors.warn('No prompt templates found.')} ${colors.dim('Create .md files in .lonny/prompts/')}\n`;
489
- }
490
- else {
491
- chatContent += `\n${colors.accent('\u25B6')} ${colors.warn(`Prompt Templates (${templates.length})`)}\n`;
492
- for (const t of templates) {
493
- chatContent += ` ${colors.dim('\u2022')} ${colors.inputPrompt(t.name)}`;
494
- if (t.description)
495
- chatContent += ` ${colors.dim(`\u2014 ${t.description}`)}`;
496
- chatContent += '\n';
497
- }
498
- }
499
- chatMarkdown.setText(chatContent);
500
- return;
501
- }
502
- if (cmd === 'skills') {
503
- const skills = loadSkills(config.cwd);
504
- if (skills.length === 0) {
505
- chatContent += `\n${colors.warn('No skills loaded.')} ${colors.dim('Create .md files in .lonny/skills/')}\n`;
506
- }
507
- else {
508
- chatContent += `\n${colors.accent('\u25B6')} ${colors.warn(`Active Skills (${skills.length})`)}\n`;
509
- for (const s of skills) {
510
- chatContent += ` ${colors.dim('\u2022')} ${colors.inputPrompt(s.name)}`;
511
- if (s.description)
512
- chatContent += ` ${colors.dim(`\u2014 ${s.description}`)}`;
513
- chatContent += '\n';
514
- }
515
- }
516
- chatMarkdown.setText(chatContent);
517
- return;
518
- }
519
- if (cmd === 'plans') {
520
- showPlansOverlay();
521
- return;
522
- }
523
- if (cmd === 'filter') {
524
- plansList.setFilter(arg);
525
- tui.requestRender(true);
526
- return;
527
- }
528
- if (cmd === 'help' || cmd === '?') {
529
- showHelpOverlay();
530
- return;
531
- }
532
- if (cmd === 'init') {
533
- ensureSkillsDir(config.cwd);
534
- ensurePromptsDir(config.cwd);
535
- chatContent += `\n${colors.success('\u2714')} Initialized .lonny/skills/ and .lonny/prompts/\n`;
536
- chatMarkdown.setText(chatContent);
537
- return;
538
- }
539
- if (cmd === 'stop') {
540
- if (!isRunning) {
541
- chatContent += `\n${colors.dim('Agent is not running.')}\n`;
542
- chatMarkdown.setText(chatContent);
543
- return;
544
- }
545
- // Tell the session to stop gracefully
546
- session.stop();
547
- chatContent += `\n${colors.warn('\u23F9')} Stopping agent...\n`;
548
- chatMarkdown.setText(chatContent);
549
- isRunning = false;
550
- loader.stop();
551
- loader.setMessage('');
552
- tui.setShowHardwareCursor(true);
553
- updateFooter();
554
- return;
555
- }
556
- chatContent += `\n${colors.error('\u2716')} Unknown command: /${cmd}. ${colors.dim('Type /help for available commands.')}\n`;
557
- chatMarkdown.setText(chatContent);
558
- return;
559
- }
560
- // Block regular messages when agent is already running
561
- if (isRunning)
562
- return;
563
- isRunning = true;
564
- loader.setMessage('thinking...');
565
- tui.setShowHardwareCursor(false);
566
- updateFooter();
567
- session
568
- .chat(trimmed)
569
- .then(() => {
570
- isRunning = false;
571
- loader.setMessage('');
572
- refreshPlans();
573
- tui.setShowHardwareCursor(true);
574
- updateFooter();
575
- refreshBalance();
576
- })
577
- .catch((err) => {
578
- isRunning = false;
579
- loader.setMessage('');
580
- const errMsg = fmtErr(err);
581
- chatContent += `\n${colors.error('\u2716 Error:')} ${errMsg}\n`;
582
- chatMarkdown.setText(chatContent);
583
- tui.setShowHardwareCursor(true);
584
- updateFooter();
585
- });
586
- }
587
- // Wire up submit on editor (after landing transition)
194
+ ctx.plansList = plansList;
195
+ // ── Wire up submit on editor ─────────────────────────────────────────-
588
196
  editor.onSubmit = (value) => {
589
- sendMessage(value);
197
+ sendMessage(ctx, value);
590
198
  };
591
- // ── Landing screen transition ────────────────────────────────────────────
592
- // When the user presses any key on the landing screen, transition to the
593
- // full chat layout (editor + chat area).
199
+ // ── Landing screen transition ──────────────────────────────────────────
594
200
  landingScreen.onSubmit = () => {
595
- if (isRunning)
201
+ if (ctx.isRunning)
596
202
  return;
597
- // Hide the landing overlay
598
203
  if (landingOverlayHandle)
599
204
  landingOverlayHandle.hide();
600
205
  footer.setPhase('chat');
601
- // Add chat components to the main TUI
602
- // (footer is already an overlay anchored to bottom-left, no need to addChild)
603
206
  tui.addChild(chatBox);
604
207
  tui.addChild(loader);
605
208
  inputOverlayHandle = tui.showOverlay(editor, {
@@ -609,86 +212,77 @@ export async function startTui(config) {
609
212
  maxHeight: 12,
610
213
  nonCapturing: false,
611
214
  });
612
- showTodoPanel();
613
- // Focus the chat editor
215
+ showTodoPanel(ctx);
614
216
  tui.setFocus(editor);
615
217
  tui.requestRender(true);
616
218
  };
617
219
  // ── Input listener ──────────────────────────────────────────────────────
618
220
  tui.addInputListener(data => {
619
- // Check if tool confirmation is pending (consume all input until resolved)
620
- if (pendingConfirmResolve) {
621
- return { consume: handleConfirmInput(data) };
221
+ if (ctx.pendingConfirmResolve) {
222
+ return { consume: handleConfirmInput(ctx, data) };
622
223
  }
623
- // Check if help overlay is active
624
- if (helpOverlayHandle?.isHidden() === false) {
224
+ if (ctx.helpOverlayHandle?.isHidden() === false) {
625
225
  if (data === '\x1b' || data === '\x1b[' || data === '?') {
626
- helpOverlayHandle.hide();
627
- helpOverlayHandle = null;
226
+ ctx.helpOverlayHandle.hide();
227
+ ctx.helpOverlayHandle = null;
628
228
  }
629
229
  return { consume: true };
630
230
  }
631
- // Check if plans overlay is active
632
- if (plansOverlayHandle?.isHidden() === false) {
231
+ if (ctx.plansOverlayHandle?.isHidden() === false) {
633
232
  if (data === '\x1b' || data === '\x1b[') {
634
- if (plansDetailMode) {
635
- // Go back to plan list
636
- plansOverlayHandle.hide();
637
- plansOverlayHandle = null;
638
- plansDetailMode = false;
639
- showPlansOverlay();
233
+ if (ctx.plansDetailMode) {
234
+ ctx.plansOverlayHandle.hide();
235
+ ctx.plansOverlayHandle = null;
236
+ ctx.plansDetailMode = false;
237
+ showPlansOverlay(ctx);
640
238
  }
641
239
  else {
642
- plansOverlayHandle.hide();
643
- plansOverlayHandle = null;
240
+ ctx.plansOverlayHandle.hide();
241
+ ctx.plansOverlayHandle = null;
644
242
  }
645
243
  return { consume: true };
646
244
  }
647
- if (data === '\r' && !plansDetailMode) {
648
- // Enter: view plan detail
649
- showPlanDetail();
245
+ if (data === '\r' && !ctx.plansDetailMode) {
246
+ showPlanDetail(ctx);
650
247
  return { consume: true };
651
248
  }
652
249
  if (data === '\x1b[A') {
653
250
  const plans = listPlans(config.cwd);
654
251
  if (plans.length > 0) {
655
- const sel = plansList.getSelectedItem();
252
+ const sel = ctx.plansList.getSelectedItem();
656
253
  const idx = sel ? plans.findIndex(p => p.name === sel.value) : -1;
657
254
  const nextIdx = idx <= 0 ? plans.length - 1 : idx - 1;
658
- plansList.setSelectedIndex(nextIdx);
659
- updateFooter();
255
+ ctx.plansList.setSelectedIndex(nextIdx);
256
+ updateFooter(ctx);
660
257
  }
661
258
  return { consume: true };
662
259
  }
663
260
  if (data === '\x1b[B') {
664
261
  const plans = listPlans(config.cwd);
665
262
  if (plans.length > 0) {
666
- const sel = plansList.getSelectedItem();
263
+ const sel = ctx.plansList.getSelectedItem();
667
264
  const idx = sel ? plans.findIndex(p => p.name === sel.value) : -1;
668
265
  const nextIdx = idx === -1 ? 0 : (idx + 1) % plans.length;
669
- plansList.setSelectedIndex(nextIdx);
670
- updateFooter();
266
+ ctx.plansList.setSelectedIndex(nextIdx);
267
+ updateFooter(ctx);
671
268
  }
672
269
  return { consume: true };
673
270
  }
674
271
  return { consume: true };
675
272
  }
676
273
  if (data === '?') {
677
- showHelpOverlay();
274
+ showHelpOverlay(ctx);
678
275
  return { consume: true };
679
276
  }
680
277
  return undefined;
681
278
  });
682
279
  // ── Initial render ────────────────────────────────────────────────────
683
280
  loader.setMessage('');
684
- refreshPlans();
685
- // If no session was restored, keep the landing screen and clear chat.
686
- // If a session was restored, chatContent already has the resume message.
281
+ refreshPlans(ctx);
687
282
  if (!restored) {
688
- chatMarkdown.setText('');
283
+ ctx.chatMarkdown.setText('');
689
284
  }
690
285
  tui.start();
691
- // Keep alive
692
286
  await new Promise(() => { });
693
287
  }
694
288
  //# sourceMappingURL=index.js.map