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
@@ -186,12 +186,19 @@ function buildSummary(messages: LLMMessage[]): string {
186
186
 
187
187
  function extractEditPaths(input: Record<string, unknown>): string[] {
188
188
  const paths: string[] = []
189
+ // Accumulate paths from legacy input format: { edits: [{ file_path: '...' }, ...] }
189
190
  if (Array.isArray(input.edits)) {
190
191
  for (const e of input.edits) {
191
192
  if (typeof e === 'object' && e && 'file_path' in e) {
192
- paths.push(e.file_path as string)
193
+ paths.push((e as Record<string, unknown>).file_path as string)
193
194
  }
194
195
  }
195
196
  }
196
- return paths
197
+ // Also support direct input format used by tests: { file_path: '...' }
198
+ const direct = input as Record<string, unknown>
199
+ if (typeof direct.file_path === 'string') {
200
+ paths.push(direct.file_path)
201
+ }
202
+ // Deduplicate to avoid reporting duplicates
203
+ return Array.from(new Set(paths))
197
204
  }
@@ -2,7 +2,11 @@ import * as readline from 'node:readline'
2
2
  import type { Config } from '../config/index.js'
3
3
  import { Session, type SessionOutput } from './session.js'
4
4
 
5
- export async function runAgent(prompt: string, config: Config): Promise<void> {
5
+ export async function runAgent(
6
+ prompt: string,
7
+ config: Config,
8
+ preloadedSession?: Session,
9
+ ): Promise<void> {
6
10
  const output: SessionOutput = {
7
11
  write: text => process.stdout.write(text),
8
12
  confirmTool: async toolCalls => {
@@ -20,6 +24,7 @@ export async function runAgent(prompt: string, config: Config): Promise<void> {
20
24
  })
21
25
  },
22
26
  }
23
- const session = (await Session.load(config, output)) || new Session(config, output)
27
+ const session =
28
+ preloadedSession ?? (await Session.load(config, output)) ?? new Session(config, output)
24
29
  await session.chat(prompt)
25
30
  }
package/src/agent/llm.ts CHANGED
@@ -20,6 +20,10 @@ export interface LLMProvider {
20
20
  export interface TokenUsage {
21
21
  input_tokens: number
22
22
  output_tokens: number
23
+ /** DeepSeek-specific: tokens read from cache */
24
+ prompt_cache_hit_tokens?: number
25
+ /** DeepSeek-specific: tokens not in cache */
26
+ prompt_cache_miss_tokens?: number
23
27
  }
24
28
 
25
29
  export interface LLMChunk {
@@ -0,0 +1,60 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+
4
+ export interface MemoryEntry {
5
+ id: string
6
+ createdAt: string
7
+ content: string
8
+ tags?: string[]
9
+ }
10
+
11
+ const MEM_DIR = '.lonny/memory'
12
+
13
+ export function getMemoryDir(cwd: string): string {
14
+ return path.resolve(cwd, MEM_DIR)
15
+ }
16
+
17
+ export function ensureMemoryDir(cwd: string): void {
18
+ const d = getMemoryDir(cwd)
19
+ if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true })
20
+ }
21
+
22
+ export function loadMemory(cwd: string): MemoryEntry[] {
23
+ const d = getMemoryDir(cwd)
24
+ try {
25
+ if (!fs.existsSync(d)) return []
26
+ const files = fs.readdirSync(d).filter(f => f.endsWith('.json'))
27
+ const out: MemoryEntry[] = []
28
+ for (const f of files) {
29
+ try {
30
+ const data = JSON.parse(fs.readFileSync(path.join(d, f), 'utf-8'))
31
+ out.push(data as MemoryEntry)
32
+ } catch (e) {
33
+ // ignore corrupted file
34
+ }
35
+ }
36
+ // sort by createdAt
37
+ out.sort((a, b) => a.createdAt.localeCompare(b.createdAt))
38
+ return out
39
+ } catch (e) {
40
+ return []
41
+ }
42
+ }
43
+
44
+ export function saveMemory(cwd: string, entry: MemoryEntry): void {
45
+ ensureMemoryDir(cwd)
46
+ const id = entry.id || String(Date.now())
47
+ const file = path.join(getMemoryDir(cwd), `${id}.json`)
48
+ fs.writeFileSync(file, JSON.stringify(entry, null, 2), 'utf-8')
49
+ }
50
+
51
+ export function formatMemoryForPrompt(memories: MemoryEntry[]): string {
52
+ if (!memories || memories.length === 0) return ''
53
+ const parts: string[] = []
54
+ for (const m of memories) {
55
+ parts.push(
56
+ `- [${m.createdAt}] ${m.content.slice(0, 400).replace(/\n/g, ' ')}${m.content.length > 400 ? '…' : ''}`,
57
+ )
58
+ }
59
+ return parts.join('\n')
60
+ }
@@ -1,13 +1,26 @@
1
1
  import * as os from 'node:os'
2
2
  import type { Config } from '../config/index.js'
3
+ import { formatToolTreeForPrompt } from '../tools/tree.js'
4
+ import type { ToolDefinition } from '../tools/types.js'
5
+
6
+ const CORE_TOOL_NAMES = new Set(['read', 'edit', 'bash', 'glob', 'grep'])
7
+
8
+ import { formatMemoryForPrompt, loadMemory } from './memory.js'
3
9
  import { discoverProject, formatProjectContext } from './project.js'
4
10
  import { formatSkillsForPrompt, loadSkills } from './skills.js'
5
11
 
6
12
  /**
7
13
  * Build the system prompt for the current configuration.
8
14
  * Extracted from session.ts to keep module size manageable (<500 LoC target).
15
+ *
16
+ * @param config - Current configuration
17
+ * @param definitions - Optional tool definitions for dynamic tree generation.
18
+ * When provided, replaces the hardcoded tool lists with a hierarchical tree.
9
19
  */
10
- export async function buildSystemPrompt(config: Config): Promise<string> {
20
+ export async function buildSystemPrompt(
21
+ config: Config,
22
+ definitions?: ToolDefinition[],
23
+ ): Promise<string> {
11
24
  const platform = os.platform()
12
25
  const release = os.release()
13
26
  const shell = process.env.SHELL || process.env.ComSpec || 'unknown'
@@ -19,12 +32,33 @@ export async function buildSystemPrompt(config: Config): Promise<string> {
19
32
  const skills = loadSkills(cwd)
20
33
  const skillsSection = formatSkillsForPrompt(skills)
21
34
 
35
+ // ── Load long-term memory (persistent) ─────────────────────────────────
36
+ const memories = loadMemory(cwd)
37
+ const memorySection = formatMemoryForPrompt(memories)
38
+
22
39
  // ── Load project context ─────────────────────────────────────────────────
23
40
  const projectInfo = await discoverProject(cwd)
24
41
  const projectSection = formatProjectContext(projectInfo)
25
42
 
26
43
  // ── Mode-specific tool list ───────────────────────────────────────────
27
44
  function getToolListForMode(mode: string): string {
45
+ // When tool definitions are available, use the dynamic tree
46
+ if (definitions && definitions.length > 0) {
47
+ const header =
48
+ mode === 'plan'
49
+ ? 'Available tools (read-only investigation + write_plan):'
50
+ : mode === 'ask'
51
+ ? 'Available tools:'
52
+ : 'Available tools:'
53
+ const tree = formatToolTreeForPrompt(definitions, CORE_TOOL_NAMES)
54
+ const note =
55
+ mode !== 'ask'
56
+ ? '\n Direct access: read, edit, bash, glob, grep\n Extended tools: use `tool()` gateway (see tree above)\n'
57
+ : ''
58
+ return `${header}\n${tree}${note}\n`
59
+ }
60
+
61
+ // Fallback hardcoded lists (when definitions not available)
28
62
  if (mode === 'ask') {
29
63
  return `Available tools:
30
64
  - \`fetch\`: Fetch content from a URL
@@ -49,13 +83,13 @@ export async function buildSystemPrompt(config: Config): Promise<string> {
49
83
  - \`glob\`: Find files by glob pattern (pattern: string)
50
84
  - \`grep\`: Search file content by regex (pattern: string, include?: string, path?: string)
51
85
  - \`ls\`: List directory (path?: string)
52
- - \`bash\`: Execute a shell command
53
- - \`edit\`: Replace text in files call with {edits: [{file_path: string, old_string: string, new_string: string}]} (array required)
54
- - \`install_skill\`: Install an npm package as a skill — fetches package info from npm, runs npm install, and creates a .lonny/skills/ file with usage instructions for the AI
55
- - \`find\`: Find files by name pattern (pattern: string, path?: string, maxResults?: number)
56
- - \`git\`: Run read-only git commands (command: string)
57
- - \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
58
- - \`exec\`: Run JavaScript in a sandbox to orchestrate multiple tool calls all tools are available as \`await tools.xxx(args)\` inside exec (code mode only)
86
+ - \`bash\`: Execute a shell command — for running commands (NOT for creating or modifying files — use \`edit\` for that)
87
+ - \`edit\`: Replace text in files using markdown code block format. Use: \`edit({ content: "\`\`\`edit\\nfile: path\\nold: |\\ntext\\nnew: |\\ntext\\n\`\`\`" })\`. NOTE: There is no "write" tool — always use \`edit\` to modify files.
88
+ - \`install_skill\`: Install an npm package as a skill — fetches package info from npm, runs npm install, and creates a .lonny/skills/ file with usage instructions for the AI
89
+ - \`save_memory\`: Save a memory entry to long-term memory (content: string, tags?: string[])
90
+ - \`find\`: Find files by name pattern (pattern: string, path?: string, maxResults?: number)
91
+ - \`git\`: Run read-only git commands (command: string)
92
+ - \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
59
93
  `
60
94
  }
61
95
 
@@ -64,17 +98,51 @@ export async function buildSystemPrompt(config: Config): Promise<string> {
64
98
  RULES:
65
99
  1. Read first: Use read/grep/glob tools to gather all context you need before making any edits.
66
100
  2. Be thorough: Explore the relevant parts of the codebase.
67
- 3. COST OPTIMIZATION (CRITICAL): Each API call costs money. You MUST maximize work per call. Use \`read(paths: [...])\` to read multiple files at once. Use \`edit({edits: [...]})\` to edit multiple files at once. Do NOT do sequential single-file reads or single-edit calls.
68
- 4. When using \`edit\`, the \`edits\` value MUST be an arrayeven for a single change. Do NOT pass empty objects.
101
+ 3. AST tools (\`ast_query\`, \`ast_edit\`) are available via the \`tool()\` gateway. Use \`ast_query\` to inspect code structure (functions, classes, imports) before editing. Use \`ast_edit\` for structure-aware edits that preserve formatting.
102
+ 4. **For JavaScript/TypeScript files, prefer AST tools over raw text tools**: Use \`ast_query\` (not \`read\`) to understand file structure it returns structured function/class/import/export data with exact line numbers. Use \`ast_edit\` (not \`edit\`) to replace entire functions, classes, or variables it avoids string-matching issues and preserves formatting. Reserve \`edit\` for small surgical changes to function bodies or single-line fixes.
103
+ 5. COST OPTIMIZATION (CRITICAL): Each API call costs money. You MUST maximize work per call. Use \`read(paths: [...])\` to read multiple files at once. Use \`edit({ content: "..." })\` with multiple \`\`\`edit blocks to edit multiple files at once.
104
+ 6. There is NO "write" tool. To modify files, use the \`edit\` tool (listed above). Calling \`write\` will fail with "Unknown tool".
105
+
106
+ ${getToolListForMode(config.mode)}
107
+ `
108
+ // ── Memory section (appended to system prompt to provide long-term context) ──
109
+ const memoryPromptSection = memorySection ? `\n## Long-term Memory\n\n${memorySection}` : ''
69
110
 
70
- ${getToolListForMode(config.mode)}`
111
+ // NOTE: Mention save_memory in loop/code mode tool lists only if the tool exists at runtime.
112
+ // The tool may be provided via plugin or future implementation.
71
113
 
72
114
  // ── Mode-specific instructions ───────────────────────────────────────────
73
115
  const modeInstructions =
74
- config.mode === 'plan'
75
- ? `You are a planning agent. Your ONLY job is to investigate the codebase and produce an actionable implementation plan with a todo list.
116
+ config.mode === 'loop'
117
+ ? `You are an autonomous coding agent operating in LOOP mode. You will CONTINUE working on the task automatically after each turn you do NOT need to ask for confirmation between steps.
76
118
 
77
- You CANNOT edit files. You do NOT have access to edit, bash (write mode), exec, or install_skill.
119
+ ${getToolListForMode('loop')}
120
+ RULES (loop-specific):
121
+ 1. Read first: Use read/grep/glob tools to gather all context you need BEFORE making any edits. **For JS/TS files, prefer \`ast_query\` over \`read\`** — it returns structured function/class/import/export data with exact line numbers. Use \`tool({ name: "ast_query", params: { path: "file.ts", query: "structure" }})\` to inspect code structure via AST before editing. **For edits to whole functions/classes/variables in JS/TS, prefer \`ast_edit\` over \`edit\`** — use \`tool({ name: "ast_edit", params: { path: "file.ts", editType: "replace-node", targetLine: 5, newCode: "..." }})\` to avoid string-matching issues. Reserve \`edit\` for small surgical changes inside function bodies or single-line fixes. The \`read\` output prefixes each line with "<lineNumber>: " for easy reference. Do NOT include the "N: " prefix when copying text into \`edit\`.
122
+ 2. edit CALL FORMAT — use markdown code block format:
123
+ \`\`\`edit
124
+ file: src/file.ts
125
+ old: |
126
+ text to replace
127
+ new: |
128
+ replacement text
129
+ \`\`\`
130
+ Use separate \`\`\`edit blocks for multiple files.
131
+ 3. After making edits to a file, if you need to make ANOTHER edit to the SAME file, you MUST re-read it first to get the updated content.
132
+ 4. If \`edit\` reports \`old_string not found\`, do NOT retry with the same old_string — re-read the file immediately to see its actual current content, then retry with correctly-copied text.
133
+ 5. When copying old_string from \`read\` output, include 2-3 lines of context BEFORE and AFTER the target change to make the string unique in the file.
134
+ 6. On Windows, files may use CRLF (\\r\\n) line endings, but the \`edit\` tool normalizes them to LF (\\n). Always use \`\\n\` (not \`\\r\\n\`) in old_string/new_string.
135
+ 7. CRITICAL: old_string must be CONTIGUOUS — do NOT skip any lines between the old_string start and end. If you need to modify non-adjacent sections, use separate \`\`\`edit blocks.
136
+ 8. The \`|\` (pipe) after \`old:\` / \`new:\` supports chomping: \`|\` keeps trailing newline, \`|-\` strips it. Use \`|\` (not \`|-\`) when copying old_string — wrong chomping causes "old_string not found".
137
+ 9. COST OPTIMIZATION (CRITICAL): Each API call costs money. You MUST maximize work per call. Use \`read(paths: [...])\` to read multiple files at once. Use \`edit({ content: "..." })\` with multiple \`\`\`edit blocks to edit multiple files at once.
138
+ 10. TODO LIST MAINTENANCE: After completing a task item, update the corresponding plan file in \`.lonny/\` by checking off the TODO item (change \`- [ ]\` to \`- [x]\`). Use \`read\` to find the plan file, then \`edit\` to update the checkbox.
139
+ 11. LOOP BEHAVIOR: After this turn ends, you will AUTOMATICALLY receive a continuation prompt to continue working on the same task. You do NOT need to stop and wait for the user — keep going until the task is complete.
140
+ 12. If you believe the task is COMPLETE, end your response with a clear summary of what was accomplished. The system will detect this and stop the loop.
141
+ 13. You can use /stop at any time to halt execution.`
142
+ : config.mode === 'plan'
143
+ ? `You are a planning agent. Your ONLY job is to investigate the codebase and produce an actionable implementation plan with a todo list.
144
+
145
+ You CANNOT edit files. You do NOT have access to edit, bash (write mode), or install_skill.
78
146
  Any attempt to call these tools will FAIL — they are simply unavailable in this mode.
79
147
 
80
148
  RULES (plan-specific):
@@ -86,6 +154,7 @@ RULES (plan-specific):
86
154
  6. If the plan is very long (or the todo list has many items), split into multiple files with descriptive names like \`backend-api.md\`, \`frontend-ui.md\`, \`database.md\`, etc.
87
155
  7. You MUST also include the todo list in your text response to the user (not just in the file).
88
156
  7. If the user asks you to modify files, run write commands, or install packages — refuse and explain they need to switch to code mode (\`/mode code\`).
157
+ 8. When the task involves writing code, your plan MUST follow Test-Driven Development: write tests first, then implement. Include a \`- [ ] Write tests for ...\` step BEFORE the implementation steps in the todo list.
89
158
 
90
159
  OUTPUT FORMAT (you MUST include both in write_plan AND in your response text):
91
160
 
@@ -101,35 +170,41 @@ A short, ordered description of the approach. Reference concrete files using \`p
101
170
  End your response by telling the user where the plan was saved and asking whether they want to switch to \`code\` mode to execute it. Use exactly: "Switch to code mode to implement this plan? (run \`/mode code\`)"
102
171
 
103
172
  If the user's request is a question rather than a change request, answer it directly and skip the plan/todo sections.`
104
- : config.mode === 'ask'
105
- ? `You are a Q&A assistant. You can ONLY use the following tools to search for information:
106
- - \`fetch\`: Fetch content from a URL
107
- - \`search\`: Search the web using Tavily
108
-
173
+ : config.mode === 'ask'
174
+ ? `You are a Q&A assistant. You can ONLY use the following tools to search for information:
175
+ ${getToolListForMode('ask')}
109
176
  You CANNOT execute any shell commands (\`bash\`), read local files, or make any changes to the codebase.
110
177
 
111
178
  RULES (ask-specific):
112
179
  1. Use \`fetch\` and \`search\` to find information and answer user questions.
113
180
  2. You CANNOT use \`bash\`, \`read\`, \`edit\`, \`write_plan\`, \`glob\`, \`grep\`, \`ls\`, \`find\`, or \`git\`.
114
181
  3. If the user wants you to modify code or run commands, explain you are in ask mode and suggest switching to code mode.`
115
- : `You are a coding agent optimized for per-call pricing.
182
+ : `You are a coding agent optimized for per-call pricing.
116
183
 
117
184
  RULES (code-specific):
118
185
  1. Read first: Use read/grep/glob tools to gather all context you need BEFORE making any edits. The \`read\` output prefixes each line with "<lineNumber>: " for easy reference. Do NOT include the "N: " prefix when copying text into \`edit\`.
119
- 2. edit CALL FORMAT — you MUST call edit with exactly this JSON shape:
120
- { "edits": [{ "file_path": "src/file.ts", "old_string": "text to replace", "new_string": "replacement text" }] }
121
- The "edits" value is ALWAYS an array, even for a single edit. Do NOT pass file_path/old_string/new_string as top-level keys. Do NOT pass an empty object {}.
186
+ 2. edit CALL FORMAT — use markdown code block format:
187
+ \`\`\`edit
188
+ file: src/file.ts
189
+ old: |
190
+ text to replace
191
+ new: |
192
+ replacement text
193
+ \`\`\`
194
+ Use separate \`\`\`edit blocks for multiple files.
122
195
  3. After making edits to a file, if you need to make ANOTHER edit to the SAME file, you MUST re-read it first to get the updated content.
123
196
  4. If \`edit\` reports \`old_string not found\`, do NOT retry with the same old_string — re-read the file immediately to see its actual current content, then retry with correctly-copied text.
124
- 5. When copying old_string from \`read\` output, include 2-3 lines of context BEFORE and AFTER the target change to make the string unique in the file.
125
- 6. On Windows, files may use CRLF (\\r\\n) line endings, but the \`edit\` tool normalizes them to LF (\\n). Always use \`\\n\` (not \`\\r\\n\`) in old_string/new_string.
126
- 7. COST OPTIMIZATION (CRITICAL): Each API call costs money. You have a hard limit of ~5 API calls per task.
127
- 8. TODO LIST MAINTENANCE: After completing a task item, update the corresponding plan file in \`.lonny/\` by checking off the TODO item (change \`- [ ]\` to \`- [x]\`). Use \`read\` to find the plan file, then \`edit\` to update the checkbox.`
197
+ 5. When copying old_string from \`read\` output, include 2-3 lines of context BEFORE and AFTER the target change to make the string unique in the file.
198
+ 6. CRITICAL: old_string must be CONTIGUOUS do NOT skip any lines between the old_string start and end. If you need to modify non-adjacent sections, use separate \`\`\`edit blocks.
199
+ 7. The \`|\` (pipe) after \`old:\` / \`new:\` supports chomping: \`|\` keeps trailing newline, \`|-\` strips it. Use \`|\` (not \`|-\`) when copying old_string wrong chomping causes "old_string not found".
200
+ 8. On Windows, files may use CRLF (\\r\\n) line endings, but the \`edit\` tool normalizes them to LF (\\n). Always use \`\\n\` (not \`\\r\\n\`) in old_string/new_string.
201
+ 9. COST OPTIMIZATION (CRITICAL): Each API call costs money. You have a hard limit of ~5 API calls per task.
202
+ 10. TODO LIST MAINTENANCE: After completing a task item, update the corresponding plan file in \`.lonny/\` by checking off the TODO item (change \`- [ ]\` to \`- [x]\`). Use \`read\` to find the plan file, then \`edit\` to update the checkbox.`
128
203
 
129
204
  // ── Built-in development methodologies ─────────────────────────────────
130
205
  // Embedded directly from Superpowers — no skill files needed.
131
206
  const methodologySection =
132
- config.mode === 'code'
207
+ config.mode === 'code' || config.mode === 'loop'
133
208
  ? `
134
209
 
135
210
  ## Development Methodology
@@ -165,17 +240,20 @@ Before writing a plan, explore the user's request thoroughly:
165
240
  - Shell: ${shell}
166
241
  - Working directory: ${cwd}
167
242
  - OS: ${isWindows ? 'Windows' : 'Linux/macOS'}
168
- - Available shell commands: ${isWindows ? 'PowerShell (cmd is also available but PowerShell is preferred)' : 'bash'}
169
- ${isWindows ? ' - Use PowerShell. Do NOT use Unix commands like `cat`, `ls`, `grep`, `which`, `chmod`, `mv`, `cp`, `rm`, `touch`, `mkdir`, `uname`, etc.' : ''}
170
- ${isWindows ? ' - Use `type` instead of `cat`, `dir` instead of `ls`, `where` instead of `which`' : ''}`
243
+ - Available shell commands: ${isWindows ? 'PowerShell (cmd is also available but PowerShell is preferred)' : 'bash'}
244
+ ${isWindows ? ' ⚠️ THIS IS WINDOWS. Do NOT use Unix/Linux paths like `/workspace/...` or `/home/...`. The working directory is a Windows path (e.g. `C:\\Users\\...`).' : ''}
245
+ ${isWindows ? ' ⚠️ Do NOT use Unix commands: `find`, `cat`, `ls -la`, `which`, `cp`, `mv`, `rm`, `touch`, `chmod`, `mkdir`, `grep`, `head`, `tail`. They will ALL fail.' : ''}
246
+ ${isWindows ? ' - Use `type` instead of `cat`, `dir` instead of `ls`, `where` instead of `which`, `Select-Object -First N` instead of `head -N`' : ''}
247
+ ${isWindows ? ' - Use `;` (semicolon) instead of `&&` to chain commands' : ''}
248
+ ${isWindows ? ' - ⚠️ `Select-String` exits with code 1 when no match is found (e.g. `Select-String -Pattern "FAIL"` returns code 1 if no line contains FAIL). This is NORMAL — it does NOT mean the command failed. Append `; $LASTEXITCODE = 0` to suppress this.' : ''}`
171
249
 
172
250
  // Plan mode uses its own standalone tool list inside modeInstructions — skip sharedRules
173
251
  const rulesSection =
174
- config.mode === 'plan'
252
+ config.mode === 'plan' || config.mode === 'loop'
175
253
  ? ''
176
254
  : `
177
255
  ${sharedRules}`
178
256
  return `${modeInstructions}
179
257
 
180
- ${envSection}${rulesSection}${methodologySection}${projectSection}${skillsSection}`
258
+ ${envSection}${rulesSection}${methodologySection}${projectSection}${memoryPromptSection}${skillsSection}`
181
259
  }
@@ -130,7 +130,6 @@ export class GoogleProvider implements LLMProvider {
130
130
  if (systemInstruction) {
131
131
  body.system_instruction = { parts: [{ text: systemInstruction }] }
132
132
  }
133
-
134
133
  // Make streaming API call
135
134
  const url = `${this.baseUrl}/models/${this.model}:streamGenerateContent?alt=sse`
136
135
  const response = await fetch(url, {
@@ -18,12 +18,46 @@ interface ExtendedCreateParams {
18
18
  stream_options?: { include_usage: boolean }
19
19
  thinking?: { type: string }
20
20
  reasoning_effort?: string
21
+ reasoning?: { effort: string }
21
22
  enable_cache?: boolean
22
23
  }
23
24
 
25
+ /** Try to parse JSON, and attempt to repair common issues like unescaped quotes */
26
+ function tryParseJSON(input: string): Record<string, unknown> | null {
27
+ try {
28
+ return JSON.parse(input)
29
+ } catch {
30
+ // Try to repair common JSON issues from LLM output
31
+ let repaired = input
32
+
33
+ // Fix unescaped quotes in string values:
34
+ // Pattern: key="unquoted text" -> key="escaped text"
35
+ // This regex finds ="<not properly escaped>" and adds escaping
36
+ // It handles: ="<content with spaces and 中文">
37
+ repaired = repaired.replace(/= "([^"\\]*(?:\\.[^"\\]*)*)"/g, (match, content) => {
38
+ // Escape any unescaped quotes within the content
39
+ const escaped = content.replace(/(?<!\\)"/g, '\\"')
40
+ return `= "${escaped}"`
41
+ })
42
+
43
+ // Also try escaping quotes after @ (for @click="...")
44
+ repaired = repaired.replace(/@(\w+)="([^"\\]*(?:\\.[^"\\]*)*)"/g, (match, attr, content) => {
45
+ const escaped = content.replace(/(?<!\\)"/g, '\\"')
46
+ return `@${attr}="${escaped}"`
47
+ })
48
+
49
+ try {
50
+ return JSON.parse(repaired)
51
+ } catch {
52
+ return null
53
+ }
54
+ }
55
+ }
56
+
24
57
  export class OpenAIProvider implements LLMProvider {
25
58
  private client: OpenAI
26
59
  private model: string
60
+ private baseURL?: string
27
61
  private thinking?: boolean
28
62
  private reasoningEffort?: string
29
63
  private enableCache: boolean
@@ -41,6 +75,7 @@ export class OpenAIProvider implements LLMProvider {
41
75
  ) {
42
76
  this.client = new OpenAI({ apiKey, baseURL })
43
77
  this.model = model || 'gpt-4o'
78
+ this.baseURL = baseURL
44
79
  this.thinking = thinking
45
80
  this.reasoningEffort = reasoningEffort
46
81
  this.enableCache = enableCache ?? false
@@ -119,6 +154,20 @@ export class OpenAIProvider implements LLMProvider {
119
154
  }
120
155
  })
121
156
 
157
+ // 检测当前对接的是否为官方 OpenAI 应用程序接口
158
+ const isOfficialOpenAI = this.baseURL ? /api\.openai\.com/i.test(this.baseURL) : true
159
+
160
+ // Build reasoning params compatible with the target API
161
+ const reasoningParams: Record<string, unknown> = {}
162
+ if (this.thinking) {
163
+ if (isOfficialOpenAI) {
164
+ reasoningParams.thinking = { type: 'enabled' }
165
+ reasoningParams.reasoning_effort = this.reasoningEffort || 'high'
166
+ } else {
167
+ reasoningParams.reasoning = { effort: this.reasoningEffort || 'high' }
168
+ }
169
+ }
170
+
122
171
  const stream: Stream<ChatCompletionChunk> = await (
123
172
  this.client.chat.completions.create as (
124
173
  params: ExtendedCreateParams,
@@ -131,9 +180,7 @@ export class OpenAIProvider implements LLMProvider {
131
180
  tools: openAIFormattedTools.length > 0 ? openAIFormattedTools : undefined,
132
181
  stream: true,
133
182
  stream_options: { include_usage: true },
134
- ...(this.thinking
135
- ? { thinking: { type: 'enabled' }, reasoning_effort: this.reasoningEffort || 'high' }
136
- : {}),
183
+ ...reasoningParams,
137
184
  ...(this.enableCache ? { enable_cache: true } : {}),
138
185
  },
139
186
  signal ? { signal } : undefined,
@@ -146,7 +193,14 @@ export class OpenAIProvider implements LLMProvider {
146
193
  } | null = null
147
194
  let fullText = ''
148
195
  let reasoningContent: string | undefined
149
- let lastUsage: { prompt_tokens?: number; completion_tokens?: number } | undefined
196
+ let lastUsage:
197
+ | {
198
+ prompt_tokens?: number
199
+ completion_tokens?: number
200
+ prompt_cache_hit_tokens?: number
201
+ prompt_cache_miss_tokens?: number
202
+ }
203
+ | undefined
150
204
 
151
205
  // Track a pending "complete" yield — OpenAI sends finish_reason in a content
152
206
  // chunk but sends usage in a *separate final chunk* with no choices/delta.
@@ -159,7 +213,15 @@ export class OpenAIProvider implements LLMProvider {
159
213
  for await (const chunk of stream) {
160
214
  // Capture usage info if present (may come in a chunk without choices)
161
215
  // OpenAI uses prompt_tokens/completion_tokens; map to our input_tokens/output_tokens
162
- const rawChunk = chunk as { usage?: { prompt_tokens?: number; completion_tokens?: number } }
216
+ // DeepSeek also returns prompt_cache_hit_tokens & prompt_cache_miss_tokens
217
+ const rawChunk = chunk as {
218
+ usage?: {
219
+ prompt_tokens?: number
220
+ completion_tokens?: number
221
+ prompt_cache_hit_tokens?: number
222
+ prompt_cache_miss_tokens?: number
223
+ }
224
+ }
163
225
  if (rawChunk.usage) {
164
226
  lastUsage = rawChunk.usage
165
227
  // If we have a pending complete, yield it now with usage
@@ -171,6 +233,8 @@ export class OpenAIProvider implements LLMProvider {
171
233
  usage: {
172
234
  input_tokens: rawChunk.usage.prompt_tokens ?? 0,
173
235
  output_tokens: rawChunk.usage.completion_tokens ?? 0,
236
+ prompt_cache_hit_tokens: rawChunk.usage.prompt_cache_hit_tokens,
237
+ prompt_cache_miss_tokens: rawChunk.usage.prompt_cache_miss_tokens,
174
238
  },
175
239
  }
176
240
  pendingComplete = null
@@ -203,7 +267,7 @@ export class OpenAIProvider implements LLMProvider {
203
267
  let input: Record<string, unknown>
204
268
  const rawArgs = currentToolCall.arguments || ''
205
269
  try {
206
- input = JSON.parse(rawArgs || '{}')
270
+ input = tryParseJSON(rawArgs || '{}') || {}
207
271
  } catch {
208
272
  console.error(
209
273
  '[openai] Failed to parse tool_call arguments (flush on new tool):',
@@ -237,12 +301,13 @@ export class OpenAIProvider implements LLMProvider {
237
301
  if (currentToolCall) {
238
302
  const finalArgs = currentToolCall.arguments || ''
239
303
  try {
304
+ const parsed = tryParseJSON(finalArgs || '{}')
240
305
  yield {
241
306
  type: 'tool_use',
242
307
  tool_call: {
243
308
  id: currentToolCall.id,
244
309
  name: currentToolCall.name,
245
- input: JSON.parse(finalArgs || '{}'),
310
+ input: parsed || {},
246
311
  },
247
312
  reasoning_content: reasoningContent,
248
313
  }
@@ -274,6 +339,8 @@ export class OpenAIProvider implements LLMProvider {
274
339
  usage: {
275
340
  input_tokens: lastUsage.prompt_tokens ?? 0,
276
341
  output_tokens: lastUsage.completion_tokens ?? 0,
342
+ prompt_cache_hit_tokens: lastUsage.prompt_cache_hit_tokens,
343
+ prompt_cache_miss_tokens: lastUsage.prompt_cache_miss_tokens,
277
344
  },
278
345
  }
279
346
  reasoningContent = undefined
@@ -294,6 +361,8 @@ export class OpenAIProvider implements LLMProvider {
294
361
  ? {
295
362
  input_tokens: lastUsage.prompt_tokens ?? 0,
296
363
  output_tokens: lastUsage.completion_tokens ?? 0,
364
+ prompt_cache_hit_tokens: lastUsage.prompt_cache_hit_tokens,
365
+ prompt_cache_miss_tokens: lastUsage.prompt_cache_miss_tokens,
297
366
  }
298
367
  : undefined
299
368
  yield {
@@ -310,12 +379,14 @@ export class OpenAIProvider implements LLMProvider {
310
379
  ? {
311
380
  input_tokens: lastUsage.prompt_tokens ?? 0,
312
381
  output_tokens: lastUsage.completion_tokens ?? 0,
382
+ prompt_cache_hit_tokens: lastUsage.prompt_cache_hit_tokens,
383
+ prompt_cache_miss_tokens: lastUsage.prompt_cache_miss_tokens,
313
384
  }
314
385
  : undefined
315
386
  let input: Record<string, unknown>
316
387
  const rawFinalArgs = currentToolCall.arguments || ''
317
388
  try {
318
- input = JSON.parse(rawFinalArgs || '{}')
389
+ input = tryParseJSON(rawFinalArgs || '{}') || {}
319
390
  } catch {
320
391
  console.error('[openai] Failed to parse tool_call arguments (final flush):', rawFinalArgs)
321
392
  input = {}