lonny-agent 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/.github/workflows/ci.yml +21 -0
  2. package/AGENTS.md +106 -0
  3. package/CHANGELOG.md +24 -0
  4. package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
  5. package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
  6. package/dist/agent/__tests__/session-restore.test.js +260 -0
  7. package/dist/agent/__tests__/session-restore.test.js.map +1 -0
  8. package/dist/agent/commands.d.ts +26 -0
  9. package/dist/agent/commands.d.ts.map +1 -0
  10. package/dist/agent/commands.js +253 -0
  11. package/dist/agent/commands.js.map +1 -0
  12. package/dist/agent/compaction.js +8 -1
  13. package/dist/agent/compaction.js.map +1 -1
  14. package/dist/agent/index.d.ts +2 -1
  15. package/dist/agent/index.d.ts.map +1 -1
  16. package/dist/agent/index.js +2 -2
  17. package/dist/agent/index.js.map +1 -1
  18. package/dist/agent/memory.d.ts +12 -0
  19. package/dist/agent/memory.d.ts.map +1 -0
  20. package/dist/agent/memory.js +51 -0
  21. package/dist/agent/memory.js.map +1 -0
  22. package/dist/agent/prompt-builder.d.ts +6 -1
  23. package/dist/agent/prompt-builder.d.ts.map +1 -1
  24. package/dist/agent/prompt-builder.js +89 -26
  25. package/dist/agent/prompt-builder.js.map +1 -1
  26. package/dist/agent/providers/openai.d.ts +1 -0
  27. package/dist/agent/providers/openai.d.ts.map +1 -1
  28. package/dist/agent/providers/openai.js +16 -3
  29. package/dist/agent/providers/openai.js.map +1 -1
  30. package/dist/agent/session-chat.d.ts +3 -0
  31. package/dist/agent/session-chat.d.ts.map +1 -0
  32. package/dist/agent/session-chat.js +384 -0
  33. package/dist/agent/session-chat.js.map +1 -0
  34. package/dist/agent/session-display.d.ts +29 -0
  35. package/dist/agent/session-display.d.ts.map +1 -0
  36. package/dist/agent/session-display.js +179 -0
  37. package/dist/agent/session-display.js.map +1 -0
  38. package/dist/agent/session-persistence.d.ts +55 -0
  39. package/dist/agent/session-persistence.d.ts.map +1 -0
  40. package/dist/agent/session-persistence.js +101 -0
  41. package/dist/agent/session-persistence.js.map +1 -0
  42. package/dist/agent/session-utils.d.ts +5 -0
  43. package/dist/agent/session-utils.d.ts.map +1 -0
  44. package/dist/agent/session-utils.js +51 -0
  45. package/dist/agent/session-utils.js.map +1 -0
  46. package/dist/agent/session.d.ts +15 -17
  47. package/dist/agent/session.d.ts.map +1 -1
  48. package/dist/agent/session.js +226 -608
  49. package/dist/agent/session.js.map +1 -1
  50. package/dist/cli/index.d.ts +4 -0
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +67 -2
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/config/index.d.ts +2 -2
  55. package/dist/config/index.d.ts.map +1 -1
  56. package/dist/index.js +24 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/tools/__tests__/bash.test.js +168 -1
  59. package/dist/tools/__tests__/bash.test.js.map +1 -1
  60. package/dist/tools/__tests__/edit.test.js +253 -6
  61. package/dist/tools/__tests__/edit.test.js.map +1 -1
  62. package/dist/tools/__tests__/fetch.test.js +1 -1
  63. package/dist/tools/__tests__/fetch.test.js.map +1 -1
  64. package/dist/tools/__tests__/glob.test.js +1 -1
  65. package/dist/tools/__tests__/glob.test.js.map +1 -1
  66. package/dist/tools/__tests__/grep.test.js +1 -1
  67. package/dist/tools/__tests__/grep.test.js.map +1 -1
  68. package/dist/tools/__tests__/ls.test.js +1 -1
  69. package/dist/tools/__tests__/ls.test.js.map +1 -1
  70. package/dist/tools/__tests__/read.test.js +1 -1
  71. package/dist/tools/__tests__/read.test.js.map +1 -1
  72. package/dist/tools/__tests__/registry.test.js +313 -0
  73. package/dist/tools/__tests__/registry.test.js.map +1 -1
  74. package/dist/tools/__tests__/write_plan.test.js +1 -1
  75. package/dist/tools/__tests__/write_plan.test.js.map +1 -1
  76. package/dist/tools/bash.d.ts.map +1 -1
  77. package/dist/tools/bash.js +659 -39
  78. package/dist/tools/bash.js.map +1 -1
  79. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
  80. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
  81. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
  82. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
  83. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
  84. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
  85. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
  86. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
  87. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
  88. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
  89. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
  90. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
  91. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
  92. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
  93. package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
  94. package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
  95. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
  96. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
  97. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
  98. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
  99. package/dist/tools/codebase/ast/adapter.d.ts +23 -0
  100. package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
  101. package/dist/tools/codebase/ast/adapter.js +11 -0
  102. package/dist/tools/codebase/ast/adapter.js.map +1 -0
  103. package/dist/tools/codebase/ast/tools.d.ts +3 -0
  104. package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
  105. package/dist/tools/codebase/ast/tools.js +286 -0
  106. package/dist/tools/codebase/ast/tools.js.map +1 -0
  107. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
  108. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
  109. package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
  110. package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
  111. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
  112. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
  113. package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
  114. package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
  115. package/dist/tools/codebase/ast/types.d.ts +103 -0
  116. package/dist/tools/codebase/ast/types.d.ts.map +1 -0
  117. package/dist/tools/codebase/ast/types.js +2 -0
  118. package/dist/tools/codebase/ast/types.js.map +1 -0
  119. package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  120. package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  121. package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  122. package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  123. package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  124. package/dist/tools/codebase/find.d.ts +3 -0
  125. package/dist/tools/codebase/find.d.ts.map +1 -0
  126. package/dist/tools/codebase/find.js +61 -0
  127. package/dist/tools/codebase/find.js.map +1 -0
  128. package/dist/tools/codebase/glob.d.ts +3 -0
  129. package/dist/tools/codebase/glob.d.ts.map +1 -0
  130. package/dist/tools/codebase/glob.js +37 -0
  131. package/dist/tools/codebase/glob.js.map +1 -0
  132. package/dist/tools/codebase/grep.d.ts +3 -0
  133. package/dist/tools/codebase/grep.d.ts.map +1 -0
  134. package/dist/tools/codebase/grep.js +169 -0
  135. package/dist/tools/codebase/grep.js.map +1 -0
  136. package/dist/tools/codebase/ls.d.ts +3 -0
  137. package/dist/tools/codebase/ls.d.ts.map +1 -0
  138. package/dist/tools/codebase/ls.js +39 -0
  139. package/dist/tools/codebase/ls.js.map +1 -0
  140. package/dist/tools/codebase/read.d.ts +10 -0
  141. package/dist/tools/codebase/read.d.ts.map +1 -0
  142. package/dist/tools/codebase/read.js +81 -0
  143. package/dist/tools/codebase/read.js.map +1 -0
  144. package/dist/tools/delete_memory.d.ts +3 -0
  145. package/dist/tools/delete_memory.d.ts.map +1 -0
  146. package/dist/tools/delete_memory.js +46 -0
  147. package/dist/tools/delete_memory.js.map +1 -0
  148. package/dist/tools/edit/diff-compute.d.ts +4 -0
  149. package/dist/tools/edit/diff-compute.d.ts.map +1 -0
  150. package/dist/tools/edit/diff-compute.js +18 -0
  151. package/dist/tools/edit/diff-compute.js.map +1 -0
  152. package/dist/tools/edit/diff-render.d.ts +18 -0
  153. package/dist/tools/edit/diff-render.d.ts.map +1 -0
  154. package/dist/tools/edit/diff-render.js +128 -0
  155. package/dist/tools/edit/diff-render.js.map +1 -0
  156. package/dist/tools/edit/edit.d.ts +4 -0
  157. package/dist/tools/edit/edit.d.ts.map +1 -0
  158. package/dist/tools/edit/edit.js +440 -0
  159. package/dist/tools/edit/edit.js.map +1 -0
  160. package/dist/tools/edit/matcher.d.ts +24 -0
  161. package/dist/tools/edit/matcher.d.ts.map +1 -0
  162. package/dist/tools/edit/matcher.js +66 -0
  163. package/dist/tools/edit/matcher.js.map +1 -0
  164. package/dist/tools/edit/parser.d.ts +10 -0
  165. package/dist/tools/edit/parser.d.ts.map +1 -0
  166. package/dist/tools/edit/parser.js +120 -0
  167. package/dist/tools/edit/parser.js.map +1 -0
  168. package/dist/tools/edit/types.d.ts +16 -0
  169. package/dist/tools/edit/types.d.ts.map +1 -0
  170. package/dist/tools/edit/types.js +2 -0
  171. package/dist/tools/edit/types.js.map +1 -0
  172. package/dist/tools/edit/write_plan.d.ts +5 -0
  173. package/dist/tools/edit/write_plan.d.ts.map +1 -0
  174. package/dist/tools/edit/write_plan.js +98 -0
  175. package/dist/tools/edit/write_plan.js.map +1 -0
  176. package/dist/tools/edit.d.ts.map +1 -1
  177. package/dist/tools/edit.js +66 -1
  178. package/dist/tools/edit.js.map +1 -1
  179. package/dist/tools/execute/bash/constants.d.ts +24 -0
  180. package/dist/tools/execute/bash/constants.d.ts.map +1 -0
  181. package/dist/tools/execute/bash/constants.js +88 -0
  182. package/dist/tools/execute/bash/constants.js.map +1 -0
  183. package/dist/tools/execute/bash/errors.d.ts +39 -0
  184. package/dist/tools/execute/bash/errors.d.ts.map +1 -0
  185. package/dist/tools/execute/bash/errors.js +249 -0
  186. package/dist/tools/execute/bash/errors.js.map +1 -0
  187. package/dist/tools/execute/bash/execution.d.ts +18 -0
  188. package/dist/tools/execute/bash/execution.d.ts.map +1 -0
  189. package/dist/tools/execute/bash/execution.js +207 -0
  190. package/dist/tools/execute/bash/execution.js.map +1 -0
  191. package/dist/tools/execute/bash/index.d.ts +3 -0
  192. package/dist/tools/execute/bash/index.d.ts.map +1 -0
  193. package/dist/tools/execute/bash/index.js +117 -0
  194. package/dist/tools/execute/bash/index.js.map +1 -0
  195. package/dist/tools/execute/bash/platform.d.ts +6 -0
  196. package/dist/tools/execute/bash/platform.d.ts.map +1 -0
  197. package/dist/tools/execute/bash/platform.js +64 -0
  198. package/dist/tools/execute/bash/platform.js.map +1 -0
  199. package/dist/tools/execute/bash/security.d.ts +12 -0
  200. package/dist/tools/execute/bash/security.d.ts.map +1 -0
  201. package/dist/tools/execute/bash/security.js +26 -0
  202. package/dist/tools/execute/bash/security.js.map +1 -0
  203. package/dist/tools/execute/bash/validation.d.ts +16 -0
  204. package/dist/tools/execute/bash/validation.d.ts.map +1 -0
  205. package/dist/tools/execute/bash/validation.js +57 -0
  206. package/dist/tools/execute/bash/validation.js.map +1 -0
  207. package/dist/tools/execute/bash.d.ts +3 -0
  208. package/dist/tools/execute/bash.d.ts.map +1 -0
  209. package/dist/tools/execute/bash.js +728 -0
  210. package/dist/tools/execute/bash.js.map +1 -0
  211. package/dist/tools/execute/git.d.ts +3 -0
  212. package/dist/tools/execute/git.d.ts.map +1 -0
  213. package/dist/tools/execute/git.js +183 -0
  214. package/dist/tools/execute/git.js.map +1 -0
  215. package/dist/tools/execute/task_complete.d.ts +7 -0
  216. package/dist/tools/execute/task_complete.d.ts.map +1 -0
  217. package/dist/tools/execute/task_complete.js +38 -0
  218. package/dist/tools/execute/task_complete.js.map +1 -0
  219. package/dist/tools/fetch.d.ts.map +1 -1
  220. package/dist/tools/fetch.js +2 -0
  221. package/dist/tools/fetch.js.map +1 -1
  222. package/dist/tools/find.d.ts.map +1 -1
  223. package/dist/tools/find.js +2 -0
  224. package/dist/tools/find.js.map +1 -1
  225. package/dist/tools/git.d.ts.map +1 -1
  226. package/dist/tools/git.js +2 -0
  227. package/dist/tools/git.js.map +1 -1
  228. package/dist/tools/glob.d.ts.map +1 -1
  229. package/dist/tools/glob.js +2 -0
  230. package/dist/tools/glob.js.map +1 -1
  231. package/dist/tools/grep.d.ts.map +1 -1
  232. package/dist/tools/grep.js +2 -0
  233. package/dist/tools/grep.js.map +1 -1
  234. package/dist/tools/install/clawhub.d.ts +26 -0
  235. package/dist/tools/install/clawhub.d.ts.map +1 -0
  236. package/dist/tools/install/clawhub.js +182 -0
  237. package/dist/tools/install/clawhub.js.map +1 -0
  238. package/dist/tools/install/constants.d.ts +5 -0
  239. package/dist/tools/install/constants.d.ts.map +1 -0
  240. package/dist/tools/install/constants.js +5 -0
  241. package/dist/tools/install/constants.js.map +1 -0
  242. package/dist/tools/install/install_skill.d.ts +3 -0
  243. package/dist/tools/install/install_skill.d.ts.map +1 -0
  244. package/dist/tools/install/install_skill.js +132 -0
  245. package/dist/tools/install/install_skill.js.map +1 -0
  246. package/dist/tools/install/npm.d.ts +8 -0
  247. package/dist/tools/install/npm.d.ts.map +1 -0
  248. package/dist/tools/install/npm.js +52 -0
  249. package/dist/tools/install/npm.js.map +1 -0
  250. package/dist/tools/install/skill-content.d.ts +9 -0
  251. package/dist/tools/install/skill-content.d.ts.map +1 -0
  252. package/dist/tools/install/skill-content.js +89 -0
  253. package/dist/tools/install/skill-content.js.map +1 -0
  254. package/dist/tools/install_skill.d.ts.map +1 -1
  255. package/dist/tools/install_skill.js +2 -0
  256. package/dist/tools/install_skill.js.map +1 -1
  257. package/dist/tools/list_memory.d.ts +3 -0
  258. package/dist/tools/list_memory.d.ts.map +1 -0
  259. package/dist/tools/list_memory.js +55 -0
  260. package/dist/tools/list_memory.js.map +1 -0
  261. package/dist/tools/ls.d.ts.map +1 -1
  262. package/dist/tools/ls.js +2 -0
  263. package/dist/tools/ls.js.map +1 -1
  264. package/dist/tools/memory/delete_memory.d.ts +3 -0
  265. package/dist/tools/memory/delete_memory.d.ts.map +1 -0
  266. package/dist/tools/memory/delete_memory.js +46 -0
  267. package/dist/tools/memory/delete_memory.js.map +1 -0
  268. package/dist/tools/memory/list_memory.d.ts +3 -0
  269. package/dist/tools/memory/list_memory.d.ts.map +1 -0
  270. package/dist/tools/memory/list_memory.js +55 -0
  271. package/dist/tools/memory/list_memory.js.map +1 -0
  272. package/dist/tools/memory/save_memory.d.ts +3 -0
  273. package/dist/tools/memory/save_memory.d.ts.map +1 -0
  274. package/dist/tools/memory/save_memory.js +45 -0
  275. package/dist/tools/memory/save_memory.js.map +1 -0
  276. package/dist/tools/read.d.ts.map +1 -1
  277. package/dist/tools/read.js +2 -0
  278. package/dist/tools/read.js.map +1 -1
  279. package/dist/tools/registry.d.ts +7 -2
  280. package/dist/tools/registry.d.ts.map +1 -1
  281. package/dist/tools/registry.js +112 -17
  282. package/dist/tools/registry.js.map +1 -1
  283. package/dist/tools/save_memory.d.ts +3 -0
  284. package/dist/tools/save_memory.d.ts.map +1 -0
  285. package/dist/tools/save_memory.js +45 -0
  286. package/dist/tools/save_memory.js.map +1 -0
  287. package/dist/tools/search.d.ts.map +1 -1
  288. package/dist/tools/search.js +2 -0
  289. package/dist/tools/search.js.map +1 -1
  290. package/dist/tools/tree.d.ts +12 -0
  291. package/dist/tools/tree.d.ts.map +1 -0
  292. package/dist/tools/tree.js +87 -0
  293. package/dist/tools/tree.js.map +1 -0
  294. package/dist/tools/types.d.ts +12 -0
  295. package/dist/tools/types.d.ts.map +1 -1
  296. package/dist/tools/web/fetch.d.ts +3 -0
  297. package/dist/tools/web/fetch.d.ts.map +1 -0
  298. package/dist/tools/web/fetch.js +49 -0
  299. package/dist/tools/web/fetch.js.map +1 -0
  300. package/dist/tools/web/search.d.ts +3 -0
  301. package/dist/tools/web/search.d.ts.map +1 -0
  302. package/dist/tools/web/search.js +134 -0
  303. package/dist/tools/web/search.js.map +1 -0
  304. package/dist/tools/write_plan.d.ts.map +1 -1
  305. package/dist/tools/write_plan.js +2 -0
  306. package/dist/tools/write_plan.js.map +1 -1
  307. package/dist/tui/commands.d.ts +4 -0
  308. package/dist/tui/commands.d.ts.map +1 -0
  309. package/dist/tui/commands.js +118 -0
  310. package/dist/tui/commands.js.map +1 -0
  311. package/dist/tui/components/colors.d.ts +36 -0
  312. package/dist/tui/components/colors.d.ts.map +1 -0
  313. package/dist/tui/components/colors.js +48 -0
  314. package/dist/tui/components/colors.js.map +1 -0
  315. package/dist/tui/components/header-bar.d.ts +26 -0
  316. package/dist/tui/components/header-bar.d.ts.map +1 -0
  317. package/dist/tui/components/header-bar.js +78 -0
  318. package/dist/tui/components/header-bar.js.map +1 -0
  319. package/dist/tui/components/index.d.ts +9 -0
  320. package/dist/tui/components/index.d.ts.map +1 -0
  321. package/dist/tui/components/index.js +9 -0
  322. package/dist/tui/components/index.js.map +1 -0
  323. package/dist/tui/components/landing-screen.d.ts +12 -0
  324. package/dist/tui/components/landing-screen.d.ts.map +1 -0
  325. package/dist/tui/components/landing-screen.js +53 -0
  326. package/dist/tui/components/landing-screen.js.map +1 -0
  327. package/dist/tui/components/pixel-logo.d.ts +5 -0
  328. package/dist/tui/components/pixel-logo.d.ts.map +1 -0
  329. package/dist/tui/components/pixel-logo.js +27 -0
  330. package/dist/tui/components/pixel-logo.js.map +1 -0
  331. package/dist/tui/components/plan-utils.d.ts +11 -0
  332. package/dist/tui/components/plan-utils.d.ts.map +1 -0
  333. package/dist/tui/components/plan-utils.js +62 -0
  334. package/dist/tui/components/plan-utils.js.map +1 -0
  335. package/dist/tui/components/plans-list.d.ts +18 -0
  336. package/dist/tui/components/plans-list.d.ts.map +1 -0
  337. package/dist/tui/components/plans-list.js +46 -0
  338. package/dist/tui/components/plans-list.js.map +1 -0
  339. package/dist/tui/components/rich-footer.d.ts +29 -0
  340. package/dist/tui/components/rich-footer.d.ts.map +1 -0
  341. package/dist/tui/components/rich-footer.js +124 -0
  342. package/dist/tui/components/rich-footer.js.map +1 -0
  343. package/dist/tui/components/todo-panel.d.ts +14 -0
  344. package/dist/tui/components/todo-panel.d.ts.map +1 -0
  345. package/dist/tui/components/todo-panel.js +83 -0
  346. package/dist/tui/components/todo-panel.js.map +1 -0
  347. package/dist/tui/components.d.ts.map +1 -1
  348. package/dist/tui/components.js +14 -7
  349. package/dist/tui/components.js.map +1 -1
  350. package/dist/tui/index.d.ts +2 -1
  351. package/dist/tui/index.d.ts.map +1 -1
  352. package/dist/tui/index.js +99 -505
  353. package/dist/tui/index.js.map +1 -1
  354. package/dist/tui/overlays.d.ts +28 -0
  355. package/dist/tui/overlays.d.ts.map +1 -0
  356. package/dist/tui/overlays.js +154 -0
  357. package/dist/tui/overlays.js.map +1 -0
  358. package/dist/tui/themes.d.ts +5 -0
  359. package/dist/tui/themes.d.ts.map +1 -0
  360. package/dist/tui/themes.js +38 -0
  361. package/dist/tui/themes.js.map +1 -0
  362. package/dist/web/index.d.ts.map +1 -1
  363. package/dist/web/index.js +60 -122
  364. package/dist/web/index.js.map +1 -1
  365. package/dist/web/public/index.html +1 -1
  366. package/dist/web/public/messages.js +41 -25
  367. package/dist/web/public/state.js +72 -72
  368. package/dist/web/public/style.css +1126 -996
  369. package/dist/web/public/websocket.js +267 -267
  370. package/dist/web/session-bridge.d.ts.map +1 -1
  371. package/dist/web/session-bridge.js +82 -3
  372. package/dist/web/session-bridge.js.map +1 -1
  373. package/docs/ast-tools.md +117 -0
  374. package/package.json +60 -55
  375. package/scripts/check-line-count.mjs +53 -0
  376. package/scripts/copy-ast-wasm.mjs +32 -0
  377. package/src/agent/__tests__/session-restore.test.ts +284 -0
  378. package/src/agent/commands.ts +274 -0
  379. package/src/agent/compaction.ts +9 -2
  380. package/src/agent/index.ts +30 -25
  381. package/src/agent/memory.ts +60 -0
  382. package/src/agent/prompt-builder.ts +100 -26
  383. package/src/agent/providers/anthropic.ts +179 -179
  384. package/src/agent/providers/google.ts +210 -210
  385. package/src/agent/providers/ollama.ts +186 -186
  386. package/src/agent/providers/openai.ts +18 -3
  387. package/src/agent/session-chat.ts +480 -0
  388. package/src/agent/session-display.ts +195 -0
  389. package/src/agent/session-persistence.ts +146 -0
  390. package/src/agent/session-utils.ts +54 -0
  391. package/src/agent/session.ts +261 -718
  392. package/src/cli/index.ts +73 -4
  393. package/src/config/index.ts +342 -342
  394. package/src/index.ts +30 -3
  395. package/src/tools/__tests__/bash.test.ts +187 -1
  396. package/src/tools/__tests__/edit.test.ts +275 -11
  397. package/src/tools/__tests__/fetch.test.ts +1 -1
  398. package/src/tools/__tests__/glob.test.ts +1 -1
  399. package/src/tools/__tests__/grep.test.ts +1 -1
  400. package/src/tools/__tests__/ls.test.ts +1 -1
  401. package/src/tools/__tests__/read.test.ts +1 -1
  402. package/src/tools/__tests__/registry.test.ts +332 -0
  403. package/src/tools/__tests__/write_plan.test.ts +1 -1
  404. package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
  405. package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
  406. package/src/tools/codebase/ast/adapter.ts +44 -0
  407. package/src/tools/codebase/ast/tools.ts +322 -0
  408. package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
  409. package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
  410. package/src/tools/codebase/ast/types.ts +102 -0
  411. package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  412. package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  413. package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  414. package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  415. package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  416. package/src/tools/{find.ts → codebase/find.ts} +4 -2
  417. package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
  418. package/src/tools/{grep.ts → codebase/grep.ts} +4 -2
  419. package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
  420. package/src/tools/{read.ts → codebase/read.ts} +5 -3
  421. package/src/tools/edit/diff-compute.ts +21 -0
  422. package/src/tools/edit/diff-render.ts +145 -0
  423. package/src/tools/edit/edit.ts +468 -0
  424. package/src/tools/edit/matcher.ts +70 -0
  425. package/src/tools/edit/parser.ts +131 -0
  426. package/src/tools/edit/types.ts +19 -0
  427. package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
  428. package/src/tools/execute/bash/constants.ts +93 -0
  429. package/src/tools/execute/bash/errors.ts +304 -0
  430. package/src/tools/execute/bash/execution.ts +211 -0
  431. package/src/tools/execute/bash/index.ts +131 -0
  432. package/src/tools/execute/bash/platform.ts +65 -0
  433. package/src/tools/execute/bash/security.ts +28 -0
  434. package/src/tools/execute/bash/validation.ts +78 -0
  435. package/src/tools/execute/git.ts +209 -0
  436. package/src/tools/execute/task_complete.ts +40 -0
  437. package/src/tools/install/clawhub.ts +222 -0
  438. package/src/tools/install/constants.ts +4 -0
  439. package/src/tools/install/install_skill.ts +150 -0
  440. package/src/tools/install/npm.ts +60 -0
  441. package/src/tools/install/skill-content.ts +108 -0
  442. package/src/tools/memory/delete_memory.ts +46 -0
  443. package/src/tools/memory/list_memory.ts +53 -0
  444. package/src/tools/memory/save_memory.ts +45 -0
  445. package/src/tools/registry.ts +119 -19
  446. package/src/tools/tree.ts +103 -0
  447. package/src/tools/types.ts +13 -0
  448. package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
  449. package/src/tools/{search.ts → web/search.ts} +4 -2
  450. package/src/tui/commands.ts +135 -0
  451. package/src/tui/components/colors.ts +52 -0
  452. package/src/tui/components/header-bar.ts +88 -0
  453. package/src/tui/components/index.ts +8 -0
  454. package/src/tui/components/landing-screen.ts +67 -0
  455. package/src/tui/components/pixel-logo.ts +33 -0
  456. package/src/tui/components/plan-utils.ts +69 -0
  457. package/src/tui/components/plans-list.ts +55 -0
  458. package/src/tui/components/rich-footer.ts +146 -0
  459. package/src/tui/components/todo-panel.ts +95 -0
  460. package/src/tui/index.ts +335 -786
  461. package/src/tui/overlays.ts +203 -0
  462. package/src/tui/themes.ts +41 -0
  463. package/src/web/index.ts +64 -127
  464. package/src/web/public/index.html +1 -1
  465. package/src/web/public/messages.js +41 -25
  466. package/src/web/public/state.js +72 -72
  467. package/src/web/public/style.css +1126 -996
  468. package/src/web/public/websocket.js +267 -267
  469. package/src/web/session-bridge.ts +272 -193
  470. package/AGENT.md +0 -99
  471. package/src/tools/bash.ts +0 -125
  472. package/src/tools/edit.ts +0 -747
  473. package/src/tools/git.ts +0 -76
  474. package/src/tools/install_skill.ts +0 -539
  475. package/src/tui/components.ts +0 -635
@@ -1,635 +0,0 @@
1
- import * as fs from 'node:fs'
2
- import * as path from 'node:path'
3
- import type { Component, SelectItem, SelectListTheme } from '../pi-tui/index.js'
4
- import { SelectList } from '../pi-tui/index.js'
5
- import { PLAN_DIR } from '../tools/write_plan.js'
6
- import { visibleLen } from './utils.js'
7
-
8
- // ── ANSI Color Helpers ───────────────────────────────────────────────────────
9
-
10
- // Re-applies background after every full reset (\x1b[0m) so that foreground
11
- // color resets don't "punch through" the background.
12
- function safeBg(text: string, bg: string): string {
13
- return `\x1b[${bg}m${text.replace(/\x1b\[0m/g, `\x1b[0m\x1b[${bg}m`)}\x1b[0m`
14
- }
15
-
16
- export const colors = {
17
- bgDark: (text: string) => safeBg(text, '48;2;30;30;30'),
18
- bgDim: (text: string) => safeBg(text, '48;2;25;25;25'),
19
- headerBg: (text: string) => safeBg(text, '48;2;170;170;170'),
20
- separator: (text: string) => `\x1b[38;2;60;60;60m${text}\x1b[0m`,
21
- statusBg: (text: string) => safeBg(text, '48;2;25;25;35'),
22
- running: (text: string) => `\x1b[38;2;0;255;100m${text}\x1b[0m`,
23
- idle: (text: string) => `\x1b[38;2;150;150;150m${text}\x1b[0m`,
24
- doneTodo: (text: string) => `\x1b[38;2;100;200;100m${text}\x1b[0m`,
25
- todo: (text: string) => `\x1b[38;2;150;150;150m${text}\x1b[0m`,
26
- accent: (text: string) => `\x1b[38;2;0;170;255m${text}\x1b[0m`,
27
- dim: (text: string) => `\x1b[38;2;90;90;90m${text}\x1b[0m`,
28
- userLabel: (text: string) => `\x1b[38;2;255;200;50m${text}\x1b[0m`,
29
- assistantLabel: (text: string) => `\x1b[38;2;0;255;150m${text}\x1b[0m`,
30
- error: (text: string) => `\x1b[38;2;255;80;80m${text}\x1b[0m`,
31
- success: (text: string) => `\x1b[38;2;0;200;100m${text}\x1b[0m`,
32
- inputPrompt: (text: string) => `\x1b[38;2;0;170;255m${text}\x1b[0m`,
33
- warn: (text: string) => `\x1b[38;2;255;200;50m${text}\x1b[0m`,
34
- }
35
-
36
- // ── App Version (read from package.json) ─────────────────────────────────
37
-
38
- const APP_VERSION: string = (() => {
39
- try {
40
- const pkgPath = new URL('../../package.json', import.meta.url)
41
- const raw = fs.readFileSync(pkgPath, 'utf-8')
42
- return JSON.parse(raw).version || '0.1.0'
43
- } catch {
44
- return '0.1.0'
45
- }
46
- })()
47
-
48
- // ── Pixel font for "lonnycode" logo (5 rows — 5 cols per char) ──────────
49
-
50
- const PIXEL_FONT: Record<string, string[]> = {
51
- L: ['█ ', '█ ', '█ ', '█ ', '█████'],
52
- O: [' ███ ', '█ █', '█ █', '█ █', ' ███ '],
53
- N: ['█ █', '██ █', '█ █ █', '█ ██', '█ █'],
54
- Y: ['█ █', ' █ █ ', ' █ ', ' █ ', ' █ '],
55
- C: [' ███ ', '█ ', '█ ', '█ ', ' ███ '],
56
- D: ['███ ', '█ █ ', '█ █', '█ █ ', '███ '],
57
- E: ['█████', '█ ', '███ ', '█ ', '█████'],
58
- }
59
-
60
- const LONNY_CHARS = ['L', 'O', 'N', 'N', 'Y']
61
- const CODE_CHARS = ['C', 'O', 'D', 'E']
62
-
63
- const PIXEL_LOGO_WIDTH = 54 // 5 cols — 9 chars + 8 gaps + 2 gap between words
64
-
65
- function renderPixelLogo(): string[] {
66
- const midGray = '\x1b[38;2;160;160;160m'
67
- const brightWhite = '\x1b[38;2;255;255;255m'
68
- const reset = '\x1b[0m'
69
- const lines: string[] = []
70
- for (let row = 0; row < 5; row++) {
71
- const lonnyPart = LONNY_CHARS.map(ch => PIXEL_FONT[ch][row]).join(' ')
72
- const codePart = CODE_CHARS.map(ch => PIXEL_FONT[ch][row]).join(' ')
73
- lines.push(`${midGray + lonnyPart} ${brightWhite}${codePart}${reset}`)
74
- }
75
- return lines
76
- }
77
-
78
- // ── Landing input colors ────────────────────────────────────────────────
79
-
80
- export const landingColors = {
81
- inputBg: '\x1b[48;2;35;35;35m',
82
- inputBorder: '\x1b[38;2;60;60;60m',
83
- cyanBar: '\x1b[38;2;0;200;255m',
84
- placeholderDim: '\x1b[38;2;130;130;130m',
85
- placeholderQuote: '\x1b[38;2;160;160;160m',
86
- inputText: '\x1b[38;2;220;220;220m',
87
- statusBg: '\x1b[48;2;18;18;18m',
88
- statusText: '\x1b[38;2;110;110;110m',
89
- statusAccent: '\x1b[38;2;0;170;255m',
90
- reset: '\x1b[0m',
91
- }
92
-
93
- // ── Helpers ──────────────────────────────────────────────────────────────
94
-
95
- export interface PlanEntry {
96
- name: string
97
- description: string
98
- fullPath: string
99
- mtime: number
100
- }
101
-
102
- export function listPlans(cwd: string): PlanEntry[] {
103
- const planDir = path.resolve(cwd, PLAN_DIR)
104
- try {
105
- const files = fs.readdirSync(planDir)
106
- return files
107
- .filter(f => f.endsWith('.md'))
108
- .map(f => {
109
- const fullPath = path.join(planDir, f)
110
- let mtime = 0
111
- try {
112
- mtime = fs.statSync(fullPath).mtimeMs
113
- } catch {
114
- /* ignore */
115
- }
116
- return {
117
- name: f.replace(/\.md$/, ''),
118
- description: f,
119
- fullPath,
120
- mtime,
121
- }
122
- })
123
- .sort((a, b) => b.mtime - a.mtime)
124
- } catch {
125
- return []
126
- }
127
- }
128
-
129
- export function loadTodos(filePath: string): string {
130
- try {
131
- const content = fs.readFileSync(filePath, 'utf-8')
132
- const lines = content.split('\n')
133
- const todos: string[] = []
134
- let inTodo = false
135
- for (const line of lines) {
136
- if (line.startsWith('## Todo List')) {
137
- inTodo = true
138
- continue
139
- }
140
- if (inTodo && line.startsWith('## ')) break
141
- if (inTodo) {
142
- const m = line.trim().match(/^- \[([ x])\]\s+(.+)/)
143
- if (m) {
144
- const done = m[1] === 'x'
145
- const check = done ? '\u2705' : '\u2B1C'
146
- todos.push(`${check} ${done ? colors.doneTodo(m[2]) : colors.todo(m[2])}`)
147
- }
148
- }
149
- }
150
- return todos.length > 0 ? todos.join('\n') : '(no todo items)'
151
- } catch {
152
- return '(no plan selected)'
153
- }
154
- }
155
-
156
- export function plansToItems(plans: PlanEntry[]): SelectItem[] {
157
- return plans.map(p => ({
158
- value: p.name,
159
- label: p.name,
160
- description: p.mtime
161
- ? `${new Date(p.mtime).toLocaleDateString()} ${new Date(p.mtime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`
162
- : '',
163
- }))
164
- }
165
-
166
- // ── Rich Footer helpers ────────────────────────────────────────────────────
167
-
168
- function formatTokens(n: number): string {
169
- if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
170
- if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`
171
- return String(n)
172
- }
173
-
174
- // ── OpenCode-style Header ────────────────────────────────────────────────
175
-
176
- export class HeaderBar implements Component {
177
- private mode: string
178
- private model: string
179
- private provider: string
180
- private agentStatus: 'running' | 'idle'
181
- private planCount: number
182
- private planName: string
183
- private totalInputTokens: number = 0
184
- private totalOutputTokens: number = 0
185
- private totalApiCalls: number = 0
186
- private projectName: string = ''
187
-
188
- constructor(model: string, provider: string) {
189
- this.model = model
190
- this.provider = provider
191
- this.mode = 'code'
192
- this.agentStatus = 'idle'
193
- this.planCount = 0
194
- this.planName = ''
195
- }
196
-
197
- setMode(m: string): void {
198
- this.mode = m
199
- }
200
- setAgentStatus(s: 'running' | 'idle'): void {
201
- this.agentStatus = s
202
- }
203
- setPlanCount(n: number): void {
204
- this.planCount = n
205
- }
206
- setPlanName(n: string): void {
207
- this.planName = n
208
- }
209
- setTokenUsage(inputTokens: number, outputTokens: number, apiCalls: number): void {
210
- this.totalInputTokens = inputTokens
211
- this.totalOutputTokens = outputTokens
212
- this.totalApiCalls = apiCalls
213
- }
214
- setProjectName(name: string): void {
215
- this.projectName = name
216
- }
217
- invalidate(): void {}
218
- handleInput?(_data: string): void {}
219
-
220
- render(width: number): string[] {
221
- const appName = colors.accent('\u2588 lonny')
222
- const statusDot =
223
- this.agentStatus === 'running' ? colors.running('\u25CF') : colors.dim('\u25CB')
224
- const statusLabel =
225
- this.agentStatus === 'running' ? colors.running('running') : colors.dim('idle')
226
- const modeLabel = this.mode === 'ask' ? colors.success(this.mode) : colors.warn(this.mode)
227
- const modelInfo = colors.dim(`${this.provider}/${this.model}`)
228
-
229
- let rightPart = `${statusDot} ${statusLabel} ${modeLabel} ${modelInfo}`
230
-
231
- // Show token usage if there are any
232
- const totalTokens = this.totalInputTokens + this.totalOutputTokens
233
- if (totalTokens > 0) {
234
- const tokenStr = `\u25B4${this.totalInputTokens} \u25BE${this.totalOutputTokens} ${totalTokens}`
235
- const callsStr = `${this.totalApiCalls} calls`
236
- const projectTag = this.projectName ? `${this.projectName} ` : ''
237
- rightPart += ` ${colors.dim('|')} ${colors.dim(`${projectTag}${tokenStr} ${callsStr}`)}`
238
- }
239
-
240
- if (this.planCount > 0) {
241
- rightPart += ` ${colors.dim('|')} ${colors.accent(`${this.planCount} plan${this.planCount > 1 ? 's' : ''}`)}`
242
- if (this.planName) rightPart += ` ${colors.dim(this.planName)}`
243
- }
244
-
245
- const line = ` ${appName} ${colors.dim('·')} ${rightPart}`
246
- return [colors.headerBg(line), colors.dim('\u2500'.repeat(width))]
247
- }
248
- }
249
-
250
- // ── Rich Footer (cwd | mode | tokens | model | version + command hints) ────
251
-
252
- export class RichFooter implements Component {
253
- private cwd: string
254
- private mode: string = 'code'
255
- private model: string = ''
256
- private provider: string = ''
257
- private totalInputTokens: number = 0
258
- private totalOutputTokens: number = 0
259
- private totalApiCalls: number = 0
260
- private balance: string = ''
261
- private webBalance: string = ''
262
- private visible = true
263
- private phase: 'landing' | 'chat' = 'landing'
264
- private agentStatus: 'running' | 'idle' = 'idle'
265
-
266
- constructor(cwd: string, model: string, provider: string) {
267
- this.cwd = cwd
268
- this.model = model
269
- this.provider = provider
270
- }
271
-
272
- setMode(m: string): void {
273
- this.mode = m
274
- }
275
- setModel(model: string, provider: string): void {
276
- this.model = model
277
- this.provider = provider
278
- }
279
- setTokenUsage(inputTokens: number, outputTokens: number, apiCalls: number): void {
280
- this.totalInputTokens = inputTokens
281
- this.totalOutputTokens = outputTokens
282
- this.totalApiCalls = apiCalls
283
- }
284
- setVisible(v: boolean): void {
285
- this.visible = v
286
- }
287
- setPhase(p: 'landing' | 'chat'): void {
288
- this.phase = p
289
- }
290
- setBalance(b: string): void {
291
- this.balance = b
292
- }
293
- setWebBalance(wb: string): void {
294
- this.webBalance = wb
295
- }
296
- setAgentStatus(s: 'running' | 'idle'): void {
297
- this.agentStatus = s
298
- }
299
-
300
- invalidate(): void {}
301
- handleInput?(_data: string): void {}
302
-
303
- private visibleLen(s: string): number {
304
- return visibleLen(s)
305
- }
306
-
307
- render(width: number): string[] {
308
- if (!this.visible || width < 40) return []
309
-
310
- const { statusBg, statusText, statusAccent, reset } = landingColors
311
-
312
- // Left: working directory
313
- const dir = this.cwd.length > 30 ? `...${this.cwd.slice(-27)}` : this.cwd
314
- const leftPart = `${statusAccent}\u25A0${reset}${statusBg}${statusText} ${dir}${reset}`
315
-
316
- if (this.phase === 'landing') {
317
- // Minimal: cwd | ready | version
318
- const centerPart = `${statusBg + statusText} ready ${reset}`
319
- const rightPart = `${statusBg + statusText}v${APP_VERSION} ${reset}`
320
- const line = leftPart + centerPart + rightPart
321
- const visLen = this.visibleLen(line)
322
- const padded = visLen < width ? line + statusBg + ' '.repeat(width - visLen) + reset : line
323
- return [padded]
324
- }
325
-
326
- // ── Chat phase: build segments ────────────────────────────────────────
327
- const segments: string[] = []
328
-
329
- // Status dot
330
- const statusDot =
331
- this.agentStatus === 'running'
332
- ? `\x1b[38;2;0;255;100m\u25CF\x1b[0m`
333
- : `\x1b[38;2;150;150;150m\u25CB\x1b[0m`
334
- const statusLabel =
335
- this.agentStatus === 'running'
336
- ? `\x1b[38;2;0;255;100mrunning\x1b[0m`
337
- : `\x1b[38;2;150;150;150midle\x1b[0m`
338
- segments.push(`${statusDot} ${statusLabel}`)
339
-
340
- // Mode tag
341
- const modeTag =
342
- this.mode === 'plan'
343
- ? `\x1b[38;2;255;200;50m${this.mode}\x1b[0m`
344
- : this.mode === 'ask'
345
- ? `\x1b[38;2;0;200;100m${this.mode}\x1b[0m`
346
- : `\x1b[38;2;0;200;255m${this.mode}\x1b[0m`
347
- segments.push(modeTag)
348
-
349
- // Model/provider
350
- if (this.model) {
351
- segments.push(`\x1b[38;2;110;110;110m${this.provider}/${this.model}\x1b[0m`)
352
- }
353
-
354
- // Token usage
355
- const totalTokens = this.totalInputTokens + this.totalOutputTokens
356
- if (totalTokens > 0) {
357
- const tokenStr = `\u25B4${formatTokens(this.totalInputTokens)} \u25BE${formatTokens(this.totalOutputTokens)} ${formatTokens(totalTokens)}`
358
- segments.push(`\x1b[38;2;110;110;110m${tokenStr}\x1b[0m`)
359
- segments.push(`\x1b[38;2;110;110;110m${this.totalApiCalls}c\x1b[0m`)
360
- }
361
-
362
- // Balance (DeepSeek official API only)
363
- if (this.webBalance) {
364
- // Show the web-style breakdown if available
365
- segments.push(`\x1b[38;2;255;200;50m\u4F59\u989D\x1b[0m\uFF1A${this.webBalance}`)
366
- } else if (this.balance) {
367
- segments.push(`\x1b[38;2;255;200;50m\u4F59\u989D\x1b[0m\uFF1A${this.balance}`)
368
- }
369
-
370
- // Build center part from segments
371
- const separator = `${statusBg} \x1b[38;2;60;60;60m\u2502\x1b[0m ${reset}`
372
- const centerContent = `${statusBg + statusText} ${segments.join(separator)} ${reset}`
373
-
374
- // Right: version
375
- const rightPart = `${statusBg + statusText}v${APP_VERSION} ${reset}`
376
-
377
- const line = leftPart + centerContent + rightPart
378
-
379
- // If there's extra space, append command hints
380
- const lineLen = line.length - 2 * statusBg.length - reset.length // approximate
381
- let result = line
382
- if (lineLen < width - 40) {
383
- const hints = [
384
- '\x1b[38;2;110;110;110m/mode\x1b[0m',
385
- '\x1b[38;2;110;110;110m/plans\x1b[0m',
386
- '\x1b[38;2;110;110;110m/help\x1b[0m',
387
- '\x1b[38;2;110;110;110m?\x1b[0m',
388
- ].join(' \x1b[38;2;60;60;60m\u00b7\x1b[0m ')
389
- const hintStr = `${statusBg + statusText} ${hints} ${reset}`
390
- // Only append if it fits
391
- const fullLine = line + hintStr
392
- // Calculate approximate visible length (strip ANSI)
393
- const approxLen = fullLine.replace(/\x1b\[[0-9;]*m/g, '').length
394
- if (approxLen <= width) {
395
- result = fullLine
396
- }
397
- }
398
-
399
- const visLen = this.visibleLen(result)
400
- const padded = visLen < width ? result + statusBg + ' '.repeat(width - visLen) + reset : result
401
-
402
- return [padded]
403
- }
404
- }
405
-
406
- // ── LandingScreen (pixel logo + welcome) ──────────────────────────────
407
-
408
- export class LandingScreen implements Component {
409
- onSubmit?: (value: string) => void
410
- private model: string
411
- private provider: string
412
-
413
- constructor(model: string, provider: string) {
414
- this.model = model
415
- this.provider = provider
416
- }
417
-
418
- invalidate(): void {}
419
-
420
- handleInput(data: string): void {
421
- // Any key transitions to chat
422
- if (data && this.onSubmit) {
423
- this.onSubmit(data)
424
- }
425
- }
426
-
427
- private visibleLen(s: string): number {
428
- return visibleLen(s)
429
- }
430
-
431
- render(width: number): string[] {
432
- const lines: string[] = []
433
- const center = (text: string, totalWidth: number): string => {
434
- const textWidth = this.visibleLen(text)
435
- const pad = Math.max(0, Math.floor((totalWidth - textWidth) / 2))
436
- return ' '.repeat(pad) + text
437
- }
438
-
439
- // ── Pixel logo ─────────────────────────────────────────────────────
440
- const logoLines = renderPixelLogo()
441
- const logoPad = Math.max(0, Math.floor((width - PIXEL_LOGO_WIDTH) / 2))
442
- const padStr = ' '.repeat(logoPad)
443
-
444
- for (const line of logoLines) {
445
- lines.push(padStr + line)
446
- }
447
-
448
- // ── Decorative divider ────────────────────────────────────────────
449
- const divider = colors.dim('\u2500'.repeat(Math.min(36, width - 4)))
450
- lines.push(center(divider, width))
451
-
452
- // ── Prompt text ───────────────────────────────────────────────────
453
- lines.push('')
454
- const prompt =
455
- colors.dim('Type a message and press ') + colors.accent('Enter') + colors.dim(' to start')
456
- lines.push(center(prompt, width))
457
- lines.push('')
458
-
459
- // ── Quick command hints ───────────────────────────────────────────
460
- const cmds = [
461
- colors.inputPrompt('/mode'),
462
- colors.inputPrompt('/model'),
463
- colors.inputPrompt('/plans'),
464
- colors.inputPrompt('/help'),
465
- ].join(colors.dim(' \u00B7 '))
466
- lines.push(center(colors.dim('Commands: ') + cmds, width))
467
-
468
- // ── Model & version info ──────────────────────────────────────────
469
- const modelInfo = colors.dim(`${this.provider}/${this.model}`)
470
- const versionInfo = colors.dim(`v${APP_VERSION}`)
471
- lines.push(center(modelInfo + colors.separator(' \u2502 ') + versionInfo, width))
472
-
473
- return lines
474
- }
475
- }
476
-
477
- // ── PlansList wrapper (SelectList has no setItems) ───────────────────────
478
-
479
- export class PlansList implements Component {
480
- private selectList: SelectList
481
- private allItems: SelectItem[]
482
- private maxVisible: number
483
- private theme: SelectListTheme
484
- onSelectionChange?: (item: SelectItem) => void
485
-
486
- constructor(items: SelectItem[], maxVisible: number, theme: SelectListTheme) {
487
- this.allItems = items
488
- this.selectList = new SelectList(items, maxVisible, theme)
489
- this.maxVisible = maxVisible
490
- this.theme = theme
491
- this.selectList.onSelectionChange = item => {
492
- if (this.onSelectionChange) this.onSelectionChange(item)
493
- }
494
- }
495
-
496
- setFilter(filter: string): void {
497
- this.selectList.setFilter(filter)
498
- }
499
-
500
- clearFilter(): void {
501
- this.selectList.setFilter('')
502
- }
503
-
504
- refresh(items: SelectItem[]): void {
505
- this.allItems = items
506
- const cb = this.onSelectionChange
507
- this.selectList = new SelectList(items, this.maxVisible, this.theme)
508
- this.selectList.onSelectionChange = cb
509
- }
510
-
511
- getSelectedItem(): SelectItem | null {
512
- return this.selectList.getSelectedItem()
513
- }
514
-
515
- setSelectedIndex(index: number): void {
516
- this.selectList.setSelectedIndex(index)
517
- }
518
-
519
- invalidate(): void {
520
- this.selectList.invalidate()
521
- }
522
-
523
- render(width: number): string[] {
524
- return this.selectList.render(width)
525
- }
526
-
527
- handleInput(data: string): void {
528
- this.selectList.handleInput(data)
529
- }
530
- }
531
-
532
- // ── Persistent Todo Side Panel ───────────────────────────────────────
533
-
534
- export class TodoPanel implements Component {
535
- private cwd: string
536
- private todos: { text: string; done: boolean }[] = []
537
- private planName: string = ''
538
- private dirty: boolean = true
539
-
540
- constructor(cwd: string) {
541
- this.cwd = cwd
542
- }
543
-
544
- refresh(): void {
545
- this.dirty = true
546
- }
547
-
548
- invalidate(): void {
549
- this.dirty = true
550
- }
551
-
552
- private load(): void {
553
- this.dirty = false
554
- const plans = listPlans(this.cwd)
555
- if (plans.length === 0) {
556
- this.todos = []
557
- this.planName = ''
558
- return
559
- }
560
- const plan = plans[0]
561
- this.planName = plan.name
562
- this.todos = []
563
- try {
564
- const content = fs.readFileSync(plan.fullPath, 'utf-8')
565
- const lines = content.split('\n')
566
- let inTodo = false
567
- for (const line of lines) {
568
- if (line.startsWith('## Todo List')) {
569
- inTodo = true
570
- continue
571
- }
572
- if (inTodo && line.startsWith('## ')) break
573
- if (inTodo) {
574
- const m = line.trim().match(/^- \[([ x])\]\s+(.+)/)
575
- if (m) {
576
- this.todos.push({ text: m[2], done: m[1] === 'x' })
577
- }
578
- }
579
- }
580
- } catch {
581
- // ignore file read errors
582
- }
583
- }
584
-
585
- private visibleLen(s: string): number {
586
- return visibleLen(s)
587
- }
588
-
589
- render(width: number): string[] {
590
- if (this.dirty) this.load()
591
-
592
- const lines: string[] = []
593
-
594
- // ── Header ──
595
- const headerText = ` ${colors.accent('\u25B6')} TODO`
596
- const headerPadding = Math.max(0, width - this.visibleLen(headerText))
597
- lines.push(colors.bgDark(headerText + ' '.repeat(headerPadding)))
598
-
599
- // ── Divider ──
600
- lines.push(colors.separator('\u2500'.repeat(width)))
601
-
602
- if (!this.planName) {
603
- lines.push(colors.dim(' (no plan)'))
604
- return lines
605
- }
606
-
607
- if (this.todos.length === 0) {
608
- lines.push(colors.dim(' (no todos)'))
609
- return lines
610
- }
611
-
612
- // ── Todo items ──
613
- const contentWidth = width - 3 // leave room for icon + space
614
- for (const todo of this.todos) {
615
- const icon = todo.done ? '\u2705' : '\u2B1C'
616
- const textStyle = todo.done ? colors.doneTodo : colors.todo
617
- const text = todo.text
618
- // Truncate text if too long (account for visible width)
619
- const maxTextLen = contentWidth - 1 // 1 for space after icon
620
- let truncated = text
621
- let textVisLen = this.visibleLen(textStyle(text))
622
- if (textVisLen > maxTextLen) {
623
- // Simple truncation: cut raw text and add ellipsis
624
- truncated = `${text.slice(0, maxTextLen - 1)}\u2026`
625
- textVisLen = maxTextLen
626
- }
627
- const line = ` ${icon} ${textStyle(truncated)}`
628
- const visLen = this.visibleLen(line)
629
- const padding = Math.max(0, width - visLen)
630
- lines.push(line + ' '.repeat(padding))
631
- }
632
-
633
- return lines
634
- }
635
- }