lonny-agent 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/.github/workflows/ci.yml +21 -0
  2. package/AGENTS.md +106 -0
  3. package/CHANGELOG.md +24 -0
  4. package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
  5. package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
  6. package/dist/agent/__tests__/session-restore.test.js +260 -0
  7. package/dist/agent/__tests__/session-restore.test.js.map +1 -0
  8. package/dist/agent/commands.d.ts +26 -0
  9. package/dist/agent/commands.d.ts.map +1 -0
  10. package/dist/agent/commands.js +253 -0
  11. package/dist/agent/commands.js.map +1 -0
  12. package/dist/agent/compaction.js +8 -1
  13. package/dist/agent/compaction.js.map +1 -1
  14. package/dist/agent/index.d.ts +2 -1
  15. package/dist/agent/index.d.ts.map +1 -1
  16. package/dist/agent/index.js +2 -2
  17. package/dist/agent/index.js.map +1 -1
  18. package/dist/agent/memory.d.ts +12 -0
  19. package/dist/agent/memory.d.ts.map +1 -0
  20. package/dist/agent/memory.js +51 -0
  21. package/dist/agent/memory.js.map +1 -0
  22. package/dist/agent/prompt-builder.d.ts +6 -1
  23. package/dist/agent/prompt-builder.d.ts.map +1 -1
  24. package/dist/agent/prompt-builder.js +89 -26
  25. package/dist/agent/prompt-builder.js.map +1 -1
  26. package/dist/agent/providers/openai.d.ts +1 -0
  27. package/dist/agent/providers/openai.d.ts.map +1 -1
  28. package/dist/agent/providers/openai.js +16 -3
  29. package/dist/agent/providers/openai.js.map +1 -1
  30. package/dist/agent/session-chat.d.ts +3 -0
  31. package/dist/agent/session-chat.d.ts.map +1 -0
  32. package/dist/agent/session-chat.js +384 -0
  33. package/dist/agent/session-chat.js.map +1 -0
  34. package/dist/agent/session-display.d.ts +29 -0
  35. package/dist/agent/session-display.d.ts.map +1 -0
  36. package/dist/agent/session-display.js +179 -0
  37. package/dist/agent/session-display.js.map +1 -0
  38. package/dist/agent/session-persistence.d.ts +55 -0
  39. package/dist/agent/session-persistence.d.ts.map +1 -0
  40. package/dist/agent/session-persistence.js +101 -0
  41. package/dist/agent/session-persistence.js.map +1 -0
  42. package/dist/agent/session-utils.d.ts +5 -0
  43. package/dist/agent/session-utils.d.ts.map +1 -0
  44. package/dist/agent/session-utils.js +51 -0
  45. package/dist/agent/session-utils.js.map +1 -0
  46. package/dist/agent/session.d.ts +15 -17
  47. package/dist/agent/session.d.ts.map +1 -1
  48. package/dist/agent/session.js +226 -608
  49. package/dist/agent/session.js.map +1 -1
  50. package/dist/cli/index.d.ts +4 -0
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +67 -2
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/config/index.d.ts +2 -2
  55. package/dist/config/index.d.ts.map +1 -1
  56. package/dist/index.js +24 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/tools/__tests__/bash.test.js +168 -1
  59. package/dist/tools/__tests__/bash.test.js.map +1 -1
  60. package/dist/tools/__tests__/edit.test.js +253 -6
  61. package/dist/tools/__tests__/edit.test.js.map +1 -1
  62. package/dist/tools/__tests__/fetch.test.js +1 -1
  63. package/dist/tools/__tests__/fetch.test.js.map +1 -1
  64. package/dist/tools/__tests__/glob.test.js +1 -1
  65. package/dist/tools/__tests__/glob.test.js.map +1 -1
  66. package/dist/tools/__tests__/grep.test.js +1 -1
  67. package/dist/tools/__tests__/grep.test.js.map +1 -1
  68. package/dist/tools/__tests__/ls.test.js +1 -1
  69. package/dist/tools/__tests__/ls.test.js.map +1 -1
  70. package/dist/tools/__tests__/read.test.js +1 -1
  71. package/dist/tools/__tests__/read.test.js.map +1 -1
  72. package/dist/tools/__tests__/registry.test.js +313 -0
  73. package/dist/tools/__tests__/registry.test.js.map +1 -1
  74. package/dist/tools/__tests__/write_plan.test.js +1 -1
  75. package/dist/tools/__tests__/write_plan.test.js.map +1 -1
  76. package/dist/tools/bash.d.ts.map +1 -1
  77. package/dist/tools/bash.js +659 -39
  78. package/dist/tools/bash.js.map +1 -1
  79. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
  80. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
  81. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
  82. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
  83. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
  84. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
  85. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
  86. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
  87. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
  88. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
  89. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
  90. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
  91. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
  92. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
  93. package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
  94. package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
  95. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
  96. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
  97. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
  98. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
  99. package/dist/tools/codebase/ast/adapter.d.ts +23 -0
  100. package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
  101. package/dist/tools/codebase/ast/adapter.js +11 -0
  102. package/dist/tools/codebase/ast/adapter.js.map +1 -0
  103. package/dist/tools/codebase/ast/tools.d.ts +3 -0
  104. package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
  105. package/dist/tools/codebase/ast/tools.js +286 -0
  106. package/dist/tools/codebase/ast/tools.js.map +1 -0
  107. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
  108. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
  109. package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
  110. package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
  111. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
  112. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
  113. package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
  114. package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
  115. package/dist/tools/codebase/ast/types.d.ts +103 -0
  116. package/dist/tools/codebase/ast/types.d.ts.map +1 -0
  117. package/dist/tools/codebase/ast/types.js +2 -0
  118. package/dist/tools/codebase/ast/types.js.map +1 -0
  119. package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  120. package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  121. package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  122. package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  123. package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  124. package/dist/tools/codebase/find.d.ts +3 -0
  125. package/dist/tools/codebase/find.d.ts.map +1 -0
  126. package/dist/tools/codebase/find.js +61 -0
  127. package/dist/tools/codebase/find.js.map +1 -0
  128. package/dist/tools/codebase/glob.d.ts +3 -0
  129. package/dist/tools/codebase/glob.d.ts.map +1 -0
  130. package/dist/tools/codebase/glob.js +37 -0
  131. package/dist/tools/codebase/glob.js.map +1 -0
  132. package/dist/tools/codebase/grep.d.ts +3 -0
  133. package/dist/tools/codebase/grep.d.ts.map +1 -0
  134. package/dist/tools/codebase/grep.js +169 -0
  135. package/dist/tools/codebase/grep.js.map +1 -0
  136. package/dist/tools/codebase/ls.d.ts +3 -0
  137. package/dist/tools/codebase/ls.d.ts.map +1 -0
  138. package/dist/tools/codebase/ls.js +39 -0
  139. package/dist/tools/codebase/ls.js.map +1 -0
  140. package/dist/tools/codebase/read.d.ts +10 -0
  141. package/dist/tools/codebase/read.d.ts.map +1 -0
  142. package/dist/tools/codebase/read.js +81 -0
  143. package/dist/tools/codebase/read.js.map +1 -0
  144. package/dist/tools/delete_memory.d.ts +3 -0
  145. package/dist/tools/delete_memory.d.ts.map +1 -0
  146. package/dist/tools/delete_memory.js +46 -0
  147. package/dist/tools/delete_memory.js.map +1 -0
  148. package/dist/tools/edit/diff-compute.d.ts +4 -0
  149. package/dist/tools/edit/diff-compute.d.ts.map +1 -0
  150. package/dist/tools/edit/diff-compute.js +18 -0
  151. package/dist/tools/edit/diff-compute.js.map +1 -0
  152. package/dist/tools/edit/diff-render.d.ts +18 -0
  153. package/dist/tools/edit/diff-render.d.ts.map +1 -0
  154. package/dist/tools/edit/diff-render.js +128 -0
  155. package/dist/tools/edit/diff-render.js.map +1 -0
  156. package/dist/tools/edit/edit.d.ts +4 -0
  157. package/dist/tools/edit/edit.d.ts.map +1 -0
  158. package/dist/tools/edit/edit.js +440 -0
  159. package/dist/tools/edit/edit.js.map +1 -0
  160. package/dist/tools/edit/matcher.d.ts +24 -0
  161. package/dist/tools/edit/matcher.d.ts.map +1 -0
  162. package/dist/tools/edit/matcher.js +66 -0
  163. package/dist/tools/edit/matcher.js.map +1 -0
  164. package/dist/tools/edit/parser.d.ts +10 -0
  165. package/dist/tools/edit/parser.d.ts.map +1 -0
  166. package/dist/tools/edit/parser.js +120 -0
  167. package/dist/tools/edit/parser.js.map +1 -0
  168. package/dist/tools/edit/types.d.ts +16 -0
  169. package/dist/tools/edit/types.d.ts.map +1 -0
  170. package/dist/tools/edit/types.js +2 -0
  171. package/dist/tools/edit/types.js.map +1 -0
  172. package/dist/tools/edit/write_plan.d.ts +5 -0
  173. package/dist/tools/edit/write_plan.d.ts.map +1 -0
  174. package/dist/tools/edit/write_plan.js +98 -0
  175. package/dist/tools/edit/write_plan.js.map +1 -0
  176. package/dist/tools/edit.d.ts.map +1 -1
  177. package/dist/tools/edit.js +66 -1
  178. package/dist/tools/edit.js.map +1 -1
  179. package/dist/tools/execute/bash/constants.d.ts +24 -0
  180. package/dist/tools/execute/bash/constants.d.ts.map +1 -0
  181. package/dist/tools/execute/bash/constants.js +88 -0
  182. package/dist/tools/execute/bash/constants.js.map +1 -0
  183. package/dist/tools/execute/bash/errors.d.ts +39 -0
  184. package/dist/tools/execute/bash/errors.d.ts.map +1 -0
  185. package/dist/tools/execute/bash/errors.js +249 -0
  186. package/dist/tools/execute/bash/errors.js.map +1 -0
  187. package/dist/tools/execute/bash/execution.d.ts +18 -0
  188. package/dist/tools/execute/bash/execution.d.ts.map +1 -0
  189. package/dist/tools/execute/bash/execution.js +207 -0
  190. package/dist/tools/execute/bash/execution.js.map +1 -0
  191. package/dist/tools/execute/bash/index.d.ts +3 -0
  192. package/dist/tools/execute/bash/index.d.ts.map +1 -0
  193. package/dist/tools/execute/bash/index.js +117 -0
  194. package/dist/tools/execute/bash/index.js.map +1 -0
  195. package/dist/tools/execute/bash/platform.d.ts +6 -0
  196. package/dist/tools/execute/bash/platform.d.ts.map +1 -0
  197. package/dist/tools/execute/bash/platform.js +64 -0
  198. package/dist/tools/execute/bash/platform.js.map +1 -0
  199. package/dist/tools/execute/bash/security.d.ts +12 -0
  200. package/dist/tools/execute/bash/security.d.ts.map +1 -0
  201. package/dist/tools/execute/bash/security.js +26 -0
  202. package/dist/tools/execute/bash/security.js.map +1 -0
  203. package/dist/tools/execute/bash/validation.d.ts +16 -0
  204. package/dist/tools/execute/bash/validation.d.ts.map +1 -0
  205. package/dist/tools/execute/bash/validation.js +57 -0
  206. package/dist/tools/execute/bash/validation.js.map +1 -0
  207. package/dist/tools/execute/bash.d.ts +3 -0
  208. package/dist/tools/execute/bash.d.ts.map +1 -0
  209. package/dist/tools/execute/bash.js +728 -0
  210. package/dist/tools/execute/bash.js.map +1 -0
  211. package/dist/tools/execute/git.d.ts +3 -0
  212. package/dist/tools/execute/git.d.ts.map +1 -0
  213. package/dist/tools/execute/git.js +183 -0
  214. package/dist/tools/execute/git.js.map +1 -0
  215. package/dist/tools/execute/task_complete.d.ts +7 -0
  216. package/dist/tools/execute/task_complete.d.ts.map +1 -0
  217. package/dist/tools/execute/task_complete.js +38 -0
  218. package/dist/tools/execute/task_complete.js.map +1 -0
  219. package/dist/tools/fetch.d.ts.map +1 -1
  220. package/dist/tools/fetch.js +2 -0
  221. package/dist/tools/fetch.js.map +1 -1
  222. package/dist/tools/find.d.ts.map +1 -1
  223. package/dist/tools/find.js +2 -0
  224. package/dist/tools/find.js.map +1 -1
  225. package/dist/tools/git.d.ts.map +1 -1
  226. package/dist/tools/git.js +2 -0
  227. package/dist/tools/git.js.map +1 -1
  228. package/dist/tools/glob.d.ts.map +1 -1
  229. package/dist/tools/glob.js +2 -0
  230. package/dist/tools/glob.js.map +1 -1
  231. package/dist/tools/grep.d.ts.map +1 -1
  232. package/dist/tools/grep.js +2 -0
  233. package/dist/tools/grep.js.map +1 -1
  234. package/dist/tools/install/clawhub.d.ts +26 -0
  235. package/dist/tools/install/clawhub.d.ts.map +1 -0
  236. package/dist/tools/install/clawhub.js +182 -0
  237. package/dist/tools/install/clawhub.js.map +1 -0
  238. package/dist/tools/install/constants.d.ts +5 -0
  239. package/dist/tools/install/constants.d.ts.map +1 -0
  240. package/dist/tools/install/constants.js +5 -0
  241. package/dist/tools/install/constants.js.map +1 -0
  242. package/dist/tools/install/install_skill.d.ts +3 -0
  243. package/dist/tools/install/install_skill.d.ts.map +1 -0
  244. package/dist/tools/install/install_skill.js +132 -0
  245. package/dist/tools/install/install_skill.js.map +1 -0
  246. package/dist/tools/install/npm.d.ts +8 -0
  247. package/dist/tools/install/npm.d.ts.map +1 -0
  248. package/dist/tools/install/npm.js +52 -0
  249. package/dist/tools/install/npm.js.map +1 -0
  250. package/dist/tools/install/skill-content.d.ts +9 -0
  251. package/dist/tools/install/skill-content.d.ts.map +1 -0
  252. package/dist/tools/install/skill-content.js +89 -0
  253. package/dist/tools/install/skill-content.js.map +1 -0
  254. package/dist/tools/install_skill.d.ts.map +1 -1
  255. package/dist/tools/install_skill.js +2 -0
  256. package/dist/tools/install_skill.js.map +1 -1
  257. package/dist/tools/list_memory.d.ts +3 -0
  258. package/dist/tools/list_memory.d.ts.map +1 -0
  259. package/dist/tools/list_memory.js +55 -0
  260. package/dist/tools/list_memory.js.map +1 -0
  261. package/dist/tools/ls.d.ts.map +1 -1
  262. package/dist/tools/ls.js +2 -0
  263. package/dist/tools/ls.js.map +1 -1
  264. package/dist/tools/memory/delete_memory.d.ts +3 -0
  265. package/dist/tools/memory/delete_memory.d.ts.map +1 -0
  266. package/dist/tools/memory/delete_memory.js +46 -0
  267. package/dist/tools/memory/delete_memory.js.map +1 -0
  268. package/dist/tools/memory/list_memory.d.ts +3 -0
  269. package/dist/tools/memory/list_memory.d.ts.map +1 -0
  270. package/dist/tools/memory/list_memory.js +55 -0
  271. package/dist/tools/memory/list_memory.js.map +1 -0
  272. package/dist/tools/memory/save_memory.d.ts +3 -0
  273. package/dist/tools/memory/save_memory.d.ts.map +1 -0
  274. package/dist/tools/memory/save_memory.js +45 -0
  275. package/dist/tools/memory/save_memory.js.map +1 -0
  276. package/dist/tools/read.d.ts.map +1 -1
  277. package/dist/tools/read.js +2 -0
  278. package/dist/tools/read.js.map +1 -1
  279. package/dist/tools/registry.d.ts +7 -2
  280. package/dist/tools/registry.d.ts.map +1 -1
  281. package/dist/tools/registry.js +112 -17
  282. package/dist/tools/registry.js.map +1 -1
  283. package/dist/tools/save_memory.d.ts +3 -0
  284. package/dist/tools/save_memory.d.ts.map +1 -0
  285. package/dist/tools/save_memory.js +45 -0
  286. package/dist/tools/save_memory.js.map +1 -0
  287. package/dist/tools/search.d.ts.map +1 -1
  288. package/dist/tools/search.js +2 -0
  289. package/dist/tools/search.js.map +1 -1
  290. package/dist/tools/tree.d.ts +12 -0
  291. package/dist/tools/tree.d.ts.map +1 -0
  292. package/dist/tools/tree.js +87 -0
  293. package/dist/tools/tree.js.map +1 -0
  294. package/dist/tools/types.d.ts +12 -0
  295. package/dist/tools/types.d.ts.map +1 -1
  296. package/dist/tools/web/fetch.d.ts +3 -0
  297. package/dist/tools/web/fetch.d.ts.map +1 -0
  298. package/dist/tools/web/fetch.js +49 -0
  299. package/dist/tools/web/fetch.js.map +1 -0
  300. package/dist/tools/web/search.d.ts +3 -0
  301. package/dist/tools/web/search.d.ts.map +1 -0
  302. package/dist/tools/web/search.js +134 -0
  303. package/dist/tools/web/search.js.map +1 -0
  304. package/dist/tools/write_plan.d.ts.map +1 -1
  305. package/dist/tools/write_plan.js +2 -0
  306. package/dist/tools/write_plan.js.map +1 -1
  307. package/dist/tui/commands.d.ts +4 -0
  308. package/dist/tui/commands.d.ts.map +1 -0
  309. package/dist/tui/commands.js +118 -0
  310. package/dist/tui/commands.js.map +1 -0
  311. package/dist/tui/components/colors.d.ts +36 -0
  312. package/dist/tui/components/colors.d.ts.map +1 -0
  313. package/dist/tui/components/colors.js +48 -0
  314. package/dist/tui/components/colors.js.map +1 -0
  315. package/dist/tui/components/header-bar.d.ts +26 -0
  316. package/dist/tui/components/header-bar.d.ts.map +1 -0
  317. package/dist/tui/components/header-bar.js +78 -0
  318. package/dist/tui/components/header-bar.js.map +1 -0
  319. package/dist/tui/components/index.d.ts +9 -0
  320. package/dist/tui/components/index.d.ts.map +1 -0
  321. package/dist/tui/components/index.js +9 -0
  322. package/dist/tui/components/index.js.map +1 -0
  323. package/dist/tui/components/landing-screen.d.ts +12 -0
  324. package/dist/tui/components/landing-screen.d.ts.map +1 -0
  325. package/dist/tui/components/landing-screen.js +53 -0
  326. package/dist/tui/components/landing-screen.js.map +1 -0
  327. package/dist/tui/components/pixel-logo.d.ts +5 -0
  328. package/dist/tui/components/pixel-logo.d.ts.map +1 -0
  329. package/dist/tui/components/pixel-logo.js +27 -0
  330. package/dist/tui/components/pixel-logo.js.map +1 -0
  331. package/dist/tui/components/plan-utils.d.ts +11 -0
  332. package/dist/tui/components/plan-utils.d.ts.map +1 -0
  333. package/dist/tui/components/plan-utils.js +62 -0
  334. package/dist/tui/components/plan-utils.js.map +1 -0
  335. package/dist/tui/components/plans-list.d.ts +18 -0
  336. package/dist/tui/components/plans-list.d.ts.map +1 -0
  337. package/dist/tui/components/plans-list.js +46 -0
  338. package/dist/tui/components/plans-list.js.map +1 -0
  339. package/dist/tui/components/rich-footer.d.ts +29 -0
  340. package/dist/tui/components/rich-footer.d.ts.map +1 -0
  341. package/dist/tui/components/rich-footer.js +124 -0
  342. package/dist/tui/components/rich-footer.js.map +1 -0
  343. package/dist/tui/components/todo-panel.d.ts +14 -0
  344. package/dist/tui/components/todo-panel.d.ts.map +1 -0
  345. package/dist/tui/components/todo-panel.js +83 -0
  346. package/dist/tui/components/todo-panel.js.map +1 -0
  347. package/dist/tui/components.d.ts.map +1 -1
  348. package/dist/tui/components.js +14 -7
  349. package/dist/tui/components.js.map +1 -1
  350. package/dist/tui/index.d.ts +2 -1
  351. package/dist/tui/index.d.ts.map +1 -1
  352. package/dist/tui/index.js +99 -505
  353. package/dist/tui/index.js.map +1 -1
  354. package/dist/tui/overlays.d.ts +28 -0
  355. package/dist/tui/overlays.d.ts.map +1 -0
  356. package/dist/tui/overlays.js +154 -0
  357. package/dist/tui/overlays.js.map +1 -0
  358. package/dist/tui/themes.d.ts +5 -0
  359. package/dist/tui/themes.d.ts.map +1 -0
  360. package/dist/tui/themes.js +38 -0
  361. package/dist/tui/themes.js.map +1 -0
  362. package/dist/web/index.d.ts.map +1 -1
  363. package/dist/web/index.js +60 -122
  364. package/dist/web/index.js.map +1 -1
  365. package/dist/web/public/index.html +1 -1
  366. package/dist/web/public/messages.js +41 -25
  367. package/dist/web/public/state.js +72 -72
  368. package/dist/web/public/style.css +1126 -996
  369. package/dist/web/public/websocket.js +267 -267
  370. package/dist/web/session-bridge.d.ts.map +1 -1
  371. package/dist/web/session-bridge.js +82 -3
  372. package/dist/web/session-bridge.js.map +1 -1
  373. package/docs/ast-tools.md +117 -0
  374. package/package.json +60 -55
  375. package/scripts/check-line-count.mjs +53 -0
  376. package/scripts/copy-ast-wasm.mjs +32 -0
  377. package/src/agent/__tests__/session-restore.test.ts +284 -0
  378. package/src/agent/commands.ts +274 -0
  379. package/src/agent/compaction.ts +9 -2
  380. package/src/agent/index.ts +30 -25
  381. package/src/agent/memory.ts +60 -0
  382. package/src/agent/prompt-builder.ts +100 -26
  383. package/src/agent/providers/anthropic.ts +179 -179
  384. package/src/agent/providers/google.ts +210 -210
  385. package/src/agent/providers/ollama.ts +186 -186
  386. package/src/agent/providers/openai.ts +18 -3
  387. package/src/agent/session-chat.ts +480 -0
  388. package/src/agent/session-display.ts +195 -0
  389. package/src/agent/session-persistence.ts +146 -0
  390. package/src/agent/session-utils.ts +54 -0
  391. package/src/agent/session.ts +261 -718
  392. package/src/cli/index.ts +73 -4
  393. package/src/config/index.ts +342 -342
  394. package/src/index.ts +30 -3
  395. package/src/tools/__tests__/bash.test.ts +187 -1
  396. package/src/tools/__tests__/edit.test.ts +275 -11
  397. package/src/tools/__tests__/fetch.test.ts +1 -1
  398. package/src/tools/__tests__/glob.test.ts +1 -1
  399. package/src/tools/__tests__/grep.test.ts +1 -1
  400. package/src/tools/__tests__/ls.test.ts +1 -1
  401. package/src/tools/__tests__/read.test.ts +1 -1
  402. package/src/tools/__tests__/registry.test.ts +332 -0
  403. package/src/tools/__tests__/write_plan.test.ts +1 -1
  404. package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
  405. package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
  406. package/src/tools/codebase/ast/adapter.ts +44 -0
  407. package/src/tools/codebase/ast/tools.ts +322 -0
  408. package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
  409. package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
  410. package/src/tools/codebase/ast/types.ts +102 -0
  411. package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  412. package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  413. package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  414. package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  415. package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  416. package/src/tools/{find.ts → codebase/find.ts} +4 -2
  417. package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
  418. package/src/tools/{grep.ts → codebase/grep.ts} +4 -2
  419. package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
  420. package/src/tools/{read.ts → codebase/read.ts} +5 -3
  421. package/src/tools/edit/diff-compute.ts +21 -0
  422. package/src/tools/edit/diff-render.ts +145 -0
  423. package/src/tools/edit/edit.ts +468 -0
  424. package/src/tools/edit/matcher.ts +70 -0
  425. package/src/tools/edit/parser.ts +131 -0
  426. package/src/tools/edit/types.ts +19 -0
  427. package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
  428. package/src/tools/execute/bash/constants.ts +93 -0
  429. package/src/tools/execute/bash/errors.ts +304 -0
  430. package/src/tools/execute/bash/execution.ts +211 -0
  431. package/src/tools/execute/bash/index.ts +131 -0
  432. package/src/tools/execute/bash/platform.ts +65 -0
  433. package/src/tools/execute/bash/security.ts +28 -0
  434. package/src/tools/execute/bash/validation.ts +78 -0
  435. package/src/tools/execute/git.ts +209 -0
  436. package/src/tools/execute/task_complete.ts +40 -0
  437. package/src/tools/install/clawhub.ts +222 -0
  438. package/src/tools/install/constants.ts +4 -0
  439. package/src/tools/install/install_skill.ts +150 -0
  440. package/src/tools/install/npm.ts +60 -0
  441. package/src/tools/install/skill-content.ts +108 -0
  442. package/src/tools/memory/delete_memory.ts +46 -0
  443. package/src/tools/memory/list_memory.ts +53 -0
  444. package/src/tools/memory/save_memory.ts +45 -0
  445. package/src/tools/registry.ts +119 -19
  446. package/src/tools/tree.ts +103 -0
  447. package/src/tools/types.ts +13 -0
  448. package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
  449. package/src/tools/{search.ts → web/search.ts} +4 -2
  450. package/src/tui/commands.ts +135 -0
  451. package/src/tui/components/colors.ts +52 -0
  452. package/src/tui/components/header-bar.ts +88 -0
  453. package/src/tui/components/index.ts +8 -0
  454. package/src/tui/components/landing-screen.ts +67 -0
  455. package/src/tui/components/pixel-logo.ts +33 -0
  456. package/src/tui/components/plan-utils.ts +69 -0
  457. package/src/tui/components/plans-list.ts +55 -0
  458. package/src/tui/components/rich-footer.ts +146 -0
  459. package/src/tui/components/todo-panel.ts +95 -0
  460. package/src/tui/index.ts +335 -786
  461. package/src/tui/overlays.ts +203 -0
  462. package/src/tui/themes.ts +41 -0
  463. package/src/web/index.ts +64 -127
  464. package/src/web/public/index.html +1 -1
  465. package/src/web/public/messages.js +41 -25
  466. package/src/web/public/state.js +72 -72
  467. package/src/web/public/style.css +1126 -996
  468. package/src/web/public/websocket.js +267 -267
  469. package/src/web/session-bridge.ts +272 -193
  470. package/AGENT.md +0 -99
  471. package/src/tools/bash.ts +0 -125
  472. package/src/tools/edit.ts +0 -747
  473. package/src/tools/git.ts +0 -76
  474. package/src/tools/install_skill.ts +0 -539
  475. package/src/tui/components.ts +0 -635
package/src/tools/git.ts DELETED
@@ -1,76 +0,0 @@
1
- import { execSync } from 'node:child_process'
2
- import type { Tool, ToolDefinition } from './types.js'
3
-
4
- export const createGitTool = (cwd: string): Tool => {
5
- const definition: ToolDefinition = {
6
- name: 'git',
7
- description:
8
- 'Run git commands for repository operations. Supports status, diff, log, show, branch, and other read-only git operations.',
9
- parameters: {
10
- command: {
11
- type: 'string',
12
- description: 'Git subcommand and arguments (e.g. "status", "diff", "log --oneline -5")',
13
- required: true,
14
- },
15
- path: { type: 'string', description: 'Repository path (default: cwd)', required: false },
16
- },
17
- }
18
-
19
- const execute = async (
20
- input: Record<string, unknown>,
21
- ): Promise<{ success: boolean; output: string; error?: string }> => {
22
- const gitCmd = String(input.command || '').trim()
23
- const repoPath = String(input.path || cwd)
24
-
25
- if (!gitCmd) {
26
- return { success: false, output: '', error: 'command is required' }
27
- }
28
-
29
- // Safety: block destructive commands
30
- const destructivePatterns = [
31
- /^reset\s+(--hard|--soft|--mixed|--merge|--keep)/i,
32
- /^push\s+.*(--force|-f)/i,
33
- /^(rebase|merge|cherry-pick|revert)\b/i,
34
- /^branch\s+-[dD]/i,
35
- /^tag\s+-[dD]/i,
36
- /^(clean|gc|prune)\b/i,
37
- /^update-ref\b/i,
38
- /^rm\b/i,
39
- /^commit\s+--amend/i,
40
- /^checkout\s+.*-B/i,
41
- ]
42
-
43
- for (const pattern of destructivePatterns) {
44
- if (pattern.test(gitCmd)) {
45
- return {
46
- success: false,
47
- output: '',
48
- error: `Destructive git command not allowed: "${gitCmd}". Only read-only operations are permitted.`,
49
- }
50
- }
51
- }
52
-
53
- try {
54
- const result = execSync(`git ${gitCmd}`, {
55
- cwd: repoPath,
56
- encoding: 'utf-8',
57
- timeout: 30000,
58
- maxBuffer: 10 * 1024 * 1024, // 10MB
59
- })
60
-
61
- return { success: true, output: result.trim() || '(empty output)' }
62
- } catch (err) {
63
- const error = err as { stderr?: string; stdout?: string; message?: string }
64
- const stderr = error.stderr || ''
65
- const stdout = error.stdout || ''
66
- const msg = error.message || String(err)
67
- return {
68
- success: false,
69
- output: stdout.trim(),
70
- error: stderr.trim() || msg,
71
- }
72
- }
73
- }
74
-
75
- return { definition, execute }
76
- }
@@ -1,539 +0,0 @@
1
- import * as cp from 'node:child_process'
2
- import * as fs from 'node:fs'
3
- import * as path from 'node:path'
4
- // @ts-expect-error - adm-zip has no types
5
- import AdmZip from 'adm-zip'
6
- import { fmtErr } from './errors.js'
7
- import type { Tool, ToolResult } from './types.js'
8
-
9
- // ── Constants ────────────────────────────────────────────────────────────────
10
-
11
- const SKILLS_DIR = '.lonny/skills'
12
- const NPM_REGISTRY_URL = 'https://registry.npmjs.org'
13
- const CLAWHUB_API_BASE = 'https://clawhub.ai/api/v1'
14
- const CLAWHUB_SITE = 'https://clawhub.ai'
15
-
16
- // ── Helpers ──────────────────────────────────────────────────────────────────
17
-
18
- /** Fetch package info from npm registry */
19
- async function fetchNpmPackageInfo(
20
- packageName: string,
21
- ): Promise<{ description: string; readme: string } | null> {
22
- try {
23
- const response = await fetch(`${NPM_REGISTRY_URL}/${encodeURIComponent(packageName)}`, {
24
- headers: { Accept: 'application/json' },
25
- })
26
- if (!response.ok) return null
27
-
28
- const data = (await response.json()) as {
29
- description?: string
30
- readme?: string
31
- }
32
- return {
33
- description: data.description || '',
34
- readme: data.readme || '',
35
- }
36
- } catch {
37
- return null
38
- }
39
- }
40
-
41
- // ── ClawHub Helpers ──────────────────────────────────────────────────────────
42
-
43
- /** Fetch skill info from ClawHub API */
44
- async function fetchClawHubSkillInfo(
45
- slug: string,
46
- ): Promise<{ displayName: string; summary: string; latestVersion: string } | null> {
47
- try {
48
- const response = await fetch(`${CLAWHUB_API_BASE}/skills/${encodeURIComponent(slug)}`, {
49
- headers: { Accept: 'application/json' },
50
- })
51
- if (!response.ok) return null
52
-
53
- const data = (await response.json()) as {
54
- skill?: { displayName?: string; summary?: string }
55
- latestVersion?: { version?: string }
56
- }
57
- return {
58
- displayName: data.skill?.displayName || slug,
59
- summary: data.skill?.summary || '',
60
- latestVersion: data.latestVersion?.version || 'latest',
61
- }
62
- } catch {
63
- return null
64
- }
65
- }
66
-
67
- /** Download a ClawHub skill as a ZIP and extract its content */
68
- async function downloadClawHubSkill(
69
- slug: string,
70
- version?: string,
71
- ): Promise<{ files: Map<string, string> } | null> {
72
- try {
73
- const versionPath = version ? `/${version}` : ''
74
- const url = `${CLAWHUB_API_BASE}/download?slug=${encodeURIComponent(slug)}${version ? `&version=${encodeURIComponent(version)}` : ''}`
75
- const response = await fetch(url)
76
- if (!response.ok) {
77
- // Try alternative URL patterns
78
- const altUrl = `${CLAWHUB_API_BASE}/download/${encodeURIComponent(slug)}${versionPath}`
79
- const altResponse = await fetch(altUrl)
80
- if (!altResponse.ok) return null
81
-
82
- const buffer = await altResponse.arrayBuffer()
83
- return extractZipContent(Buffer.from(buffer))
84
- }
85
-
86
- const buffer = await response.arrayBuffer()
87
- return extractZipContent(Buffer.from(buffer))
88
- } catch {
89
- return null
90
- }
91
- }
92
-
93
- /** Extract files from a ZIP buffer */
94
- function extractZipContent(buffer: Buffer): { files: Map<string, string> } {
95
- const zip = new AdmZip(buffer)
96
- const entries = zip.getEntries()
97
- const files = new Map<string, string>()
98
-
99
- for (const entry of entries) {
100
- if (!entry.isDirectory) {
101
- files.set(entry.entryName, entry.getData().toString('utf-8'))
102
- }
103
- }
104
-
105
- return { files }
106
- }
107
-
108
- /** Sanitize a package name to a valid skill name (lowercase, digits, hyphens) */
109
- function sanitizeSkillName(name: string): string {
110
- return name
111
- .toLowerCase()
112
- .replace(/@/g, '')
113
- .replace(/\//g, '-')
114
- .replace(/[^a-z0-9-]/g, '')
115
- .replace(/-+/g, '-')
116
- .replace(/^-|-$/g, '')
117
- }
118
-
119
- /** Generate a usage guide snippet from the readme */
120
- function extractUsageGuide(readme: string): string {
121
- // Try to extract code examples from the readme
122
- const codeBlocks: string[] = []
123
- const blockRegex = /```(?:js|javascript|ts|typescript|bash)\n([\s\S]*?)```/g
124
- let match: RegExpExecArray | null = blockRegex.exec(readme)
125
- while (match !== null) {
126
- const block = match[1].trim()
127
- if (block.length > 0 && block.length < 1000) {
128
- codeBlocks.push(block)
129
- }
130
- match = blockRegex.exec(readme)
131
- }
132
- return codeBlocks.slice(0, 3).join('\n\n')
133
- }
134
-
135
- /** Generate the skill markdown content (npm package) */
136
- function generateSkillContent(
137
- packageName: string,
138
- description: string,
139
- usageGuide: string,
140
- ): string {
141
- const installCmd = `npm install ${packageName}`
142
-
143
- let content = `---
144
- name: ${sanitizeSkillName(packageName)}
145
- description: ${description}
146
- ---
147
-
148
- # ${packageName}
149
-
150
- ${description}
151
-
152
- ## Installation
153
-
154
- This package is already installed in the project via \`${installCmd}\`.
155
-
156
- ## Usage
157
-
158
- You can import and use this package in your code:
159
-
160
- \`\`\`typescript
161
- import ${packageName.replace(/@/, '').replace(/[/-]/g, '_')} from '${packageName}'
162
- \`\`\`
163
- `
164
-
165
- if (usageGuide) {
166
- content += `\n## Examples\n\n${usageGuide}\n`
167
- }
168
-
169
- content += `
170
- ## Notes
171
-
172
- - This skill was automatically installed. The package is available as a project dependency.
173
- - Refer to the package's npm page or repository for full documentation.
174
- `
175
-
176
- return content
177
- }
178
-
179
- /** Generate the skill markdown content (ClawHub skill) */
180
- function generateClawHubSkillContent(
181
- slug: string,
182
- displayName: string,
183
- summary: string,
184
- clawhubContent: string,
185
- ): string {
186
- // If the ZIP contains a SKILL.md, use it directly
187
- // Otherwise, generate a wrapper skill file
188
- let content = `---
189
- name: ${sanitizeSkillName(slug)}
190
- description: ${summary || displayName}
191
- clawhub: ${slug}
192
- ---
193
-
194
- # ${displayName}
195
-
196
- ${summary || 'ClawHub skill: ' + slug}
197
-
198
- `
199
-
200
- if (clawhubContent) {
201
- content += clawhubContent + '\n'
202
- }
203
-
204
- content += `
205
- ## Notes
206
-
207
- - This skill was installed from [ClawHub](${CLAWHUB_SITE}/${slug}).
208
- - Source: ${CLAWHUB_SITE}/${slug}
209
- - The skill instructions are loaded automatically on the next turn.
210
- `
211
-
212
- return content
213
- }
214
-
215
- /** Run npm install in the project directory */
216
- async function runNpmInstall(cwd: string, packageName: string): Promise<string | null> {
217
- return new Promise(resolve => {
218
- const child = cp.spawn('npm', ['install', packageName], {
219
- cwd,
220
- stdio: ['ignore', 'pipe', 'pipe'],
221
- shell: process.platform === 'win32',
222
- })
223
-
224
- let stderr = ''
225
- child.stderr?.on('data', (data: Buffer) => {
226
- stderr += data.toString()
227
- })
228
-
229
- const timeout = setTimeout(() => {
230
- child.kill()
231
- resolve(`Timed out after 60s. ${stderr.slice(0, 500)}`)
232
- }, 60_000)
233
-
234
- child.on('close', code => {
235
- clearTimeout(timeout)
236
- if (code === 0) {
237
- resolve(null) // success
238
- } else {
239
- resolve(stderr.slice(0, 500) || `npm install exited with code ${code}`)
240
- }
241
- })
242
-
243
- child.on('error', err => {
244
- clearTimeout(timeout)
245
- resolve(`Failed to start npm install: ${err.message}`)
246
- })
247
- })
248
- }
249
-
250
- // ── Tool ─────────────────────────────────────────────────────────────────────
251
-
252
- export function createInstallSkillTool(cwd: string): Tool {
253
- return {
254
- definition: {
255
- name: 'install_skill',
256
- description: `Install an npm package or ClawHub skill. This tool:
257
- 1. For npm packages: fetches info from npm registry, runs \`npm install\`, creates a skill file
258
- 2. For ClawHub skills: fetches skill metadata from clawhub.ai, downloads the skill bundle, writes SKILL.md
259
-
260
- After installation, the skill will be automatically loaded on the next conversation turn (the system prompt includes active skills).
261
-
262
- Use this when you need to use a third-party npm package or install a skill from ClawHub (clawhub.ai).
263
-
264
- ClawHub skills: pass the skill slug (e.g. \`repo-release-notes\`) as package_name. The tool auto-detects ClawHub skills.`,
265
- parameters: {
266
- package_name: {
267
- type: 'string',
268
- description:
269
- 'The npm package name or ClawHub skill slug to install (e.g. "dayjs", "cn-time-parser", "@scope/package", or ClawHub slug like "repo-release-notes")',
270
- required: true,
271
- },
272
- description: {
273
- type: 'string',
274
- description:
275
- 'Optional description of what the package/skill does. If not provided, it will be fetched from the npm registry or ClawHub.',
276
- required: false,
277
- },
278
- usage_guide: {
279
- type: 'string',
280
- description:
281
- 'Optional custom usage guide for the AI. If not provided, it will be auto-generated from the readme or ClawHub SKILL.md. Provide this if the auto-generated guide is insufficient.',
282
- required: false,
283
- },
284
- clawhub: {
285
- type: 'boolean',
286
- description:
287
- "Force ClawHub mode. If true, treats package_name as a ClawHub skill slug. Auto-detected if the package doesn't exist on npm.",
288
- required: false,
289
- },
290
- version: {
291
- type: 'string',
292
- description: 'Optional version for ClawHub skill install (defaults to latest).',
293
- required: false,
294
- },
295
- },
296
- },
297
- async execute(input): Promise<ToolResult> {
298
- // ── Auto-correction: if input is a string, wrap it ────────────────
299
- if (typeof input === 'string') {
300
- input = { package_name: input }
301
- }
302
-
303
- const packageName = input.package_name as string
304
- if (!packageName || typeof packageName !== 'string') {
305
- return {
306
- success: false,
307
- output: '',
308
- error: 'package_name is required (string)',
309
- }
310
- }
311
-
312
- const forceClawHub = input.clawhub === true
313
- const version = input.version as string | undefined
314
-
315
- // ── Auto-detect: try npm first, fall back to ClawHub ──────────────
316
- const npmInfo = forceClawHub ? null : await fetchNpmPackageInfo(packageName)
317
-
318
- // If npm lookup failed and not forced, try ClawHub
319
- if (!npmInfo && !forceClawHub) {
320
- const clawhubInfo = await fetchClawHubSkillInfo(packageName)
321
- if (clawhubInfo) {
322
- // It's a ClawHub skill — install from ClawHub
323
- return installFromClawHub(cwd, packageName, version, clawhubInfo, input)
324
- }
325
-
326
- return {
327
- success: false,
328
- output: '',
329
- error: `"${packageName}" was not found on npm registry nor on ClawHub. Check the name and try again.`,
330
- }
331
- }
332
-
333
- // If forced ClawHub mode
334
- if (forceClawHub) {
335
- const clawhubInfo = await fetchClawHubSkillInfo(packageName)
336
- if (!clawhubInfo) {
337
- return {
338
- success: false,
339
- output: '',
340
- error: `"${packageName}" was not found on ClawHub.`,
341
- }
342
- }
343
- return installFromClawHub(cwd, packageName, version, clawhubInfo, input)
344
- }
345
-
346
- // ── npm package install path ──────────────────────────────────────
347
- const description = (input.description as string) || npmInfo?.description || packageName
348
- const readme = npmInfo?.readme || ''
349
-
350
- const installError = await runNpmInstall(cwd, packageName)
351
- if (installError) {
352
- return {
353
- success: false,
354
- output: '',
355
- error: `npm install failed: ${installError}`,
356
- }
357
- }
358
-
359
- const usageGuide = (input.usage_guide as string) || (readme ? extractUsageGuide(readme) : '')
360
-
361
- const skillContent = generateSkillContent(packageName, description, usageGuide)
362
-
363
- const skillsDir = path.resolve(cwd, SKILLS_DIR)
364
- const skillName = sanitizeSkillName(packageName)
365
- const skillFilePath = path.join(skillsDir, `${skillName}.md`)
366
-
367
- try {
368
- fs.mkdirSync(skillsDir, { recursive: true })
369
- fs.writeFileSync(skillFilePath, skillContent, 'utf-8')
370
- } catch (err) {
371
- return {
372
- success: false,
373
- output: '',
374
- error: `Failed to write skill file: ${fmtErr(err)}`,
375
- }
376
- }
377
-
378
- const relPath = path.relative(cwd, skillFilePath).replace(/\\/g, '/')
379
-
380
- return {
381
- success: true,
382
- output: `Installed npm package "${packageName}" and created skill "${relPath}".
383
-
384
- The package is now a project dependency. The skill file contains usage instructions for the AI and will be loaded automatically on the next turn.
385
-
386
- To verify: use \`read\` on "${relPath}" to see the skill content.`,
387
- }
388
- },
389
- }
390
- }
391
-
392
- /** Install a skill from ClawHub */
393
- async function installFromClawHub(
394
- cwd: string,
395
- slug: string,
396
- version: string | undefined,
397
- info: { displayName: string; summary: string; latestVersion: string },
398
- input: Record<string, unknown>,
399
- ): Promise<ToolResult> {
400
- const versionToUse = version || info.latestVersion
401
-
402
- // Download the skill ZIP
403
- const downloaded = await downloadClawHubSkill(slug, versionToUse)
404
- if (!downloaded) {
405
- return {
406
- success: false,
407
- output: '',
408
- error: `Failed to download ClawHub skill "${slug}" (version ${versionToUse}).`,
409
- }
410
- }
411
-
412
- const skillsDir = path.resolve(cwd, SKILLS_DIR)
413
- const skillName = sanitizeSkillName(slug)
414
- const skillFilePath = path.join(skillsDir, `${skillName}.md`)
415
- const skillDirPath = path.join(skillsDir, skillName)
416
-
417
- // Find the SKILL.md in the ZIP
418
- const skillMdEntry = findSkillMdInZip(downloaded.files)
419
-
420
- // Also extract supporting files into a subdirectory
421
- if (skillMdEntry) {
422
- // Extract all files into skill subdirectory for supporting files
423
- try {
424
- fs.mkdirSync(skillDirPath, { recursive: true })
425
- for (const [filePath, content] of downloaded.files) {
426
- // Normalize the path: strip common root directory
427
- const normalizedPath = normalizeZipPath(filePath, slug)
428
- if (!normalizedPath || normalizedPath === 'SKILL.md') continue // SKILL.md goes to root
429
- const fullPath = path.join(skillDirPath, normalizedPath)
430
- fs.mkdirSync(path.dirname(fullPath), { recursive: true })
431
- fs.writeFileSync(fullPath, content, 'utf-8')
432
- }
433
- } catch (err) {
434
- // Non-critical: supporting files are optional
435
- console.warn(
436
- `[install_skill] Warning: could not extract all supporting files: ${fmtErr(err)}`,
437
- )
438
- }
439
-
440
- // Generate skill file (using the SKILL.md content from the ZIP)
441
- const description = (input.description as string) || info.summary || slug
442
- const skillContent = generateClawHubSkillContent(
443
- slug,
444
- info.displayName,
445
- description,
446
- skillMdEntry,
447
- )
448
-
449
- try {
450
- fs.mkdirSync(skillsDir, { recursive: true })
451
- fs.writeFileSync(skillFilePath, skillContent, 'utf-8')
452
- } catch (err) {
453
- return {
454
- success: false,
455
- output: '',
456
- error: `Failed to write skill file: ${fmtErr(err)}`,
457
- }
458
- }
459
- } else {
460
- // No SKILL.md found in ZIP, generate a wrapper skill
461
- const description = (input.description as string) || info.summary || slug
462
- // Collect all extracted content as a markdown reference
463
- const filesList = Array.from(downloaded.files.keys())
464
- .map(f => `- ${f}`)
465
- .join('\n')
466
- const combinedContent =
467
- `## Files in this skill\n\n${filesList}\n\n` +
468
- Array.from(downloaded.files.entries())
469
- .map(([name, content]) => `### ${name}\n\n\`\`\`\n${content.slice(0, 2000)}\n\`\`\``)
470
- .join('\n\n')
471
-
472
- const skillContent = generateClawHubSkillContent(
473
- slug,
474
- info.displayName,
475
- description,
476
- combinedContent,
477
- )
478
-
479
- try {
480
- fs.mkdirSync(skillsDir, { recursive: true })
481
- fs.writeFileSync(skillFilePath, skillContent, 'utf-8')
482
- } catch (err) {
483
- return {
484
- success: false,
485
- output: '',
486
- error: `Failed to write skill file: ${fmtErr(err)}`,
487
- }
488
- }
489
- }
490
-
491
- const relPath = path.relative(cwd, skillFilePath).replace(/\\/g, '/')
492
- const supportRelPath = skillMdEntry ? path.relative(cwd, skillDirPath).replace(/\\/g, '/') : ''
493
-
494
- let output = `Installed ClawHub skill "${slug}" (v${versionToUse}) and created skill file "${relPath}".`
495
- if (supportRelPath) {
496
- output += `\nSupporting files extracted to "${supportRelPath}/".`
497
- }
498
- output += `
499
-
500
- The skill instructions will be loaded automatically on the next turn.
501
-
502
- To verify: use \`read\` on "${relPath}" to see the skill content.`
503
-
504
- return {
505
- success: true,
506
- output,
507
- }
508
- }
509
-
510
- /** Find the SKILL.md entry in the downloaded files (case-insensitive) */
511
- function findSkillMdInZip(files: Map<string, string>): string | null {
512
- for (const [filePath] of files) {
513
- const normalized = filePath.replace(/\\/g, '/').toLowerCase()
514
- if (normalized.endsWith('/skill.md')) {
515
- return files.get(filePath) || null
516
- }
517
- }
518
- // Fallback: check root-level SKILL.md
519
- for (const [filePath] of files) {
520
- const normalized = filePath.replace(/\\/g, '/')
521
- if (normalized.toLowerCase() === 'skill.md' || normalized.endsWith('/skill.md')) {
522
- return files.get(filePath) || null
523
- }
524
- }
525
- return null
526
- }
527
-
528
- /** Normalize a ZIP file path, stripping common root directory */
529
- function normalizeZipPath(filePath: string, slug: string): string {
530
- const normalized = filePath.replace(/\\/g, '/')
531
- // Strip leading directory that matches the slug or is a common root
532
- const parts = normalized.split('/')
533
- if (parts.length > 1) {
534
- // Remove the first segment if it's a common root
535
- // ZIPs from ClawHub often have a root folder like "repo-release-notes/"
536
- return parts.slice(1).join('/')
537
- }
538
- return normalized
539
- }