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
@@ -0,0 +1,411 @@
1
+ /* ── Message Rendering ── */
2
+
3
+ import {
4
+ messagesEl,
5
+ pendingToolCalls,
6
+ state,
7
+ tokenCache,
8
+ tokenCalls,
9
+ tokenIn,
10
+ tokenOut,
11
+ } from './state.js'
12
+ import {
13
+ escapeHtml,
14
+ formatTimestamp,
15
+ formatTokenCount,
16
+ renderMarkdown,
17
+ scrollToBottom,
18
+ } from './utils.js'
19
+
20
+ export function addSystemMessage(text) {
21
+ const div = document.createElement('div')
22
+ div.className = 'system-message'
23
+ div.textContent = text
24
+ messagesEl.appendChild(div)
25
+ scrollToBottom()
26
+ }
27
+
28
+ export function addUserMessage(text) {
29
+ const msgDiv = document.createElement('div')
30
+ msgDiv.className = 'message'
31
+
32
+ const header = document.createElement('div')
33
+ header.className = 'message-header'
34
+ header.innerHTML = `<span class="label user">You</span> <span class="timestamp">${formatTimestamp()}</span>`
35
+ msgDiv.appendChild(header)
36
+
37
+ const body = document.createElement('div')
38
+ body.className = 'message-body user'
39
+ body.innerHTML = renderMarkdown(text)
40
+ msgDiv.appendChild(body)
41
+
42
+ messagesEl.appendChild(msgDiv)
43
+ scrollToBottom()
44
+ }
45
+
46
+ export function startAssistantMessage() {
47
+ state.streamingText = ''
48
+ state.streamingMsgEl = document.createElement('div')
49
+ state.streamingMsgEl.className = 'message'
50
+
51
+ const header = document.createElement('div')
52
+ header.className = 'message-header'
53
+ header.innerHTML = `<span class="label assistant">Lonny</span> <span class="timestamp">${formatTimestamp()}</span>`
54
+ state.streamingMsgEl.appendChild(header)
55
+
56
+ const body = document.createElement('div')
57
+ body.className = 'message-body assistant streaming'
58
+ body.textContent = ''
59
+ state.streamingMsgEl.appendChild(body)
60
+
61
+ messagesEl.appendChild(state.streamingMsgEl)
62
+ scrollToBottom()
63
+ }
64
+
65
+ export function appendChunk(text) {
66
+ if (!state.streamingMsgEl) {
67
+ startAssistantMessage()
68
+ }
69
+ state.streamingText += text
70
+ const body = state.streamingMsgEl.querySelector('.message-body')
71
+ body.innerHTML = renderMarkdown(state.streamingText)
72
+ body.classList.add('streaming')
73
+ scrollToBottom()
74
+ }
75
+
76
+ export function finalizeAssistantMessage() {
77
+ if (!state.streamingMsgEl) return
78
+ const body = state.streamingMsgEl.querySelector('.message-body')
79
+ body.innerHTML = renderMarkdown(state.streamingText)
80
+ body.classList.remove('streaming')
81
+ state.streamingMsgEl = null
82
+ state.streamingText = ''
83
+ scrollToBottom()
84
+ }
85
+
86
+ export function addToolCall(name, input, id) {
87
+ const container = state.streamingMsgEl || messagesEl.querySelector('.message:last-child')
88
+ const div = document.createElement('div')
89
+ div.className = id ? 'tool-call executing' : 'tool-call'
90
+ div.dataset.toolId = id || ''
91
+
92
+ if (name === 'edit') {
93
+ let paths = []
94
+ if (Array.isArray(input?.edits)) {
95
+ paths = input.edits.map(e => e.file_path).filter(Boolean)
96
+ } else if (input?.content) {
97
+ const content = input.content
98
+ // Parse Markdown edit blocks to extract file paths
99
+ // Use RegExp constructor to avoid backtick issues
100
+ const bt = '\x60'
101
+ const blockRegex = new RegExp(bt + bt + bt + 'edit\\s*([\\s\\S]*?)' + bt + bt + bt, 'gi')
102
+ for (const blockMatch of content.matchAll(blockRegex)) {
103
+ const blockContent = blockMatch[1] || ''
104
+ const fileMatch = blockContent.match(/^file:\s*(.+)$/m)
105
+ if (fileMatch) paths.push(fileMatch[1].trim())
106
+ // Detect create-file blocks (empty/missing old:)
107
+ const oldMatch = blockContent.match(/^old:([\s\S]*?)^new:/m)
108
+ if (!oldMatch || !oldMatch[1].trim()) {
109
+ hasEmptyOld = true
110
+ }
111
+ }
112
+ // Fallback: no edit markers -- try raw file: lines
113
+ if (paths.length === 0) {
114
+ const fileMatch = content.match(/^file:\s*(.+)$/gm)
115
+ if (fileMatch) paths = fileMatch.map(m => m.replace(/^file:\s*/, '').trim())
116
+ }
117
+ }
118
+ const uniquePaths = [...new Set(paths)]
119
+ const pathsStr =
120
+ uniquePaths.length > 0 ? uniquePaths.map(p => escapeHtml(p)).join(', ') : '(no files)'
121
+
122
+ // Also check JSON-format edits for create-file detection
123
+ if (!hasEmptyOld && Array.isArray(input?.edits)) {
124
+ hasEmptyOld = input.edits.some(e => e.old_string === '')
125
+ }
126
+ const badge = hasEmptyOld ? 'create' : 'edit'
127
+ div.innerHTML = `<span class="tool-name tool-name-edit">✏ edit</span><span class="tool-change-badge tool-change-${badge}">${badge}</span> <span class="tool-input tool-input-edit">${pathsStr}</span>`
128
+ } else {
129
+ const inputStr = typeof input === 'object' ? JSON.stringify(input).slice(0, 120) : String(input)
130
+ div.innerHTML = `<span class="tool-name">${escapeHtml(name)}</span> <span class="tool-input">${escapeHtml(inputStr)}</span>`
131
+ }
132
+
133
+ if (container && container.matches('.message')) {
134
+ container.appendChild(div)
135
+ } else {
136
+ messagesEl.appendChild(div)
137
+ }
138
+ if (id) pendingToolCalls.set(id, div)
139
+ scrollToBottom()
140
+ }
141
+
142
+ const DIFF_RED = '#ff5050'
143
+ const DIFF_GREEN = '#00c864'
144
+ const DIFF_DIM = '#888888'
145
+
146
+ const ANSI_RED = '\x1b[38;2;255;80;80m'
147
+ const ANSI_GREEN = '\x1b[38;2;0;200;100m'
148
+ const ANSI_DIM = '\x1b[38;2;100;100;100m'
149
+ const ANSI_PATTERN = /\x1b\[[\d;]+m/g
150
+
151
+ function renderDiffContent(text) {
152
+ const hasAnsi = text.includes(ANSI_RED) || text.includes(ANSI_GREEN) || text.includes(ANSI_DIM)
153
+
154
+ if (hasAnsi) {
155
+ return text
156
+ .split('\n')
157
+ .map(line => {
158
+ if (!line.trim()) return `${escapeHtml(line)}\n`
159
+ const color = line.includes(ANSI_RED)
160
+ ? DIFF_RED
161
+ : line.includes(ANSI_GREEN)
162
+ ? DIFF_GREEN
163
+ : line.includes(ANSI_DIM)
164
+ ? DIFF_DIM
165
+ : null
166
+ const clean = line.replace(ANSI_PATTERN, '')
167
+ if (!color) return `${escapeHtml(clean)}\n`
168
+ return `<span style="color:${color}">${escapeHtml(clean)}\n</span>`
169
+ })
170
+ .join('')
171
+ }
172
+
173
+ // Fallback: detect unified-diff markers (- / +) without ANSI codes
174
+ const hasDiffMarkers = text.split('\n').some(l => l.trim().match(/^[-+]\s/))
175
+ if (hasDiffMarkers) {
176
+ return text
177
+ .split('\n')
178
+ .map(line => {
179
+ const trimmed = line.trim()
180
+ if (trimmed.startsWith('- ')) {
181
+ return `<span style="color:${DIFF_RED}">${escapeHtml(line)}\n</span>`
182
+ }
183
+ if (trimmed.startsWith('+ ')) {
184
+ return `<span style="color:${DIFF_GREEN}">${escapeHtml(line)}\n</span>`
185
+ }
186
+ if (trimmed.match(/^[-+]\s/)) {
187
+ return `<span style="color:${DIFF_DIM}">${escapeHtml(line)}\n</span>`
188
+ }
189
+ return `${escapeHtml(line)}\n`
190
+ })
191
+ .join('')
192
+ }
193
+
194
+ return escapeHtml(text)
195
+ }
196
+
197
+ export function addToolResult(name, success, outputOrError, id) {
198
+ if (id) {
199
+ const pendingEl = pendingToolCalls.get(id)
200
+ if (pendingEl) {
201
+ pendingEl.classList.remove('executing')
202
+ pendingToolCalls.delete(id)
203
+ if (name === 'edit') {
204
+ if (success) {
205
+ pendingEl.classList.add('completed')
206
+ const fileSummary = (outputOrError || '')
207
+ .split('\n')
208
+ .map(l => l.trim())
209
+ .filter(l => /^(Edited|Created|Deleted) /.test(l))
210
+ .join(', ')
211
+ if (fileSummary) {
212
+ const inputSpan = pendingEl.querySelector('.tool-input-edit')
213
+ if (inputSpan) {
214
+ inputSpan.textContent = fileSummary.replace(/:\s*$/, '')
215
+ }
216
+ }
217
+ } else {
218
+ pendingEl.classList.add('failed')
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ const container = state.streamingMsgEl || messagesEl.querySelector('.message:last-child')
225
+ const div = document.createElement('div')
226
+ div.className = 'tool-result'
227
+ if (success) {
228
+ let display = outputOrError
229
+ if (display === '(no output)') display = ''
230
+ if (name === 'edit' && display) {
231
+ const lines = display.split('\n')
232
+ const edits = [] // { summary, diffLines[] }
233
+ let currentEdit = null
234
+ for (const line of lines) {
235
+ const trimmed = line.trim()
236
+ const stripped = trimmed.replace(ANSI_PATTERN, '')
237
+ if (
238
+ trimmed.startsWith('Edited ') ||
239
+ trimmed.startsWith('Created ') ||
240
+ trimmed.startsWith('Deleted ')
241
+ ) {
242
+ currentEdit = { summary: trimmed, diffLines: [] }
243
+ edits.push(currentEdit)
244
+ } else if (
245
+ currentEdit &&
246
+ (stripped.startsWith('- ') || stripped.startsWith('+ ') || stripped.match(/^\s*\d+ /))
247
+ ) {
248
+ currentEdit.diffLines.push(line)
249
+ }
250
+ }
251
+ let html = `<span class="tool-result-success">✔ ${escapeHtml(name)}</span>`
252
+ if (edits.length === 0 && display.includes(' FAIL ')) {
253
+ html += `<span class="tool-result-diff"><pre>${escapeHtml(display)}</pre></span>`
254
+ } else {
255
+ for (const edit of edits) {
256
+ html += `<span class="tool-result-summary">${escapeHtml(edit.summary)}</span>`
257
+ if (edit.diffLines.length > 0) {
258
+ html += `<span class="tool-result-diff"><pre><code>${renderDiffContent(edit.diffLines.join('\n'))}</code></pre></span>`
259
+ }
260
+ }
261
+ }
262
+ div.innerHTML = html
263
+ } else {
264
+ const summary = typeof display === 'string' ? display.slice(0, 80) : ''
265
+ div.innerHTML = `<span class="tool-result-success">✔ ${escapeHtml(name)}</span>${summary ? ` ${escapeHtml(summary)}` : ''}`
266
+ }
267
+ } else {
268
+ if (name === 'edit') {
269
+ const filePathPattern = /([\w/.-]+\.[a-z]+)/gi
270
+ const filePaths = [...new Set((outputOrError.match(filePathPattern) || []).slice(0, 5))]
271
+ const summary =
272
+ filePaths.length > 0
273
+ ? `<span class="tool-error-files">${filePaths.map(p => escapeHtml(p)).join(', ')}</span>`
274
+ : ''
275
+ div.innerHTML =
276
+ `<span class="tool-result-error">✖ ${escapeHtml(name)}</span>` +
277
+ summary +
278
+ `<span class="tool-error-detail">${escapeHtml(outputOrError)}</span>`
279
+ } else {
280
+ div.innerHTML = `<span class="tool-result-error">✖ ${escapeHtml(name)}</span> <span class="tool-error-detail">${escapeHtml(outputOrError)}</span>`
281
+ }
282
+ }
283
+ if (container && container.matches('.message')) {
284
+ container.appendChild(div)
285
+ } else {
286
+ messagesEl.appendChild(div)
287
+ }
288
+ scrollToBottom()
289
+ }
290
+
291
+ export function showThinking(text) {
292
+ if (!state.thinkingEl) {
293
+ state.thinkingEl = document.createElement('div')
294
+ state.thinkingEl.className = 'thinking-block'
295
+ state.thinkingEl.innerHTML =
296
+ '<div class="thinking-label">🤔 Think</div><div class="thinking-content"></div>'
297
+ if (state.streamingMsgEl) {
298
+ const body = state.streamingMsgEl.querySelector('.message-body')
299
+ state.streamingMsgEl.insertBefore(state.thinkingEl, body)
300
+ } else {
301
+ messagesEl.appendChild(state.thinkingEl)
302
+ }
303
+ state.thinkingText = ''
304
+ }
305
+ state.thinkingText += text
306
+ const content = state.thinkingEl.querySelector('.thinking-content')
307
+ content.innerHTML = renderMarkdown(state.thinkingText)
308
+ scrollToBottom()
309
+ }
310
+
311
+ export function hideThinking() {
312
+ if (state.thinkingEl) {
313
+ state.thinkingEl.classList.add('thinking-done')
314
+ state.thinkingEl = null
315
+ state.thinkingText = ''
316
+ }
317
+ }
318
+
319
+ export function renderSessionHistory(messages) {
320
+ if (!Array.isArray(messages)) return
321
+ let rendered = 0
322
+ let errors = 0
323
+ for (const msg of messages) {
324
+ try {
325
+ if (msg.role === 'user') {
326
+ addUserMessage(msg.content || '')
327
+ } else if (msg.role === 'assistant') {
328
+ startAssistantMessage()
329
+ if (msg.reasoning_content) {
330
+ showThinking(msg.reasoning_content)
331
+ hideThinking()
332
+ }
333
+ if (msg.content) {
334
+ state.streamingText = msg.content
335
+ const body = state.streamingMsgEl.querySelector('.message-body')
336
+ body.innerHTML = renderMarkdown(msg.content)
337
+ }
338
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
339
+ for (const tc of msg.tool_calls) {
340
+ addToolCall(tc.name, tc.input)
341
+ }
342
+ }
343
+ finalizeAssistantMessage()
344
+ } else if (msg.role === 'tool') {
345
+ const content = msg.content || ''
346
+ const isError = content.startsWith('ERROR: ')
347
+ if (isError) {
348
+ addToolResult(msg.name || '', false, content.replace(/^ERROR:\s*/, ''))
349
+ } else {
350
+ addToolResult(msg.name || '', true, content)
351
+ }
352
+ }
353
+ rendered++
354
+ } catch (err) {
355
+ errors++
356
+ console.error(
357
+ '[renderSessionHistory] Error rendering message #' + rendered + ' role=' + msg.role + ':',
358
+ err,
359
+ )
360
+ }
361
+ }
362
+ if (errors > 0) {
363
+ console.warn(
364
+ '[renderSessionHistory] Rendered ' + rendered + ' messages with ' + errors + ' errors',
365
+ )
366
+ }
367
+ }
368
+
369
+ export function addTokenStats(
370
+ turnIn,
371
+ turnOut,
372
+ totalIn,
373
+ totalOut,
374
+ turnApi,
375
+ totalApi,
376
+ totalCacheHit,
377
+ totalCacheMiss,
378
+ ) {
379
+ const div = document.createElement('div')
380
+ const cacheHit = totalCacheHit ?? 0
381
+ const cacheMiss = totalCacheMiss ?? 0
382
+ const cacheTotal = cacheHit + cacheMiss
383
+ let statsText = `▴${turnIn} ▾${turnOut} total ${totalIn + totalOut} calls ${turnApi}(${totalApi})`
384
+ if (cacheTotal > 0) {
385
+ const pct = Math.round((cacheHit / cacheTotal) * 100)
386
+ statsText += ` cached ${pct}%`
387
+ }
388
+ div.className = 'token-stats-bar'
389
+ div.textContent = statsText
390
+ messagesEl.appendChild(div)
391
+ scrollToBottom()
392
+
393
+ tokenIn.textContent = formatTokenCount(totalIn)
394
+ tokenOut.textContent = formatTokenCount(totalOut)
395
+ tokenCalls.textContent = `(${totalApi})`
396
+ if (cacheTotal > 0) {
397
+ const pct = Math.round((cacheHit / cacheTotal) * 100)
398
+ tokenCache.textContent = `| 缓存 ${pct}%`
399
+ tokenCache.classList.remove('hidden')
400
+ } else {
401
+ tokenCache.classList.add('hidden')
402
+ }
403
+ }
404
+
405
+ export function addErrorMessage(text) {
406
+ const div = document.createElement('div')
407
+ div.className = 'message'
408
+ div.innerHTML = `<div class="message-body error">${escapeHtml(text)}</div>`
409
+ messagesEl.appendChild(div)
410
+ scrollToBottom()
411
+ }
@@ -0,0 +1,82 @@
1
+ /* ── Sidebar (Plans / Todos) ── */
2
+
3
+ import {
4
+ plansList,
5
+ plansPane,
6
+ plansPlaceholder,
7
+ sidebarTabs,
8
+ state,
9
+ todosList,
10
+ todosPane,
11
+ todosPlaceholder,
12
+ } from './state.js'
13
+ import { escapeHtml } from './utils.js'
14
+ import { sendWsMsg } from './ws.js'
15
+
16
+ export function switchTab(tabId) {
17
+ sidebarTabs.forEach(tab => {
18
+ tab.classList.toggle('active', tab.dataset.tab === tabId)
19
+ })
20
+ plansPane.classList.toggle('active', tabId === 'plans')
21
+ todosPane.classList.toggle('active', tabId === 'todos')
22
+ }
23
+
24
+ export function updatePlans() {
25
+ plansList.innerHTML = ''
26
+ if (state.plans.length === 0) {
27
+ plansPlaceholder.style.display = ''
28
+ return
29
+ }
30
+ plansPlaceholder.style.display = 'none'
31
+ for (const plan of state.plans) {
32
+ const div = document.createElement('div')
33
+ div.className = `plan-item${plan.name === state.currentPlanName ? ' active' : ''}`
34
+ const timeStr = plan.mtime
35
+ ? new Date(plan.mtime).toLocaleDateString() +
36
+ ' ' +
37
+ new Date(plan.mtime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
38
+ : ''
39
+ div.dataset.planName = plan.name
40
+ div.innerHTML = `<div class="plan-name">${escapeHtml(plan.name)}</div><div class="plan-time">${escapeHtml(timeStr)}</div>`
41
+ div.addEventListener('click', () => onPlanClick(plan.name))
42
+ plansList.appendChild(div)
43
+ }
44
+ }
45
+
46
+ export function updateTodos() {
47
+ todosList.innerHTML = ''
48
+ if (!state.currentPlanName) {
49
+ todosPlaceholder.textContent = '(no plan selected)'
50
+ todosPlaceholder.style.display = ''
51
+ return
52
+ }
53
+ if (state.todos.length === 0) {
54
+ todosPlaceholder.textContent = '(no todos)'
55
+ todosPlaceholder.style.display = ''
56
+ return
57
+ }
58
+ todosPlaceholder.style.display = 'none'
59
+ const header = document.createElement('div')
60
+ header.className = 'todo-header-text'
61
+ header.textContent = state.currentPlanName
62
+ todosList.appendChild(header)
63
+ for (const todo of state.todos) {
64
+ const div = document.createElement('div')
65
+ div.className = 'todo-item'
66
+ const done = todo.done
67
+ div.innerHTML =
68
+ `<span class="todo-check ${done ? 'done' : 'pending'}">${done ? '✅' : '⬜'}</span>` +
69
+ `<span class="todo-text ${done ? 'done' : ''}">${escapeHtml(todo.text)}</span>`
70
+ todosList.appendChild(div)
71
+ }
72
+ }
73
+
74
+ export function onPlanClick(planName) {
75
+ sendWsMsg({ type: 'load_plan', planName })
76
+ switchTab('todos')
77
+ }
78
+
79
+ export function updatePlansAndTodos() {
80
+ updatePlans()
81
+ updateTodos()
82
+ }
@@ -0,0 +1,72 @@
1
+ /* ── DOM References & Shared State ── */
2
+
3
+ // ── DOM References ──
4
+ export const messagesEl = document.getElementById('messages')
5
+ export const chatInput = document.getElementById('chat-input')
6
+ export const sendBtn = document.getElementById('send-btn')
7
+ export const stopBtn = document.getElementById('stop-btn')
8
+ export const statusIndicator = document.getElementById('status-indicator')
9
+ export const modeDisplay = document.getElementById('mode-display')
10
+ export const modelDisplay = document.getElementById('model-display')
11
+ export const tokenIn = document.getElementById('token-in')
12
+ export const tokenOut = document.getElementById('token-out')
13
+ export const tokenCalls = document.getElementById('token-calls')
14
+ export const tokenCache = document.getElementById('token-cache')
15
+ export const balanceDisplay = document.getElementById('balance-display')
16
+ export const balanceSep = document.getElementById('balance-sep')
17
+ export const cwdDisplay = document.getElementById('cwd-display')
18
+ export const connectionOverlay = document.getElementById('connection-overlay')
19
+ export const chatContainer = document.getElementById('chat-container')
20
+ export const sidebar = document.getElementById('sidebar')
21
+ export const sidebarTabs = document.querySelectorAll('.sidebar-tab')
22
+ export const plansPane = document.getElementById('plans-pane')
23
+ export const todosPane = document.getElementById('todos-pane')
24
+ export const plansList = document.getElementById('plans-list')
25
+ export const todosList = document.getElementById('todos-list')
26
+ export const plansPlaceholder = document.getElementById('plans-placeholder')
27
+ export const todosPlaceholder = document.getElementById('todos-placeholder')
28
+ export const slashHint = document.getElementById('slash-hint')
29
+
30
+ // ── Mutable State ──
31
+ export const state = {
32
+ isRunning: false,
33
+ currentMode: 'code',
34
+ currentModel: '',
35
+ currentProvider: '',
36
+ streamingMsgEl: null,
37
+ streamingText: '',
38
+ thinkingEl: null,
39
+ thinkingText: '',
40
+ reconnectAttempts: 0,
41
+ plans: [],
42
+ currentPlanName: '',
43
+ todos: [],
44
+ pendingConfirmResolve: null,
45
+ }
46
+
47
+ export const pendingToolCalls = new Map()
48
+
49
+ export const MAX_RECONNECT_ATTEMPTS = 10
50
+ export const RECONNECT_DELAY = 2000
51
+
52
+ // ── Status Helpers ──
53
+
54
+ export function setInputEnabled(enabled) {
55
+ chatInput.disabled = !enabled
56
+ sendBtn.disabled = !enabled
57
+ sendBtn.classList.toggle('hidden', !enabled)
58
+ stopBtn.classList.toggle('hidden', enabled)
59
+ if (!enabled) {
60
+ stopBtn.disabled = false
61
+ }
62
+ if (enabled) {
63
+ chatInput.focus()
64
+ }
65
+ }
66
+
67
+ export function setStatus(running) {
68
+ state.isRunning = running
69
+ statusIndicator.textContent = running ? '● running' : '○ idle'
70
+ statusIndicator.className = running ? 'status-running' : 'status-idle'
71
+ setInputEnabled(!running)
72
+ }