sparkecoder 0.1.22 → 0.1.23

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 (290) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +1308 -212
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +1933 -454
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +20 -3
  7. package/dist/db/index.js +97 -0
  8. package/dist/db/index.js.map +1 -1
  9. package/dist/{index-CNwLFGiZ.d.ts → index-BblbmG_0.d.ts} +19 -4
  10. package/dist/index.d.ts +6 -6
  11. package/dist/index.js +1913 -434
  12. package/dist/index.js.map +1 -1
  13. package/dist/{schema-Df7MU3nM.d.ts → schema-D_8A4k01.d.ts} +246 -2
  14. package/dist/search-ybREg7F_.d.ts +254 -0
  15. package/dist/server/index.js +1918 -439
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/tools/index.d.ts +7 -56
  18. package/dist/tools/index.js +894 -27
  19. package/dist/tools/index.js.map +1 -1
  20. package/package.json +5 -1
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/app-path-routes-manifest.json +4 -0
  24. package/web/.next/standalone/web/.next/build-manifest.json +7 -6
  25. package/web/.next/standalone/web/.next/prerender-manifest.json +99 -3
  26. package/web/.next/standalone/web/.next/required-server-files.json +28 -4
  27. package/web/.next/standalone/web/.next/routes-manifest.json +24 -0
  28. package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +5 -4
  29. package/web/.next/standalone/web/.next/server/app/(main)/page.js +2 -2
  30. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  31. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  32. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +5 -4
  33. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js +2 -2
  34. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  35. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +5 -4
  37. package/web/.next/standalone/web/.next/server/app/_global-error/page.js +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +5 -4
  48. package/web/.next/standalone/web/.next/server/app/_not-found/page.js +2 -2
  49. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  50. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  51. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  52. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  54. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  60. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/installation/page/app-paths-manifest.json +3 -0
  62. package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +18 -0
  63. package/web/.next/standalone/web/.next/server/app/docs/installation/page/next-font-manifest.json +11 -0
  64. package/web/.next/standalone/web/.next/server/app/docs/installation/page/react-loadable-manifest.json +1 -0
  65. package/web/.next/standalone/web/.next/server/app/docs/installation/page/server-reference-manifest.json +4 -0
  66. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js +21 -0
  67. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.map +5 -0
  68. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.nft.json +1 -0
  69. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +2 -0
  70. package/web/.next/standalone/web/.next/server/app/docs/installation.html +86 -0
  71. package/web/.next/standalone/web/.next/server/app/docs/installation.meta +16 -0
  72. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +36 -0
  73. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +36 -0
  74. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +6 -0
  75. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +7 -0
  76. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +3 -0
  77. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +22 -0
  78. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +4 -0
  79. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +5 -0
  80. package/web/.next/standalone/web/.next/server/app/docs/page/app-paths-manifest.json +3 -0
  81. package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +18 -0
  82. package/web/.next/standalone/web/.next/server/app/docs/page/next-font-manifest.json +11 -0
  83. package/web/.next/standalone/web/.next/server/app/docs/page/react-loadable-manifest.json +1 -0
  84. package/web/.next/standalone/web/.next/server/app/docs/page/server-reference-manifest.json +4 -0
  85. package/web/.next/standalone/web/.next/server/app/docs/page.js +21 -0
  86. package/web/.next/standalone/web/.next/server/app/docs/page.js.map +5 -0
  87. package/web/.next/standalone/web/.next/server/app/docs/page.js.nft.json +1 -0
  88. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +2 -0
  89. package/web/.next/standalone/web/.next/server/app/docs/skills/page/app-paths-manifest.json +3 -0
  90. package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +18 -0
  91. package/web/.next/standalone/web/.next/server/app/docs/skills/page/next-font-manifest.json +11 -0
  92. package/web/.next/standalone/web/.next/server/app/docs/skills/page/react-loadable-manifest.json +1 -0
  93. package/web/.next/standalone/web/.next/server/app/docs/skills/page/server-reference-manifest.json +4 -0
  94. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js +21 -0
  95. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.map +5 -0
  96. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.nft.json +1 -0
  97. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +2 -0
  98. package/web/.next/standalone/web/.next/server/app/docs/skills.html +268 -0
  99. package/web/.next/standalone/web/.next/server/app/docs/skills.meta +16 -0
  100. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +82 -0
  101. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +82 -0
  102. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +6 -0
  103. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +7 -0
  104. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +3 -0
  105. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +66 -0
  106. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +4 -0
  107. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +5 -0
  108. package/web/.next/standalone/web/.next/server/app/docs/tools/page/app-paths-manifest.json +3 -0
  109. package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +18 -0
  110. package/web/.next/standalone/web/.next/server/app/docs/tools/page/next-font-manifest.json +11 -0
  111. package/web/.next/standalone/web/.next/server/app/docs/tools/page/react-loadable-manifest.json +1 -0
  112. package/web/.next/standalone/web/.next/server/app/docs/tools/page/server-reference-manifest.json +4 -0
  113. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js +21 -0
  114. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.map +5 -0
  115. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.nft.json +1 -0
  116. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +2 -0
  117. package/web/.next/standalone/web/.next/server/app/docs/tools.html +242 -0
  118. package/web/.next/standalone/web/.next/server/app/docs/tools.meta +16 -0
  119. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +87 -0
  120. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +87 -0
  121. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +6 -0
  122. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +7 -0
  123. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +3 -0
  124. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +72 -0
  125. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +4 -0
  126. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +5 -0
  127. package/web/.next/standalone/web/.next/server/app/docs.html +74 -0
  128. package/web/.next/standalone/web/.next/server/app/docs.meta +15 -0
  129. package/web/.next/standalone/web/.next/server/app/docs.rsc +34 -0
  130. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +34 -0
  131. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +6 -0
  132. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +7 -0
  133. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +3 -0
  134. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +20 -0
  135. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +5 -0
  136. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  137. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  138. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  139. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  140. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  141. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  142. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  143. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  144. package/web/.next/standalone/web/.next/server/app-paths-manifest.json +4 -0
  145. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5f58fd73._.js → 2374f_244589df._.js} +1 -1
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_84859a94._.js → 2374f_41a27541._.js} +1 -1
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_65fcfd95._.js → 2374f_47c9e2d5._.js} +1 -1
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f1038f7c._.js → 2374f_4bf2df9d._.js} +1 -1
  149. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_663d1038._.js +3 -0
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_954e49c0._.js +3 -0
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_387a1437._.js → 2374f_c33b095a._.js} +1 -1
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_cfd0137a._.js → 2374f_fa61fbb2._.js} +1 -1
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_741f6b67._.js → 2374f_fb82ac0d._.js} +1 -1
  154. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_96bca05b._.js → 2374f_next_dist_bbe64674._.js} +2 -2
  155. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7f04455b._.js → [root-of-the-server]__1e06ddf7._.js} +2 -2
  156. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2b151e1c._.js +3 -0
  157. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2dbf511a._.js +9 -0
  158. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__397fadd4._.js +3 -0
  159. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44bd8bd1._.js +3 -0
  160. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__70cecda8._.js +3 -0
  161. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9fdf9974._.js +3 -0
  162. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f18f92f4._.js → [root-of-the-server]__b050bb8f._.js} +2 -2
  163. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d3034cd2._.js +3 -0
  164. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c3a1e22c._.js → [root-of-the-server]__ef2713cf._.js} +2 -2
  165. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f764bebe._.js +3 -0
  166. package/web/.next/standalone/web/.next/server/chunks/ssr/web_046bf7db._.js +3 -0
  167. package/web/.next/standalone/web/.next/server/chunks/ssr/web_656c1e45._.js +7 -0
  168. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_c7618534._.js → web_76ccf09f._.js} +4 -4
  169. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_installation_page_actions_52cc0648.js +3 -0
  170. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_page_actions_4fe77da8.js +3 -0
  171. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_skills_page_actions_251df2e1.js +3 -0
  172. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_tools_page_actions_3e6382b0.js +3 -0
  173. package/web/.next/standalone/web/.next/server/chunks/ssr/web_a565dc94._.js +4 -0
  174. package/web/.next/standalone/web/.next/server/chunks/ssr/web_b1cce0b7._.js +4 -0
  175. package/web/.next/standalone/web/.next/server/chunks/ssr/web_b42ed1be._.js +3 -0
  176. package/web/.next/standalone/web/.next/server/chunks/ssr/web_c0c2bee4._.js +4 -0
  177. package/web/.next/standalone/web/.next/server/chunks/ssr/web_eea9c122._.js +3 -0
  178. package/web/.next/standalone/web/.next/server/chunks/ssr/web_ff00a5c3._.js +4 -0
  179. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
  180. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +3 -0
  181. package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +5 -4
  182. package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
  183. package/web/.next/standalone/web/.next/server/next-font-manifest.json +16 -0
  184. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  185. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  186. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  187. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  188. package/web/.next/standalone/web/.next/static/chunks/0cc382a66266188e.js +7 -0
  189. package/web/.next/standalone/web/.next/static/chunks/0fda34e553582102.js +1 -0
  190. package/web/.next/standalone/web/.next/static/{static/chunks/5e5b485d77ac0d8f.js → chunks/6407c045dfc908fe.js} +3 -3
  191. package/web/.next/standalone/web/.next/static/chunks/651e187cc15d66de.js +1 -0
  192. package/web/.next/standalone/web/.next/static/chunks/862ced58ce21a270.js +4 -0
  193. package/web/.next/standalone/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
  194. package/web/.next/standalone/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
  195. package/web/.next/standalone/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
  196. package/web/.next/standalone/web/.next/static/chunks/af22745850132107.css +1 -0
  197. package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
  198. package/web/.next/standalone/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
  199. package/web/.next/standalone/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  200. package/web/.next/standalone/web/.next/static/static/chunks/0cc382a66266188e.js +7 -0
  201. package/web/.next/standalone/web/.next/static/static/chunks/0fda34e553582102.js +1 -0
  202. package/web/.next/{static/chunks/5e5b485d77ac0d8f.js → standalone/web/.next/static/static/chunks/6407c045dfc908fe.js} +3 -3
  203. package/web/.next/standalone/web/.next/static/static/chunks/651e187cc15d66de.js +1 -0
  204. package/web/.next/standalone/web/.next/static/static/chunks/862ced58ce21a270.js +4 -0
  205. package/web/.next/standalone/web/.next/static/static/chunks/89bc21c0443670f4.js +1 -0
  206. package/web/.next/standalone/web/.next/static/static/chunks/8f4edf22ededc29b.js +7 -0
  207. package/web/.next/standalone/web/.next/static/static/chunks/ad6b9dbb257d62cc.js +1 -0
  208. package/web/.next/standalone/web/.next/static/static/chunks/af22745850132107.css +1 -0
  209. package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +1 -0
  210. package/web/.next/standalone/web/.next/static/static/chunks/db9b22c844a35e20.js +5 -0
  211. package/web/.next/standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  212. package/web/.next/standalone/web/mdx-components.tsx +119 -0
  213. package/web/.next/standalone/web/next.config.ts +15 -1
  214. package/web/.next/standalone/web/package-lock.json +559 -4
  215. package/web/.next/standalone/web/package.json +4 -0
  216. package/web/.next/standalone/web/runtime-config.json +1 -1
  217. package/web/.next/standalone/web/server.js +1 -1
  218. package/web/.next/standalone/web/src/app/(main)/page.tsx +127 -5
  219. package/web/.next/standalone/web/src/app/docs/installation/page.mdx +128 -0
  220. package/web/.next/standalone/web/src/app/docs/layout.tsx +74 -0
  221. package/web/.next/standalone/web/src/app/docs/page.mdx +90 -0
  222. package/web/.next/standalone/web/src/app/docs/skills/page.mdx +334 -0
  223. package/web/.next/standalone/web/src/app/docs/tools/page.mdx +300 -0
  224. package/web/.next/standalone/web/src/components/ai-elements/mention-input.tsx +809 -0
  225. package/web/.next/standalone/web/src/components/ai-elements/search-tool.tsx +400 -0
  226. package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +275 -0
  227. package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +19 -5
  228. package/web/.next/standalone/web/src/components/chat-interface.tsx +525 -71
  229. package/web/.next/standalone/web/src/hooks/use-workspace-files.ts +108 -0
  230. package/web/.next/standalone/web/src/lib/api.ts +90 -4
  231. package/web/.next/static/chunks/0cc382a66266188e.js +7 -0
  232. package/web/.next/static/chunks/0fda34e553582102.js +1 -0
  233. package/web/.next/{standalone/web/.next/static/chunks/5e5b485d77ac0d8f.js → static/chunks/6407c045dfc908fe.js} +3 -3
  234. package/web/.next/static/chunks/651e187cc15d66de.js +1 -0
  235. package/web/.next/static/chunks/862ced58ce21a270.js +4 -0
  236. package/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
  237. package/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
  238. package/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
  239. package/web/.next/static/chunks/af22745850132107.css +1 -0
  240. package/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
  241. package/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
  242. package/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  243. package/web/package.json +4 -0
  244. package/dist/bash-CGAqW7HR.d.ts +0 -80
  245. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_814be2c9._.js +0 -3
  246. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +0 -3
  247. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__3ec22171._.js +0 -9
  248. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__513c6b45._.js +0 -3
  249. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +0 -3
  250. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +0 -7
  251. package/web/.next/standalone/web/.next/server/chunks/ssr/web_e6034803._.js +0 -3
  252. package/web/.next/standalone/web/.next/static/chunks/03d4169891280e04.js +0 -7
  253. package/web/.next/standalone/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  254. package/web/.next/standalone/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
  255. package/web/.next/standalone/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  256. package/web/.next/standalone/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  257. package/web/.next/standalone/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  258. package/web/.next/standalone/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  259. package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  260. package/web/.next/standalone/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
  261. package/web/.next/standalone/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  262. package/web/.next/standalone/web/.next/static/static/chunks/03d4169891280e04.js +0 -7
  263. package/web/.next/standalone/web/.next/static/static/chunks/2d5da0cfc011b8d9.js +0 -1
  264. package/web/.next/standalone/web/.next/static/static/chunks/3bb454ca848ec78e.js +0 -7
  265. package/web/.next/standalone/web/.next/static/static/chunks/634fd97fab9ed4e4.js +0 -4
  266. package/web/.next/standalone/web/.next/static/static/chunks/77e4bf0421481629.js +0 -1
  267. package/web/.next/standalone/web/.next/static/static/chunks/beb9625c4a470042.js +0 -1
  268. package/web/.next/standalone/web/.next/static/static/chunks/c2244168e74b8c78.js +0 -1
  269. package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +0 -5
  270. package/web/.next/standalone/web/.next/static/static/chunks/cb355fac10c6ad11.css +0 -1
  271. package/web/.next/standalone/web/.next/static/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  272. package/web/.next/static/chunks/03d4169891280e04.js +0 -7
  273. package/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  274. package/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
  275. package/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  276. package/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  277. package/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  278. package/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  279. package/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  280. package/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
  281. package/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  282. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  283. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  284. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  285. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  286. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  287. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  288. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  289. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  290. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -1,10 +1,400 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
3
7
  var __export = (target, all) => {
4
8
  for (var name in all)
5
9
  __defProp(target, name, { get: all[name], enumerable: true });
6
10
  };
7
11
 
12
+ // src/config/types.ts
13
+ import { z } from "zod";
14
+ var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, SparkcoderConfigSchema;
15
+ var init_types = __esm({
16
+ "src/config/types.ts"() {
17
+ "use strict";
18
+ ToolApprovalConfigSchema = z.object({
19
+ bash: z.boolean().optional().default(true),
20
+ write_file: z.boolean().optional().default(false),
21
+ read_file: z.boolean().optional().default(false),
22
+ load_skill: z.boolean().optional().default(false),
23
+ todo: z.boolean().optional().default(false)
24
+ });
25
+ SkillMetadataSchema = z.object({
26
+ name: z.string(),
27
+ description: z.string(),
28
+ // Whether to always inject this skill into context (vs on-demand loading)
29
+ alwaysApply: z.boolean().optional().default(false),
30
+ // Glob patterns - auto-inject when working with matching files
31
+ globs: z.array(z.string()).optional().default([])
32
+ });
33
+ SessionConfigSchema = z.object({
34
+ toolApprovals: z.record(z.string(), z.boolean()).optional(),
35
+ approvalWebhook: z.string().url().optional(),
36
+ skillsDirectory: z.string().optional(),
37
+ maxContextChars: z.number().optional().default(2e5)
38
+ });
39
+ SparkcoderConfigSchema = z.object({
40
+ // Default model to use (Vercel AI Gateway format)
41
+ defaultModel: z.string().default("anthropic/claude-opus-4-5"),
42
+ // Working directory for file operations
43
+ workingDirectory: z.string().optional(),
44
+ // Tool approval settings
45
+ toolApprovals: ToolApprovalConfigSchema.optional().default({}),
46
+ // Approval webhook URL (called when approval is needed)
47
+ approvalWebhook: z.string().url().optional(),
48
+ // Skills configuration
49
+ skills: z.object({
50
+ // Directory containing skill files
51
+ directory: z.string().optional().default("./skills"),
52
+ // Additional skill directories to include
53
+ additionalDirectories: z.array(z.string()).optional().default([])
54
+ }).optional().default({}),
55
+ // Context management
56
+ context: z.object({
57
+ // Maximum context size before summarization (in characters)
58
+ maxChars: z.number().optional().default(2e5),
59
+ // Enable automatic summarization
60
+ autoSummarize: z.boolean().optional().default(true),
61
+ // Number of recent messages to keep after summarization
62
+ keepRecentMessages: z.number().optional().default(10)
63
+ }).optional().default({}),
64
+ // Server configuration
65
+ server: z.object({
66
+ port: z.number().default(3141),
67
+ host: z.string().default("127.0.0.1"),
68
+ // Public URL for web UI to connect to API (for Docker/remote access)
69
+ // If not set, defaults to http://{host}:{port}
70
+ publicUrl: z.string().url().optional()
71
+ }).default({ port: 3141, host: "127.0.0.1" }),
72
+ // Database path
73
+ databasePath: z.string().optional().default("./sparkecoder.db")
74
+ });
75
+ }
76
+ });
77
+
78
+ // src/skills/index.ts
79
+ var skills_exports = {};
80
+ __export(skills_exports, {
81
+ formatAgentsMdContent: () => formatAgentsMdContent,
82
+ formatAlwaysLoadedSkills: () => formatAlwaysLoadedSkills,
83
+ formatGlobMatchedSkills: () => formatGlobMatchedSkills,
84
+ formatSkillsForContext: () => formatSkillsForContext,
85
+ getGlobMatchedSkills: () => getGlobMatchedSkills,
86
+ loadAgentsMd: () => loadAgentsMd,
87
+ loadAllSkills: () => loadAllSkills,
88
+ loadAllSkillsFromDiscovered: () => loadAllSkillsFromDiscovered,
89
+ loadSkillContent: () => loadSkillContent,
90
+ loadSkillsFromDirectory: () => loadSkillsFromDirectory
91
+ });
92
+ import { readFile as readFile6, readdir } from "fs/promises";
93
+ import { resolve as resolve6, basename, extname as extname3, relative as relative4 } from "path";
94
+ import { existsSync as existsSync8 } from "fs";
95
+ import { minimatch } from "minimatch";
96
+ function parseSkillFrontmatter(content) {
97
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
98
+ if (!frontmatterMatch) {
99
+ return null;
100
+ }
101
+ const [, frontmatter, body] = frontmatterMatch;
102
+ try {
103
+ const lines = frontmatter.split("\n");
104
+ const data = {};
105
+ let currentArray = null;
106
+ let currentArrayKey = null;
107
+ for (const line of lines) {
108
+ if (currentArrayKey && line.trim().startsWith("-")) {
109
+ let value = line.trim().slice(1).trim();
110
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
111
+ value = value.slice(1, -1);
112
+ }
113
+ currentArray?.push(value);
114
+ continue;
115
+ }
116
+ if (currentArrayKey && currentArray) {
117
+ data[currentArrayKey] = currentArray;
118
+ currentArray = null;
119
+ currentArrayKey = null;
120
+ }
121
+ const colonIndex = line.indexOf(":");
122
+ if (colonIndex > 0) {
123
+ const key = line.slice(0, colonIndex).trim();
124
+ let value = line.slice(colonIndex + 1).trim();
125
+ if (value === "" || value === "[]") {
126
+ currentArrayKey = key;
127
+ currentArray = [];
128
+ continue;
129
+ }
130
+ if (value.startsWith("[") && value.endsWith("]")) {
131
+ const arrayContent = value.slice(1, -1);
132
+ const items = arrayContent.split(",").map((item) => {
133
+ let trimmed = item.trim();
134
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
135
+ trimmed = trimmed.slice(1, -1);
136
+ }
137
+ return trimmed;
138
+ }).filter((item) => item.length > 0);
139
+ data[key] = items;
140
+ continue;
141
+ }
142
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
143
+ value = value.slice(1, -1);
144
+ }
145
+ if (value === "true") {
146
+ data[key] = true;
147
+ } else if (value === "false") {
148
+ data[key] = false;
149
+ } else {
150
+ data[key] = value;
151
+ }
152
+ }
153
+ }
154
+ if (currentArrayKey && currentArray) {
155
+ data[currentArrayKey] = currentArray;
156
+ }
157
+ const metadata = SkillMetadataSchema.parse(data);
158
+ return { metadata, body: body.trim() };
159
+ } catch {
160
+ return null;
161
+ }
162
+ }
163
+ function getSkillNameFromPath(filePath) {
164
+ return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
165
+ }
166
+ async function loadSkillsFromDirectory(directory, options = {}) {
167
+ const {
168
+ priority = 50,
169
+ defaultLoadType = "on_demand",
170
+ forceAlwaysApply = false
171
+ } = options;
172
+ if (!existsSync8(directory)) {
173
+ return [];
174
+ }
175
+ const skills = [];
176
+ const entries = await readdir(directory, { withFileTypes: true });
177
+ for (const entry of entries) {
178
+ let filePath;
179
+ let fileName;
180
+ if (entry.isDirectory()) {
181
+ const skillMdPath = resolve6(directory, entry.name, "SKILL.md");
182
+ if (existsSync8(skillMdPath)) {
183
+ filePath = skillMdPath;
184
+ fileName = entry.name;
185
+ } else {
186
+ continue;
187
+ }
188
+ } else if (entry.name.endsWith(".md") || entry.name.endsWith(".mdc")) {
189
+ filePath = resolve6(directory, entry.name);
190
+ fileName = entry.name;
191
+ } else {
192
+ continue;
193
+ }
194
+ const content = await readFile6(filePath, "utf-8");
195
+ const parsed = parseSkillFrontmatter(content);
196
+ if (parsed) {
197
+ const alwaysApply = forceAlwaysApply || parsed.metadata.alwaysApply;
198
+ const loadType = alwaysApply ? "always" : defaultLoadType;
199
+ skills.push({
200
+ name: parsed.metadata.name,
201
+ description: parsed.metadata.description,
202
+ filePath,
203
+ alwaysApply,
204
+ globs: parsed.metadata.globs,
205
+ loadType,
206
+ priority,
207
+ sourceDir: directory
208
+ });
209
+ } else {
210
+ const name = getSkillNameFromPath(filePath);
211
+ const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
212
+ skills.push({
213
+ name,
214
+ description: firstParagraph.replace(/^#\s*/, "").trim(),
215
+ filePath,
216
+ alwaysApply: forceAlwaysApply,
217
+ globs: [],
218
+ loadType: forceAlwaysApply ? "always" : defaultLoadType,
219
+ priority,
220
+ sourceDir: directory
221
+ });
222
+ }
223
+ }
224
+ return skills;
225
+ }
226
+ async function loadAllSkills(directories) {
227
+ const allSkills = [];
228
+ const seenNames = /* @__PURE__ */ new Set();
229
+ for (const dir of directories) {
230
+ const skills = await loadSkillsFromDirectory(dir);
231
+ for (const skill of skills) {
232
+ if (!seenNames.has(skill.name.toLowerCase())) {
233
+ seenNames.add(skill.name.toLowerCase());
234
+ allSkills.push(skill);
235
+ }
236
+ }
237
+ }
238
+ return allSkills;
239
+ }
240
+ async function loadAllSkillsFromDiscovered(discovered) {
241
+ const allSkills = [];
242
+ const seenNames = /* @__PURE__ */ new Set();
243
+ for (const { path, priority } of discovered.alwaysLoadedDirs) {
244
+ const skills = await loadSkillsFromDirectory(path, {
245
+ priority,
246
+ defaultLoadType: "always",
247
+ forceAlwaysApply: true
248
+ });
249
+ for (const skill of skills) {
250
+ if (!seenNames.has(skill.name.toLowerCase())) {
251
+ seenNames.add(skill.name.toLowerCase());
252
+ allSkills.push(skill);
253
+ }
254
+ }
255
+ }
256
+ for (const { path, priority } of discovered.onDemandDirs) {
257
+ const skills = await loadSkillsFromDirectory(path, {
258
+ priority,
259
+ defaultLoadType: "on_demand",
260
+ forceAlwaysApply: false
261
+ });
262
+ for (const skill of skills) {
263
+ if (!seenNames.has(skill.name.toLowerCase())) {
264
+ seenNames.add(skill.name.toLowerCase());
265
+ allSkills.push(skill);
266
+ }
267
+ }
268
+ }
269
+ const alwaysSkills = allSkills.filter((s) => s.alwaysApply || s.loadType === "always");
270
+ const onDemandSkills = allSkills.filter((s) => !s.alwaysApply && s.loadType !== "always");
271
+ const alwaysWithContent = await Promise.all(
272
+ alwaysSkills.map(async (skill) => {
273
+ const content = await readFile6(skill.filePath, "utf-8");
274
+ const parsed = parseSkillFrontmatter(content);
275
+ return {
276
+ ...skill,
277
+ content: parsed ? parsed.body : content
278
+ };
279
+ })
280
+ );
281
+ return {
282
+ always: alwaysWithContent,
283
+ onDemand: onDemandSkills,
284
+ all: allSkills
285
+ };
286
+ }
287
+ async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
288
+ if (activeFiles.length === 0) {
289
+ return [];
290
+ }
291
+ const relativeFiles = activeFiles.map((f) => {
292
+ if (f.startsWith(workingDirectory)) {
293
+ return relative4(workingDirectory, f);
294
+ }
295
+ return f;
296
+ });
297
+ const matchedSkills = skills.filter((skill) => {
298
+ if (skill.alwaysApply || skill.loadType === "always") {
299
+ return false;
300
+ }
301
+ if (!skill.globs || skill.globs.length === 0) {
302
+ return false;
303
+ }
304
+ return relativeFiles.some(
305
+ (file) => skill.globs.some((pattern) => minimatch(file, pattern, { matchBase: true }))
306
+ );
307
+ });
308
+ const matchedWithContent = await Promise.all(
309
+ matchedSkills.map(async (skill) => {
310
+ const content = await readFile6(skill.filePath, "utf-8");
311
+ const parsed = parseSkillFrontmatter(content);
312
+ return {
313
+ ...skill,
314
+ content: parsed ? parsed.body : content,
315
+ loadType: "glob_matched"
316
+ };
317
+ })
318
+ );
319
+ return matchedWithContent;
320
+ }
321
+ async function loadAgentsMd(agentsMdPath) {
322
+ if (!agentsMdPath || !existsSync8(agentsMdPath)) {
323
+ return null;
324
+ }
325
+ const content = await readFile6(agentsMdPath, "utf-8");
326
+ return content;
327
+ }
328
+ async function loadSkillContent(skillName, directories) {
329
+ const allSkills = await loadAllSkills(directories);
330
+ const skill = allSkills.find(
331
+ (s) => s.name.toLowerCase() === skillName.toLowerCase()
332
+ );
333
+ if (!skill) {
334
+ return null;
335
+ }
336
+ const content = await readFile6(skill.filePath, "utf-8");
337
+ const parsed = parseSkillFrontmatter(content);
338
+ return {
339
+ ...skill,
340
+ content: parsed ? parsed.body : content
341
+ };
342
+ }
343
+ function formatSkillsForContext(skills) {
344
+ const onDemandSkills = skills.filter((s) => !s.alwaysApply && s.loadType !== "always");
345
+ if (onDemandSkills.length === 0) {
346
+ return "No on-demand skills available.";
347
+ }
348
+ const lines = ["Available skills (use load_skill tool to load into context):"];
349
+ for (const skill of onDemandSkills) {
350
+ const globInfo = skill.globs?.length ? ` [auto-loads for: ${skill.globs.join(", ")}]` : "";
351
+ lines.push(`- ${skill.name}: ${skill.description}${globInfo}`);
352
+ }
353
+ return lines.join("\n");
354
+ }
355
+ function formatAlwaysLoadedSkills(skills) {
356
+ if (skills.length === 0) {
357
+ return "";
358
+ }
359
+ const sections = [];
360
+ for (const skill of skills) {
361
+ sections.push(`### ${skill.name}
362
+
363
+ ${skill.content}`);
364
+ }
365
+ return `## Active Rules & Skills (Always Loaded)
366
+
367
+ ${sections.join("\n\n---\n\n")}`;
368
+ }
369
+ function formatGlobMatchedSkills(skills) {
370
+ if (skills.length === 0) {
371
+ return "";
372
+ }
373
+ const sections = [];
374
+ for (const skill of skills) {
375
+ sections.push(`### ${skill.name}
376
+
377
+ ${skill.content}`);
378
+ }
379
+ return `## Context-Relevant Skills (Auto-loaded based on active files)
380
+
381
+ ${sections.join("\n\n---\n\n")}`;
382
+ }
383
+ function formatAgentsMdContent(content) {
384
+ if (!content) {
385
+ return "";
386
+ }
387
+ return `## Project Instructions (AGENTS.md)
388
+
389
+ ${content}`;
390
+ }
391
+ var init_skills = __esm({
392
+ "src/skills/index.ts"() {
393
+ "use strict";
394
+ init_types();
395
+ }
396
+ });
397
+
8
398
  // src/cli.ts
9
399
  import { Command } from "commander";
10
400
  import chalk from "chalk";
@@ -18,19 +408,20 @@ import { Hono as Hono5 } from "hono";
18
408
  import { serve } from "@hono/node-server";
19
409
  import { cors } from "hono/cors";
20
410
  import { logger } from "hono/logger";
21
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
22
- import { resolve as resolve8, dirname as dirname6, join as join5 } from "path";
411
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
412
+ import { resolve as resolve9, dirname as dirname7, join as join7 } from "path";
23
413
  import { spawn as spawn2 } from "child_process";
24
414
  import { createServer as createNetServer } from "net";
25
- import { fileURLToPath as fileURLToPath2 } from "url";
415
+ import { fileURLToPath as fileURLToPath3 } from "url";
26
416
 
27
417
  // src/server/routes/sessions.ts
28
418
  import { Hono } from "hono";
29
419
  import { zValidator } from "@hono/zod-validator";
30
- import { z as z9 } from "zod";
31
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync, unlinkSync } from "fs";
32
- import { join as join3, basename as basename2, extname as extname5 } from "path";
33
- import { nanoid as nanoid4 } from "nanoid";
420
+ import { z as z11 } from "zod";
421
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync, unlinkSync } from "fs";
422
+ import { readdir as readdir4 } from "fs/promises";
423
+ import { join as join4, basename as basename2, extname as extname6, relative as relative7 } from "path";
424
+ import { nanoid as nanoid5 } from "nanoid";
34
425
 
35
426
  // src/db/index.ts
36
427
  import Database from "better-sqlite3";
@@ -47,6 +438,7 @@ __export(schema_exports, {
47
438
  loadedSkills: () => loadedSkills,
48
439
  messages: () => messages,
49
440
  sessions: () => sessions,
441
+ subagentExecutions: () => subagentExecutions,
50
442
  terminals: () => terminals,
51
443
  todoItems: () => todoItems,
52
444
  toolExecutions: () => toolExecutions
@@ -150,6 +542,26 @@ var fileBackups = sqliteTable("file_backups", {
150
542
  existed: integer("existed", { mode: "boolean" }).notNull().default(true),
151
543
  createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
152
544
  });
545
+ var subagentExecutions = sqliteTable("subagent_executions", {
546
+ id: text("id").primaryKey(),
547
+ sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
548
+ toolCallId: text("tool_call_id").notNull(),
549
+ // The tool call that spawned this subagent
550
+ subagentType: text("subagent_type").notNull(),
551
+ // e.g., 'search', 'analyze', etc.
552
+ task: text("task").notNull(),
553
+ // The task/query given to the subagent
554
+ model: text("model").notNull(),
555
+ // The model used (e.g., 'gemini-2.0-flash')
556
+ status: text("status", { enum: ["running", "completed", "error", "cancelled"] }).notNull().default("running"),
557
+ // Steps taken by the subagent (stored as JSON array)
558
+ steps: text("steps", { mode: "json" }).$type().default([]),
559
+ // Final result/output
560
+ result: text("result", { mode: "json" }),
561
+ error: text("error"),
562
+ startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
563
+ completedAt: integer("completed_at", { mode: "timestamp" })
564
+ });
153
565
 
154
566
  // src/db/index.ts
155
567
  var db = null;
@@ -254,6 +666,22 @@ function initDatabase(dbPath) {
254
666
  created_at INTEGER NOT NULL
255
667
  );
256
668
 
669
+ -- Subagent executions table - tracks subagent runs
670
+ CREATE TABLE IF NOT EXISTS subagent_executions (
671
+ id TEXT PRIMARY KEY,
672
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
673
+ tool_call_id TEXT NOT NULL,
674
+ subagent_type TEXT NOT NULL,
675
+ task TEXT NOT NULL,
676
+ model TEXT NOT NULL,
677
+ status TEXT NOT NULL DEFAULT 'running',
678
+ steps TEXT DEFAULT '[]',
679
+ result TEXT,
680
+ error TEXT,
681
+ started_at INTEGER NOT NULL,
682
+ completed_at INTEGER
683
+ );
684
+
257
685
  CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
258
686
  CREATE INDEX IF NOT EXISTS idx_tool_executions_session ON tool_executions(session_id);
259
687
  CREATE INDEX IF NOT EXISTS idx_todo_items_session ON todo_items(session_id);
@@ -263,6 +691,8 @@ function initDatabase(dbPath) {
263
691
  CREATE INDEX IF NOT EXISTS idx_checkpoints_session ON checkpoints(session_id);
264
692
  CREATE INDEX IF NOT EXISTS idx_file_backups_checkpoint ON file_backups(checkpoint_id);
265
693
  CREATE INDEX IF NOT EXISTS idx_file_backups_session ON file_backups(session_id);
694
+ CREATE INDEX IF NOT EXISTS idx_subagent_executions_session ON subagent_executions(session_id);
695
+ CREATE INDEX IF NOT EXISTS idx_subagent_executions_tool_call ON subagent_executions(tool_call_id);
266
696
  `);
267
697
  return db;
268
698
  }
@@ -656,85 +1086,149 @@ var fileBackupQueries = {
656
1086
  return result.changes;
657
1087
  }
658
1088
  };
1089
+ var subagentQueries = {
1090
+ create(data) {
1091
+ const id = nanoid();
1092
+ const result = getDb().insert(subagentExecutions).values({
1093
+ id,
1094
+ sessionId: data.sessionId,
1095
+ toolCallId: data.toolCallId,
1096
+ subagentType: data.subagentType,
1097
+ task: data.task,
1098
+ model: data.model,
1099
+ status: "running",
1100
+ steps: [],
1101
+ startedAt: /* @__PURE__ */ new Date()
1102
+ }).returning().get();
1103
+ return result;
1104
+ },
1105
+ getById(id) {
1106
+ return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.id, id)).get();
1107
+ },
1108
+ getByToolCallId(toolCallId) {
1109
+ return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.toolCallId, toolCallId)).get();
1110
+ },
1111
+ getBySession(sessionId) {
1112
+ return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).orderBy(desc(subagentExecutions.startedAt)).all();
1113
+ },
1114
+ addStep(id, step) {
1115
+ const existing = this.getById(id);
1116
+ if (!existing) return void 0;
1117
+ const currentSteps = existing.steps || [];
1118
+ const newSteps = [...currentSteps, step];
1119
+ return getDb().update(subagentExecutions).set({ steps: newSteps }).where(eq(subagentExecutions.id, id)).returning().get();
1120
+ },
1121
+ complete(id, result) {
1122
+ return getDb().update(subagentExecutions).set({
1123
+ status: "completed",
1124
+ result,
1125
+ completedAt: /* @__PURE__ */ new Date()
1126
+ }).where(eq(subagentExecutions.id, id)).returning().get();
1127
+ },
1128
+ markError(id, error) {
1129
+ return getDb().update(subagentExecutions).set({
1130
+ status: "error",
1131
+ error,
1132
+ completedAt: /* @__PURE__ */ new Date()
1133
+ }).where(eq(subagentExecutions.id, id)).returning().get();
1134
+ },
1135
+ cancel(id) {
1136
+ return getDb().update(subagentExecutions).set({
1137
+ status: "cancelled",
1138
+ completedAt: /* @__PURE__ */ new Date()
1139
+ }).where(eq(subagentExecutions.id, id)).returning().get();
1140
+ },
1141
+ deleteBySession(sessionId) {
1142
+ const result = getDb().delete(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).run();
1143
+ return result.changes;
1144
+ }
1145
+ };
659
1146
 
660
1147
  // src/agent/index.ts
661
1148
  import {
662
- streamText,
663
- generateText as generateText2,
664
- tool as tool7,
665
- stepCountIs
1149
+ streamText as streamText2,
1150
+ generateText as generateText3,
1151
+ tool as tool9,
1152
+ stepCountIs as stepCountIs2
666
1153
  } from "ai";
667
- import { gateway as gateway2 } from "@ai-sdk/gateway";
668
- import { z as z8 } from "zod";
669
- import { nanoid as nanoid3 } from "nanoid";
1154
+
1155
+ // src/agent/model.ts
1156
+ import { gateway } from "@ai-sdk/gateway";
1157
+ var ANTHROPIC_PREFIX = "anthropic/";
1158
+ function isAnthropicModel(modelId) {
1159
+ const normalized = modelId.trim().toLowerCase();
1160
+ return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
1161
+ }
1162
+ function resolveModel(modelId) {
1163
+ return gateway(modelId.trim());
1164
+ }
1165
+ var SUBAGENT_MODELS = {
1166
+ search: "google/gemini-2.0-flash",
1167
+ analyze: "google/gemini-2.0-flash",
1168
+ default: "google/gemini-2.0-flash"
1169
+ };
1170
+
1171
+ // src/agent/index.ts
1172
+ import { z as z10 } from "zod";
1173
+ import { nanoid as nanoid4 } from "nanoid";
670
1174
 
671
1175
  // src/config/index.ts
1176
+ init_types();
1177
+ init_types();
672
1178
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
673
1179
  import { resolve, dirname, join } from "path";
674
1180
  import { homedir, platform } from "os";
675
-
676
- // src/config/types.ts
677
- import { z } from "zod";
678
- var ToolApprovalConfigSchema = z.object({
679
- bash: z.boolean().optional().default(true),
680
- write_file: z.boolean().optional().default(false),
681
- read_file: z.boolean().optional().default(false),
682
- load_skill: z.boolean().optional().default(false),
683
- todo: z.boolean().optional().default(false)
684
- });
685
- var SkillMetadataSchema = z.object({
686
- name: z.string(),
687
- description: z.string()
688
- });
689
- var SessionConfigSchema = z.object({
690
- toolApprovals: z.record(z.string(), z.boolean()).optional(),
691
- approvalWebhook: z.string().url().optional(),
692
- skillsDirectory: z.string().optional(),
693
- maxContextChars: z.number().optional().default(2e5)
694
- });
695
- var SparkcoderConfigSchema = z.object({
696
- // Default model to use (Vercel AI Gateway format)
697
- defaultModel: z.string().default("anthropic/claude-opus-4-5"),
698
- // Working directory for file operations
699
- workingDirectory: z.string().optional(),
700
- // Tool approval settings
701
- toolApprovals: ToolApprovalConfigSchema.optional().default({}),
702
- // Approval webhook URL (called when approval is needed)
703
- approvalWebhook: z.string().url().optional(),
704
- // Skills configuration
705
- skills: z.object({
706
- // Directory containing skill files
707
- directory: z.string().optional().default("./skills"),
708
- // Additional skill directories to include
709
- additionalDirectories: z.array(z.string()).optional().default([])
710
- }).optional().default({}),
711
- // Context management
712
- context: z.object({
713
- // Maximum context size before summarization (in characters)
714
- maxChars: z.number().optional().default(2e5),
715
- // Enable automatic summarization
716
- autoSummarize: z.boolean().optional().default(true),
717
- // Number of recent messages to keep after summarization
718
- keepRecentMessages: z.number().optional().default(10)
719
- }).optional().default({}),
720
- // Server configuration
721
- server: z.object({
722
- port: z.number().default(3141),
723
- host: z.string().default("127.0.0.1"),
724
- // Public URL for web UI to connect to API (for Docker/remote access)
725
- // If not set, defaults to http://{host}:{port}
726
- publicUrl: z.string().url().optional()
727
- }).default({ port: 3141, host: "127.0.0.1" }),
728
- // Database path
729
- databasePath: z.string().optional().default("./sparkecoder.db")
730
- });
731
-
732
- // src/config/index.ts
733
1181
  var CONFIG_FILE_NAMES = [
734
1182
  "sparkecoder.config.json",
735
1183
  "sparkecoder.json",
736
1184
  ".sparkecoder.json"
737
1185
  ];
1186
+ function discoverSkillDirectories(workingDir) {
1187
+ const alwaysLoadedDirs = [];
1188
+ const onDemandDirs = [];
1189
+ const allDirectories = [];
1190
+ let agentsMdPath = null;
1191
+ const sparkRulesDir = join(workingDir, ".sparkecoder", "rules");
1192
+ if (existsSync(sparkRulesDir)) {
1193
+ alwaysLoadedDirs.push({ path: sparkRulesDir, priority: 1 });
1194
+ allDirectories.push(sparkRulesDir);
1195
+ }
1196
+ const sparkSkillsDir = join(workingDir, ".sparkecoder", "skills");
1197
+ if (existsSync(sparkSkillsDir)) {
1198
+ onDemandDirs.push({ path: sparkSkillsDir, priority: 2 });
1199
+ allDirectories.push(sparkSkillsDir);
1200
+ }
1201
+ const cursorRulesDir = join(workingDir, ".cursor", "rules");
1202
+ if (existsSync(cursorRulesDir)) {
1203
+ onDemandDirs.push({ path: cursorRulesDir, priority: 3 });
1204
+ allDirectories.push(cursorRulesDir);
1205
+ }
1206
+ const claudeSkillsDir = join(workingDir, ".claude", "skills");
1207
+ if (existsSync(claudeSkillsDir)) {
1208
+ onDemandDirs.push({ path: claudeSkillsDir, priority: 4 });
1209
+ allDirectories.push(claudeSkillsDir);
1210
+ }
1211
+ const legacySkillsDir = join(workingDir, "skills");
1212
+ if (existsSync(legacySkillsDir)) {
1213
+ onDemandDirs.push({ path: legacySkillsDir, priority: 5 });
1214
+ allDirectories.push(legacySkillsDir);
1215
+ }
1216
+ const agentsMd = join(workingDir, "AGENTS.md");
1217
+ if (existsSync(agentsMd)) {
1218
+ agentsMdPath = agentsMd;
1219
+ }
1220
+ const builtInSkillsDir = resolve(dirname(import.meta.url.replace("file://", "")), "../skills/default");
1221
+ if (existsSync(builtInSkillsDir)) {
1222
+ onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
1223
+ allDirectories.push(builtInSkillsDir);
1224
+ }
1225
+ return {
1226
+ alwaysLoadedDirs,
1227
+ onDemandDirs,
1228
+ agentsMdPath,
1229
+ allDirectories
1230
+ };
1231
+ }
738
1232
  function getAppDataDirectory() {
739
1233
  const appName = "sparkecoder";
740
1234
  switch (platform()) {
@@ -814,20 +1308,12 @@ function loadConfig(configPath, workingDirectory) {
814
1308
  } else {
815
1309
  resolvedWorkingDirectory = process.cwd();
816
1310
  }
1311
+ const discovered = discoverSkillDirectories(resolvedWorkingDirectory);
1312
+ const additionalDirs = (config.skills?.additionalDirectories || []).map((dir) => resolve(configDir, dir)).filter((dir) => existsSync(dir));
817
1313
  const resolvedSkillsDirectories = [
818
- resolve(configDir, config.skills?.directory || "./skills"),
819
- // Built-in skills
820
- resolve(dirname(import.meta.url.replace("file://", "")), "../skills/default"),
821
- ...(config.skills?.additionalDirectories || []).map(
822
- (dir) => resolve(configDir, dir)
823
- )
824
- ].filter((dir) => {
825
- try {
826
- return existsSync(dir);
827
- } catch {
828
- return false;
829
- }
830
- });
1314
+ ...discovered.allDirectories,
1315
+ ...additionalDirs
1316
+ ];
831
1317
  let resolvedDatabasePath;
832
1318
  if (config.databasePath && config.databasePath !== "./sparkecoder.db") {
833
1319
  resolvedDatabasePath = resolve(configDir, config.databasePath);
@@ -844,7 +1330,8 @@ function loadConfig(configPath, workingDirectory) {
844
1330
  },
845
1331
  resolvedWorkingDirectory,
846
1332
  resolvedSkillsDirectories,
847
- resolvedDatabasePath
1333
+ resolvedDatabasePath,
1334
+ discoveredSkills: discovered
848
1335
  };
849
1336
  cachedConfig = resolved;
850
1337
  return resolved;
@@ -1272,8 +1759,8 @@ async function listSessionTerminals(sessionId, workingDirectory) {
1272
1759
  const terminalsDir = join2(workingDirectory, LOG_BASE_DIR, sessionId, "terminals");
1273
1760
  const terminals3 = [];
1274
1761
  try {
1275
- const { readdir: readdir3 } = await import("fs/promises");
1276
- const entries = await readdir3(terminalsDir, { withFileTypes: true });
1762
+ const { readdir: readdir5 } = await import("fs/promises");
1763
+ const entries = await readdir5(terminalsDir, { withFileTypes: true });
1277
1764
  for (const entry of entries) {
1278
1765
  if (entry.isDirectory()) {
1279
1766
  const meta = await getMeta(entry.name, workingDirectory, sessionId);
@@ -1877,12 +2364,12 @@ function findNearestRoot(startDir, markers) {
1877
2364
  }
1878
2365
  async function commandExists(cmd) {
1879
2366
  try {
1880
- const { exec: exec5 } = await import("child_process");
1881
- const { promisify: promisify5 } = await import("util");
1882
- const execAsync5 = promisify5(exec5);
2367
+ const { exec: exec6 } = await import("child_process");
2368
+ const { promisify: promisify6 } = await import("util");
2369
+ const execAsync6 = promisify6(exec6);
1883
2370
  const isWindows = process.platform === "win32";
1884
2371
  const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
1885
- await execAsync5(checkCmd);
2372
+ await execAsync6(checkCmd);
1886
2373
  return true;
1887
2374
  } catch {
1888
2375
  return false;
@@ -2172,7 +2659,7 @@ async function createClient(serverId, handle, root) {
2172
2659
  },
2173
2660
  async waitForDiagnostics(filePath, timeoutMs = 5e3) {
2174
2661
  const normalized = normalizePath(filePath);
2175
- return new Promise((resolve10) => {
2662
+ return new Promise((resolve11) => {
2176
2663
  const startTime = Date.now();
2177
2664
  let debounceTimer;
2178
2665
  let resolved = false;
@@ -2191,7 +2678,7 @@ async function createClient(serverId, handle, root) {
2191
2678
  if (resolved) return;
2192
2679
  resolved = true;
2193
2680
  cleanup();
2194
- resolve10(diagnostics.get(normalized) || []);
2681
+ resolve11(diagnostics.get(normalized) || []);
2195
2682
  };
2196
2683
  const onDiagnostic = () => {
2197
2684
  if (debounceTimer) clearTimeout(debounceTimer);
@@ -2349,6 +2836,7 @@ function isSupported(filePath) {
2349
2836
  }
2350
2837
 
2351
2838
  // src/tools/write-file.ts
2839
+ var MAX_PROGRESS_CHUNK_SIZE = 16 * 1024;
2352
2840
  var writeFileInputSchema = z4.object({
2353
2841
  path: z4.string().describe("The path to the file. Can be relative to working directory or absolute."),
2354
2842
  mode: z4.enum(["full", "str_replace"]).describe('Write mode: "full" for complete file write, "str_replace" for targeted string replacement'),
@@ -2392,24 +2880,76 @@ Working directory: ${options.workingDirectory}`,
2392
2880
  error: 'Content is required for "full" mode'
2393
2881
  };
2394
2882
  }
2883
+ const existed = existsSync7(absolutePath);
2884
+ const action = existed ? "replaced" : "created";
2885
+ console.log("[WRITE-FILE] onProgress callback exists:", !!options.onProgress);
2886
+ console.log("[WRITE-FILE] Emitting started event for:", relativePath);
2887
+ options.onProgress?.({
2888
+ path: absolutePath,
2889
+ relativePath,
2890
+ mode: "full",
2891
+ status: "started",
2892
+ action,
2893
+ totalLength: content.length
2894
+ });
2895
+ if (content.length <= MAX_PROGRESS_CHUNK_SIZE) {
2896
+ options.onProgress?.({
2897
+ path: absolutePath,
2898
+ relativePath,
2899
+ mode: "full",
2900
+ status: "content",
2901
+ content,
2902
+ action,
2903
+ totalLength: content.length
2904
+ });
2905
+ } else {
2906
+ const chunkCount = Math.ceil(content.length / MAX_PROGRESS_CHUNK_SIZE);
2907
+ for (let i = 0; i < chunkCount; i += 1) {
2908
+ const chunkStart = i * MAX_PROGRESS_CHUNK_SIZE;
2909
+ const chunk = content.slice(chunkStart, chunkStart + MAX_PROGRESS_CHUNK_SIZE);
2910
+ options.onProgress?.({
2911
+ path: absolutePath,
2912
+ relativePath,
2913
+ mode: "full",
2914
+ status: "content",
2915
+ content: chunk,
2916
+ action,
2917
+ totalLength: content.length,
2918
+ chunkIndex: i,
2919
+ chunkCount,
2920
+ chunkStart,
2921
+ isChunked: true
2922
+ });
2923
+ if (chunkCount > 1) {
2924
+ await new Promise((resolve11) => setTimeout(resolve11, 0));
2925
+ }
2926
+ }
2927
+ }
2395
2928
  await backupFile(options.sessionId, options.workingDirectory, absolutePath);
2396
2929
  const dir = dirname5(absolutePath);
2397
2930
  if (!existsSync7(dir)) {
2398
2931
  await mkdir3(dir, { recursive: true });
2399
2932
  }
2400
- const existed = existsSync7(absolutePath);
2401
2933
  await writeFile3(absolutePath, content, "utf-8");
2402
2934
  let diagnosticsOutput = "";
2403
2935
  if (options.enableLSP !== false && isSupported(absolutePath)) {
2404
2936
  await touchFile(absolutePath, true);
2405
2937
  diagnosticsOutput = await formatDiagnosticsOutput(absolutePath);
2406
2938
  }
2939
+ options.onProgress?.({
2940
+ path: absolutePath,
2941
+ relativePath,
2942
+ mode: "full",
2943
+ status: "completed",
2944
+ action,
2945
+ totalLength: content.length
2946
+ });
2407
2947
  return {
2408
2948
  success: true,
2409
2949
  path: absolutePath,
2410
- relativePath: relative3(options.workingDirectory, absolutePath),
2950
+ relativePath,
2411
2951
  mode: "full",
2412
- action: existed ? "replaced" : "created",
2952
+ action,
2413
2953
  bytesWritten: Buffer.byteLength(content, "utf-8"),
2414
2954
  lineCount: content.split("\n").length,
2415
2955
  ...diagnosticsOutput && { diagnostics: diagnosticsOutput }
@@ -2427,6 +2967,22 @@ Working directory: ${options.workingDirectory}`,
2427
2967
  error: `File not found: ${path}. Use "full" mode to create new files.`
2428
2968
  };
2429
2969
  }
2970
+ options.onProgress?.({
2971
+ path: absolutePath,
2972
+ relativePath,
2973
+ mode: "str_replace",
2974
+ status: "started",
2975
+ action: "edited"
2976
+ });
2977
+ options.onProgress?.({
2978
+ path: absolutePath,
2979
+ relativePath,
2980
+ mode: "str_replace",
2981
+ status: "content",
2982
+ oldString: old_string,
2983
+ newString: new_string,
2984
+ action: "edited"
2985
+ });
2430
2986
  await backupFile(options.sessionId, options.workingDirectory, absolutePath);
2431
2987
  const currentContent = await readFile5(absolutePath, "utf-8");
2432
2988
  if (!currentContent.includes(old_string)) {
@@ -2457,10 +3013,17 @@ Working directory: ${options.workingDirectory}`,
2457
3013
  await touchFile(absolutePath, true);
2458
3014
  diagnosticsOutput = await formatDiagnosticsOutput(absolutePath);
2459
3015
  }
3016
+ options.onProgress?.({
3017
+ path: absolutePath,
3018
+ relativePath,
3019
+ mode: "str_replace",
3020
+ status: "completed",
3021
+ action: "edited"
3022
+ });
2460
3023
  return {
2461
3024
  success: true,
2462
3025
  path: absolutePath,
2463
- relativePath: relative3(options.workingDirectory, absolutePath),
3026
+ relativePath,
2464
3027
  mode: "str_replace",
2465
3028
  linesRemoved: oldLines,
2466
3029
  linesAdded: newLines,
@@ -2608,112 +3171,9 @@ function formatTodoItem(item) {
2608
3171
  }
2609
3172
 
2610
3173
  // src/tools/load-skill.ts
3174
+ init_skills();
2611
3175
  import { tool as tool5 } from "ai";
2612
3176
  import { z as z6 } from "zod";
2613
-
2614
- // src/skills/index.ts
2615
- import { readFile as readFile6, readdir } from "fs/promises";
2616
- import { resolve as resolve6, basename, extname as extname3 } from "path";
2617
- import { existsSync as existsSync8 } from "fs";
2618
- function parseSkillFrontmatter(content) {
2619
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
2620
- if (!frontmatterMatch) {
2621
- return null;
2622
- }
2623
- const [, frontmatter, body] = frontmatterMatch;
2624
- try {
2625
- const lines = frontmatter.split("\n");
2626
- const data = {};
2627
- for (const line of lines) {
2628
- const colonIndex = line.indexOf(":");
2629
- if (colonIndex > 0) {
2630
- const key = line.slice(0, colonIndex).trim();
2631
- let value = line.slice(colonIndex + 1).trim();
2632
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
2633
- value = value.slice(1, -1);
2634
- }
2635
- data[key] = value;
2636
- }
2637
- }
2638
- const metadata = SkillMetadataSchema.parse(data);
2639
- return { metadata, body: body.trim() };
2640
- } catch {
2641
- return null;
2642
- }
2643
- }
2644
- function getSkillNameFromPath(filePath) {
2645
- return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2646
- }
2647
- async function loadSkillsFromDirectory(directory) {
2648
- if (!existsSync8(directory)) {
2649
- return [];
2650
- }
2651
- const skills = [];
2652
- const files = await readdir(directory);
2653
- for (const file of files) {
2654
- if (!file.endsWith(".md")) continue;
2655
- const filePath = resolve6(directory, file);
2656
- const content = await readFile6(filePath, "utf-8");
2657
- const parsed = parseSkillFrontmatter(content);
2658
- if (parsed) {
2659
- skills.push({
2660
- name: parsed.metadata.name,
2661
- description: parsed.metadata.description,
2662
- filePath
2663
- });
2664
- } else {
2665
- const name = getSkillNameFromPath(filePath);
2666
- const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
2667
- skills.push({
2668
- name,
2669
- description: firstParagraph.replace(/^#\s*/, "").trim(),
2670
- filePath
2671
- });
2672
- }
2673
- }
2674
- return skills;
2675
- }
2676
- async function loadAllSkills(directories) {
2677
- const allSkills = [];
2678
- const seenNames = /* @__PURE__ */ new Set();
2679
- for (const dir of directories) {
2680
- const skills = await loadSkillsFromDirectory(dir);
2681
- for (const skill of skills) {
2682
- if (!seenNames.has(skill.name.toLowerCase())) {
2683
- seenNames.add(skill.name.toLowerCase());
2684
- allSkills.push(skill);
2685
- }
2686
- }
2687
- }
2688
- return allSkills;
2689
- }
2690
- async function loadSkillContent(skillName, directories) {
2691
- const allSkills = await loadAllSkills(directories);
2692
- const skill = allSkills.find(
2693
- (s) => s.name.toLowerCase() === skillName.toLowerCase()
2694
- );
2695
- if (!skill) {
2696
- return null;
2697
- }
2698
- const content = await readFile6(skill.filePath, "utf-8");
2699
- const parsed = parseSkillFrontmatter(content);
2700
- return {
2701
- ...skill,
2702
- content: parsed ? parsed.body : content
2703
- };
2704
- }
2705
- function formatSkillsForContext(skills) {
2706
- if (skills.length === 0) {
2707
- return "No skills available.";
2708
- }
2709
- const lines = ["Available skills (use load_skill tool to load into context):"];
2710
- for (const skill of skills) {
2711
- lines.push(`- ${skill.name}: ${skill.description}`);
2712
- }
2713
- return lines.join("\n");
2714
- }
2715
-
2716
- // src/tools/load-skill.ts
2717
3177
  var loadSkillInputSchema = z6.object({
2718
3178
  action: z6.enum(["list", "load"]).describe('Action to perform: "list" to see available skills, "load" to load a skill'),
2719
3179
  skillName: z6.string().optional().describe('For "load" action: The name of the skill to load')
@@ -2796,7 +3256,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
2796
3256
  // src/tools/linter.ts
2797
3257
  import { tool as tool6 } from "ai";
2798
3258
  import { z as z7 } from "zod";
2799
- import { resolve as resolve7, relative as relative4, isAbsolute as isAbsolute3, extname as extname4 } from "path";
3259
+ import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname4 } from "path";
2800
3260
  import { existsSync as existsSync9 } from "fs";
2801
3261
  import { readdir as readdir2, stat as stat2 } from "fs/promises";
2802
3262
  var linterInputSchema = z7.object({
@@ -2868,37 +3328,730 @@ Working directory: ${options.workingDirectory}`,
2868
3328
  if (!existsSync9(absolutePath)) {
2869
3329
  continue;
2870
3330
  }
2871
- const stats = await stat2(absolutePath);
2872
- if (stats.isDirectory()) {
2873
- const dirFiles = await findSupportedFiles(absolutePath, options.workingDirectory);
2874
- filesToCheck.push(...dirFiles);
2875
- } else if (stats.isFile()) {
2876
- if (isSupported(absolutePath)) {
2877
- filesToCheck.push(absolutePath);
3331
+ const stats = await stat2(absolutePath);
3332
+ if (stats.isDirectory()) {
3333
+ const dirFiles = await findSupportedFiles(absolutePath, options.workingDirectory);
3334
+ filesToCheck.push(...dirFiles);
3335
+ } else if (stats.isFile()) {
3336
+ if (isSupported(absolutePath)) {
3337
+ filesToCheck.push(absolutePath);
3338
+ }
3339
+ }
3340
+ }
3341
+ if (filesToCheck.length === 0) {
3342
+ return {
3343
+ success: true,
3344
+ message: "No supported files found to check. Supported extensions: " + getSupportedExtensions().join(", "),
3345
+ files: [],
3346
+ totalErrors: 0,
3347
+ totalWarnings: 0
3348
+ };
3349
+ }
3350
+ await Promise.all(
3351
+ filesToCheck.map((file) => touchFile(file, true))
3352
+ );
3353
+ const diagnosticsMap = {};
3354
+ for (const file of filesToCheck) {
3355
+ const diagnostics = await getDiagnostics(file);
3356
+ if (diagnostics.length > 0) {
3357
+ diagnosticsMap[file] = diagnostics;
3358
+ }
3359
+ }
3360
+ return formatDiagnosticsResult(diagnosticsMap, options.workingDirectory);
3361
+ } catch (error) {
3362
+ return {
3363
+ success: false,
3364
+ error: error.message
3365
+ };
3366
+ }
3367
+ }
3368
+ });
3369
+ }
3370
+ function formatDiagnosticsResult(diagnosticsMap, workingDirectory) {
3371
+ let totalErrors = 0;
3372
+ let totalWarnings = 0;
3373
+ let totalInfo = 0;
3374
+ const files = [];
3375
+ for (const [filePath, diagnostics] of Object.entries(diagnosticsMap)) {
3376
+ const relativePath = relative5(workingDirectory, filePath);
3377
+ let fileErrors = 0;
3378
+ let fileWarnings = 0;
3379
+ const formattedDiagnostics = diagnostics.map((d) => {
3380
+ const severity = getSeverityString(d.severity);
3381
+ if (d.severity === 1 /* Error */) {
3382
+ fileErrors++;
3383
+ totalErrors++;
3384
+ } else if (d.severity === 2 /* Warning */) {
3385
+ fileWarnings++;
3386
+ totalWarnings++;
3387
+ } else {
3388
+ totalInfo++;
3389
+ }
3390
+ return {
3391
+ severity,
3392
+ line: d.range.start.line + 1,
3393
+ column: d.range.start.character + 1,
3394
+ message: d.message,
3395
+ source: d.source,
3396
+ code: d.code
3397
+ };
3398
+ });
3399
+ files.push({
3400
+ path: filePath,
3401
+ relativePath,
3402
+ errors: fileErrors,
3403
+ warnings: fileWarnings,
3404
+ diagnostics: formattedDiagnostics
3405
+ });
3406
+ }
3407
+ files.sort((a, b) => b.errors - a.errors);
3408
+ const hasIssues = totalErrors > 0 || totalWarnings > 0;
3409
+ return {
3410
+ success: true,
3411
+ message: hasIssues ? `Found ${totalErrors} error(s) and ${totalWarnings} warning(s) in ${files.length} file(s).` : `No lint errors found in ${Object.keys(diagnosticsMap).length || "any"} file(s).`,
3412
+ files,
3413
+ totalErrors,
3414
+ totalWarnings,
3415
+ totalInfo,
3416
+ summary: hasIssues ? formatSummary(files) : void 0
3417
+ };
3418
+ }
3419
+ function getSeverityString(severity) {
3420
+ switch (severity) {
3421
+ case 1 /* Error */:
3422
+ return "error";
3423
+ case 2 /* Warning */:
3424
+ return "warning";
3425
+ case 3 /* Information */:
3426
+ return "info";
3427
+ case 4 /* Hint */:
3428
+ return "hint";
3429
+ default:
3430
+ return "error";
3431
+ }
3432
+ }
3433
+ function formatSummary(files) {
3434
+ const lines = [];
3435
+ for (const file of files) {
3436
+ lines.push(`
3437
+ ${file.relativePath}:`);
3438
+ for (const d of file.diagnostics.slice(0, 10)) {
3439
+ const prefix = d.severity === "error" ? "\u274C" : d.severity === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
3440
+ lines.push(` ${prefix} [${d.line}:${d.column}] ${d.message}`);
3441
+ }
3442
+ if (file.diagnostics.length > 10) {
3443
+ lines.push(` ... and ${file.diagnostics.length - 10} more`);
3444
+ }
3445
+ }
3446
+ return lines.join("\n");
3447
+ }
3448
+
3449
+ // src/tools/search.ts
3450
+ import { tool as tool8 } from "ai";
3451
+ import { z as z9 } from "zod";
3452
+
3453
+ // src/agent/subagent.ts
3454
+ import {
3455
+ generateText,
3456
+ stepCountIs
3457
+ } from "ai";
3458
+ import { nanoid as nanoid3 } from "nanoid";
3459
+ var Subagent = class {
3460
+ /** Model to use (defaults to gemini-2.0-flash) */
3461
+ model;
3462
+ /** Maximum steps before stopping */
3463
+ maxSteps = 20;
3464
+ constructor(model) {
3465
+ this.model = model || SUBAGENT_MODELS.default;
3466
+ }
3467
+ /**
3468
+ * Parse the final result from the subagent's output.
3469
+ * Override this to structure the result for your subagent type.
3470
+ */
3471
+ parseResult(text2, steps) {
3472
+ return { text: text2, steps };
3473
+ }
3474
+ /**
3475
+ * Run the subagent with streaming progress updates
3476
+ */
3477
+ async run(options) {
3478
+ const { task, sessionId, toolCallId, onProgress, abortSignal } = options;
3479
+ const steps = [];
3480
+ const execution = subagentQueries.create({
3481
+ sessionId,
3482
+ toolCallId,
3483
+ subagentType: this.type,
3484
+ task,
3485
+ model: this.model
3486
+ });
3487
+ const addStep = async (step) => {
3488
+ const fullStep = {
3489
+ id: nanoid3(8),
3490
+ timestamp: Date.now(),
3491
+ ...step
3492
+ };
3493
+ steps.push(fullStep);
3494
+ subagentQueries.addStep(execution.id, fullStep);
3495
+ await onProgress?.({
3496
+ type: "step",
3497
+ subagentId: execution.id,
3498
+ subagentType: this.type,
3499
+ step: fullStep
3500
+ });
3501
+ };
3502
+ try {
3503
+ const tools = this.getTools(options);
3504
+ const systemPrompt = this.getSystemPrompt(options);
3505
+ const result = await generateText({
3506
+ model: resolveModel(this.model),
3507
+ system: systemPrompt,
3508
+ messages: [
3509
+ { role: "user", content: task }
3510
+ ],
3511
+ tools,
3512
+ stopWhen: stepCountIs(this.maxSteps),
3513
+ abortSignal,
3514
+ onStepFinish: async (step) => {
3515
+ if (step.text) {
3516
+ await addStep({
3517
+ type: "text",
3518
+ content: step.text
3519
+ });
3520
+ await onProgress?.({
3521
+ type: "text",
3522
+ subagentId: execution.id,
3523
+ subagentType: this.type,
3524
+ text: step.text
3525
+ });
3526
+ }
3527
+ if (step.toolCalls) {
3528
+ for (const toolCall of step.toolCalls) {
3529
+ await addStep({
3530
+ type: "tool_call",
3531
+ content: `Calling ${toolCall.toolName}`,
3532
+ toolName: toolCall.toolName,
3533
+ toolInput: toolCall.input
3534
+ });
3535
+ await onProgress?.({
3536
+ type: "tool_call",
3537
+ subagentId: execution.id,
3538
+ subagentType: this.type,
3539
+ toolName: toolCall.toolName,
3540
+ toolInput: toolCall.input
3541
+ });
3542
+ }
3543
+ }
3544
+ if (step.toolResults) {
3545
+ for (const toolResult of step.toolResults) {
3546
+ await addStep({
3547
+ type: "tool_result",
3548
+ content: `Result from ${toolResult.toolName}`,
3549
+ toolName: toolResult.toolName,
3550
+ toolOutput: toolResult.output
3551
+ });
3552
+ await onProgress?.({
3553
+ type: "tool_result",
3554
+ subagentId: execution.id,
3555
+ subagentType: this.type,
3556
+ toolName: toolResult.toolName,
3557
+ toolOutput: toolResult.output
3558
+ });
3559
+ }
3560
+ }
3561
+ }
3562
+ });
3563
+ const parsedResult = this.parseResult(result.text, steps);
3564
+ subagentQueries.complete(execution.id, parsedResult);
3565
+ await onProgress?.({
3566
+ type: "complete",
3567
+ subagentId: execution.id,
3568
+ subagentType: this.type,
3569
+ result: parsedResult
3570
+ });
3571
+ return {
3572
+ success: true,
3573
+ result: parsedResult,
3574
+ steps,
3575
+ executionId: execution.id
3576
+ };
3577
+ } catch (error) {
3578
+ const errorMessage = error.message || "Unknown error";
3579
+ subagentQueries.markError(execution.id, errorMessage);
3580
+ await onProgress?.({
3581
+ type: "error",
3582
+ subagentId: execution.id,
3583
+ subagentType: this.type,
3584
+ error: errorMessage
3585
+ });
3586
+ return {
3587
+ success: false,
3588
+ error: errorMessage,
3589
+ steps,
3590
+ executionId: execution.id
3591
+ };
3592
+ }
3593
+ }
3594
+ /**
3595
+ * Run with streaming (for real-time progress in UI)
3596
+ */
3597
+ async *stream(options) {
3598
+ const events = [];
3599
+ let resolveNext = null;
3600
+ let done = false;
3601
+ const eventQueue = [];
3602
+ const runPromise = this.run({
3603
+ ...options,
3604
+ onProgress: async (event) => {
3605
+ eventQueue.push(event);
3606
+ if (resolveNext) {
3607
+ resolveNext(eventQueue.shift());
3608
+ resolveNext = null;
3609
+ }
3610
+ }
3611
+ }).then((result) => {
3612
+ done = true;
3613
+ if (resolveNext) {
3614
+ resolveNext(null);
3615
+ }
3616
+ return result;
3617
+ });
3618
+ while (!done || eventQueue.length > 0) {
3619
+ if (eventQueue.length > 0) {
3620
+ yield eventQueue.shift();
3621
+ } else if (!done) {
3622
+ const event = await new Promise((resolve11) => {
3623
+ resolveNext = resolve11;
3624
+ });
3625
+ if (event) {
3626
+ yield event;
3627
+ }
3628
+ }
3629
+ }
3630
+ await runPromise;
3631
+ }
3632
+ };
3633
+
3634
+ // src/agent/subagents/search.ts
3635
+ import { tool as tool7 } from "ai";
3636
+ import { z as z8 } from "zod";
3637
+ import { exec as exec4 } from "child_process";
3638
+ import { promisify as promisify4 } from "util";
3639
+ import { readFile as readFile7, stat as stat3, readdir as readdir3 } from "fs/promises";
3640
+ import { resolve as resolve8, relative as relative6, isAbsolute as isAbsolute4 } from "path";
3641
+ import { existsSync as existsSync10 } from "fs";
3642
+ var execAsync4 = promisify4(exec4);
3643
+ var MAX_OUTPUT_CHARS4 = 2e4;
3644
+ var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
3645
+ var SearchSubagent = class extends Subagent {
3646
+ type = "search";
3647
+ name = "Search Agent";
3648
+ constructor(model) {
3649
+ super(model || SUBAGENT_MODELS.search);
3650
+ this.maxSteps = 15;
3651
+ }
3652
+ getSystemPrompt(options) {
3653
+ return `You are a specialized search agent for exploring codebases. Your job is to find relevant code, files, and patterns based on the user's query.
3654
+
3655
+ Working Directory: ${options.workingDirectory}
3656
+
3657
+ You have these tools available:
3658
+ - grep: Search for patterns in files using ripgrep (rg)
3659
+ - glob: Find files matching a pattern
3660
+ - read_file: Read contents of a specific file
3661
+ - list_dir: List directory contents
3662
+
3663
+ ## Strategy - Search in Parallel
3664
+
3665
+ IMPORTANT: When searching, run MULTIPLE searches in PARALLEL to cover different variations and related terms. Don't search sequentially - batch your searches together!
3666
+
3667
+ For example, if asked "how does authentication work":
3668
+ - Search for "auth", "login", "session", "jwt", "token" all at once
3669
+ - Search in different likely directories: src/auth/, lib/auth/, services/auth/
3670
+ - Look for common patterns: AuthProvider, useAuth, authenticate, isAuthenticated
3671
+
3672
+ **Parallel Search Patterns:**
3673
+ 1. Try multiple naming conventions at once:
3674
+ - camelCase: "getUserData", "handleAuth"
3675
+ - snake_case: "get_user_data", "handle_auth"
3676
+ - PascalCase: "UserService", "AuthProvider"
3677
+
3678
+ 2. Search for related concepts together:
3679
+ - For "database": search "db", "database", "query", "model", "schema"
3680
+ - For "api": search "endpoint", "route", "handler", "controller", "api"
3681
+
3682
+ 3. Use glob AND grep together:
3683
+ - Find files: \`*.auth.ts\`, \`*Auth*.tsx\`, \`auth/*.ts\`
3684
+ - Search content: patterns, function names, class names
3685
+
3686
+ ## Execution Flow
3687
+ 1. First, run 2-4 parallel searches covering different angles of the query
3688
+ 2. Review results and identify the most relevant files
3689
+ 3. Read the key files to understand the full context
3690
+ 4. Provide a clear summary with exact file paths and line numbers
3691
+
3692
+ Be efficient - you have limited steps. Maximize coverage with parallel tool calls.
3693
+
3694
+ ## Output Format
3695
+ When done, provide a summary with:
3696
+ - Key files/locations found (with full paths)
3697
+ - Relevant code snippets showing the important parts
3698
+ - How the pieces connect together
3699
+
3700
+ Keep your responses concise and focused on actionable information.`;
3701
+ }
3702
+ getTools(options) {
3703
+ const workingDirectory = options.workingDirectory;
3704
+ return {
3705
+ grep: tool7({
3706
+ description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
3707
+ inputSchema: z8.object({
3708
+ pattern: z8.string().describe("The regex pattern to search for"),
3709
+ path: z8.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
3710
+ fileType: z8.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
3711
+ maxResults: z8.number().optional().default(50).describe("Maximum number of results to return")
3712
+ }),
3713
+ execute: async ({ pattern, path, fileType, maxResults }) => {
3714
+ try {
3715
+ const searchPath = path ? resolve8(workingDirectory, path) : workingDirectory;
3716
+ let args = ["rg", "--line-number", "--no-heading"];
3717
+ if (fileType) {
3718
+ args.push("--type", fileType);
3719
+ }
3720
+ args.push("--max-count", String(maxResults || 50));
3721
+ args.push("--", pattern, searchPath);
3722
+ const { stdout, stderr } = await execAsync4(args.join(" "), {
3723
+ cwd: workingDirectory,
3724
+ maxBuffer: 5 * 1024 * 1024,
3725
+ timeout: 3e4
3726
+ });
3727
+ const output = truncateOutput(stdout || "No matches found", MAX_OUTPUT_CHARS4);
3728
+ const matchCount = (stdout || "").split("\n").filter(Boolean).length;
3729
+ return {
3730
+ success: true,
3731
+ output,
3732
+ matchCount,
3733
+ pattern
3734
+ };
3735
+ } catch (error) {
3736
+ if (error.code === 1 && !error.stderr) {
3737
+ return {
3738
+ success: true,
3739
+ output: "No matches found",
3740
+ matchCount: 0,
3741
+ pattern
3742
+ };
3743
+ }
3744
+ return {
3745
+ success: false,
3746
+ error: error.message,
3747
+ pattern
3748
+ };
3749
+ }
3750
+ }
3751
+ }),
3752
+ glob: tool7({
3753
+ description: "Find files matching a glob pattern. Returns list of matching file paths.",
3754
+ inputSchema: z8.object({
3755
+ pattern: z8.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
3756
+ maxResults: z8.number().optional().default(100).describe("Maximum number of files to return")
3757
+ }),
3758
+ execute: async ({ pattern, maxResults }) => {
3759
+ try {
3760
+ const { stdout } = await execAsync4(
3761
+ `find . -type f -name "${pattern.replace("**/", "")}" 2>/dev/null | head -n ${maxResults || 100}`,
3762
+ {
3763
+ cwd: workingDirectory,
3764
+ timeout: 3e4
3765
+ }
3766
+ );
3767
+ const files = stdout.trim().split("\n").filter(Boolean);
3768
+ return {
3769
+ success: true,
3770
+ files,
3771
+ count: files.length,
3772
+ pattern
3773
+ };
3774
+ } catch (error) {
3775
+ return {
3776
+ success: false,
3777
+ error: error.message,
3778
+ pattern
3779
+ };
3780
+ }
3781
+ }
3782
+ }),
3783
+ read_file: tool7({
3784
+ description: "Read the contents of a file. Use this to examine specific files found in search.",
3785
+ inputSchema: z8.object({
3786
+ path: z8.string().describe("Path to the file (relative to working directory or absolute)"),
3787
+ startLine: z8.number().optional().describe("Start reading from this line (1-indexed)"),
3788
+ endLine: z8.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
3789
+ }),
3790
+ execute: async ({ path, startLine, endLine }) => {
3791
+ try {
3792
+ const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
3793
+ if (!existsSync10(absolutePath)) {
3794
+ return {
3795
+ success: false,
3796
+ error: `File not found: ${path}`
3797
+ };
3798
+ }
3799
+ const stats = await stat3(absolutePath);
3800
+ if (stats.size > MAX_FILE_SIZE2) {
3801
+ return {
3802
+ success: false,
3803
+ error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
3804
+ };
3805
+ }
3806
+ let content = await readFile7(absolutePath, "utf-8");
3807
+ if (startLine !== void 0 || endLine !== void 0) {
3808
+ const lines = content.split("\n");
3809
+ const start = (startLine ?? 1) - 1;
3810
+ const end = endLine ?? lines.length;
3811
+ content = lines.slice(start, end).join("\n");
3812
+ }
3813
+ return {
3814
+ success: true,
3815
+ path: relative6(workingDirectory, absolutePath),
3816
+ content: truncateOutput(content, MAX_OUTPUT_CHARS4),
3817
+ lineCount: content.split("\n").length
3818
+ };
3819
+ } catch (error) {
3820
+ return {
3821
+ success: false,
3822
+ error: error.message
3823
+ };
3824
+ }
3825
+ }
3826
+ }),
3827
+ list_dir: tool7({
3828
+ description: "List contents of a directory. Shows files and subdirectories.",
3829
+ inputSchema: z8.object({
3830
+ path: z8.string().optional().default(".").describe("Directory path (relative to working directory)"),
3831
+ recursive: z8.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
3832
+ maxDepth: z8.number().optional().default(2).describe("Maximum depth for recursive listing")
3833
+ }),
3834
+ execute: async ({ path, recursive, maxDepth }) => {
3835
+ try {
3836
+ const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
3837
+ if (!existsSync10(absolutePath)) {
3838
+ return {
3839
+ success: false,
3840
+ error: `Directory not found: ${path}`
3841
+ };
3842
+ }
3843
+ const stats = await stat3(absolutePath);
3844
+ if (!stats.isDirectory()) {
3845
+ return {
3846
+ success: false,
3847
+ error: `Not a directory: ${path}`
3848
+ };
3849
+ }
3850
+ if (recursive) {
3851
+ const { stdout } = await execAsync4(
3852
+ `find . -maxdepth ${maxDepth} -type f 2>/dev/null | head -n 200`,
3853
+ {
3854
+ cwd: absolutePath,
3855
+ timeout: 1e4
3856
+ }
3857
+ );
3858
+ const files = stdout.trim().split("\n").filter(Boolean);
3859
+ return {
3860
+ success: true,
3861
+ path: relative6(workingDirectory, absolutePath) || ".",
3862
+ files,
3863
+ count: files.length,
3864
+ recursive: true
3865
+ };
3866
+ } else {
3867
+ const entries = await readdir3(absolutePath, { withFileTypes: true });
3868
+ const items = entries.slice(0, 200).map((e) => ({
3869
+ name: e.name,
3870
+ type: e.isDirectory() ? "directory" : "file"
3871
+ }));
3872
+ return {
3873
+ success: true,
3874
+ path: relative6(workingDirectory, absolutePath) || ".",
3875
+ items,
3876
+ count: items.length
3877
+ };
3878
+ }
3879
+ } catch (error) {
3880
+ return {
3881
+ success: false,
3882
+ error: error.message
3883
+ };
3884
+ }
3885
+ }
3886
+ })
3887
+ };
3888
+ }
3889
+ parseResult(text2, steps) {
3890
+ const findings = [];
3891
+ let filesSearched = 0;
3892
+ let matchCount = 0;
3893
+ for (const step of steps) {
3894
+ if (step.type === "tool_result" && step.toolOutput) {
3895
+ const output = step.toolOutput;
3896
+ if (step.toolName === "grep" && output.success) {
3897
+ matchCount += output.matchCount || 0;
3898
+ const lines = (output.output || "").split("\n").filter(Boolean).slice(0, 10);
3899
+ for (const line of lines) {
3900
+ const match = line.match(/^([^:]+):(\d+):(.*)$/);
3901
+ if (match) {
3902
+ findings.push({
3903
+ type: "match",
3904
+ path: match[1],
3905
+ lineNumber: parseInt(match[2], 10),
3906
+ content: match[3].trim(),
3907
+ relevance: "high"
3908
+ });
2878
3909
  }
2879
3910
  }
3911
+ } else if (step.toolName === "glob" && output.success) {
3912
+ filesSearched += output.count || 0;
3913
+ for (const file of (output.files || []).slice(0, 5)) {
3914
+ findings.push({
3915
+ type: "file",
3916
+ path: file,
3917
+ relevance: "medium"
3918
+ });
3919
+ }
3920
+ } else if (step.toolName === "read_file" && output.success) {
3921
+ findings.push({
3922
+ type: "file",
3923
+ path: output.path,
3924
+ relevance: "high",
3925
+ context: `${output.lineCount} lines`
3926
+ });
2880
3927
  }
2881
- if (filesToCheck.length === 0) {
3928
+ }
3929
+ }
3930
+ const query = steps.length > 0 ? steps.find((s) => s.type === "text")?.content || "" : "";
3931
+ return {
3932
+ query,
3933
+ summary: text2,
3934
+ findings: findings.slice(0, 20),
3935
+ // Limit findings
3936
+ filesSearched,
3937
+ matchCount
3938
+ };
3939
+ }
3940
+ };
3941
+ function createSearchSubagent(model) {
3942
+ return new SearchSubagent(model);
3943
+ }
3944
+
3945
+ // src/tools/search.ts
3946
+ var MAX_RESULT_CHARS = 8e3;
3947
+ function createSearchTool(options) {
3948
+ return tool8({
3949
+ description: `Delegate a search task to a specialized search agent. Use this when you need to:
3950
+ - Find files or code matching a pattern
3951
+ - Explore the codebase structure
3952
+ - Search for specific functions, classes, or variables
3953
+ - Understand how a feature is implemented
3954
+
3955
+ The search agent will explore the codebase and return a summary of findings.
3956
+ This is more thorough than a simple grep because it can follow references and understand context.
3957
+
3958
+ Examples:
3959
+ - "Find all React components that use the useState hook"
3960
+ - "Where is the authentication logic implemented?"
3961
+ - "Find all API routes and their handlers"
3962
+ - "Search for usages of the UserService class"`,
3963
+ inputSchema: z9.object({
3964
+ query: z9.string().describe("What to search for. Be specific about what you're looking for."),
3965
+ context: z9.string().optional().describe("Optional additional context about why you need this information.")
3966
+ }),
3967
+ execute: async ({ query, context }, toolOptions) => {
3968
+ const toolCallId = toolOptions.toolCallId || `search_${Date.now()}`;
3969
+ await options.onProgress?.({
3970
+ status: "started",
3971
+ subagentId: toolCallId
3972
+ });
3973
+ try {
3974
+ const subagent = createSearchSubagent();
3975
+ const fullTask = context ? `${query}
3976
+
3977
+ Context: ${context}` : query;
3978
+ const result = await subagent.run({
3979
+ task: fullTask,
3980
+ sessionId: options.sessionId,
3981
+ toolCallId,
3982
+ workingDirectory: options.workingDirectory,
3983
+ onProgress: async (event) => {
3984
+ if (event.type === "step" && event.step) {
3985
+ await options.onProgress?.({
3986
+ status: "step",
3987
+ subagentId: event.subagentId,
3988
+ stepType: event.step.type,
3989
+ stepContent: event.step.content,
3990
+ toolName: event.step.toolName,
3991
+ toolInput: event.step.toolInput,
3992
+ toolOutput: event.step.toolOutput
3993
+ });
3994
+ } else if (event.type === "complete") {
3995
+ await options.onProgress?.({
3996
+ status: "complete",
3997
+ subagentId: event.subagentId,
3998
+ result: event.result
3999
+ });
4000
+ } else if (event.type === "error") {
4001
+ await options.onProgress?.({
4002
+ status: "error",
4003
+ subagentId: event.subagentId,
4004
+ error: event.error
4005
+ });
4006
+ }
4007
+ }
4008
+ });
4009
+ if (!result.success) {
2882
4010
  return {
2883
- success: true,
2884
- message: "No supported files found to check. Supported extensions: " + getSupportedExtensions().join(", "),
2885
- files: [],
2886
- totalErrors: 0,
2887
- totalWarnings: 0
4011
+ success: false,
4012
+ error: result.error || "Search failed",
4013
+ executionId: result.executionId
2888
4014
  };
2889
4015
  }
2890
- await Promise.all(
2891
- filesToCheck.map((file) => touchFile(file, true))
2892
- );
2893
- const diagnosticsMap = {};
2894
- for (const file of filesToCheck) {
2895
- const diagnostics = await getDiagnostics(file);
2896
- if (diagnostics.length > 0) {
2897
- diagnosticsMap[file] = diagnostics;
4016
+ const searchResult = result.result;
4017
+ let formattedResult = `## Search Results
4018
+
4019
+ `;
4020
+ formattedResult += `**Summary:** ${searchResult.summary}
4021
+
4022
+ `;
4023
+ if (searchResult.findings.length > 0) {
4024
+ formattedResult += `### Key Findings (${searchResult.findings.length} items)
4025
+
4026
+ `;
4027
+ for (const finding of searchResult.findings) {
4028
+ if (finding.type === "match") {
4029
+ formattedResult += `- **${finding.path}:${finding.lineNumber}** - ${truncateOutput(finding.content || "", 200)}
4030
+ `;
4031
+ } else if (finding.type === "file") {
4032
+ formattedResult += `- **${finding.path}** ${finding.context ? `(${finding.context})` : ""}
4033
+ `;
4034
+ }
2898
4035
  }
2899
4036
  }
2900
- return formatDiagnosticsResult(diagnosticsMap, options.workingDirectory);
4037
+ formattedResult += `
4038
+ **Stats:** ${searchResult.matchCount} matches across ${searchResult.filesSearched} files searched`;
4039
+ return {
4040
+ success: true,
4041
+ query: searchResult.query,
4042
+ summary: searchResult.summary,
4043
+ findings: searchResult.findings,
4044
+ matchCount: searchResult.matchCount,
4045
+ filesSearched: searchResult.filesSearched,
4046
+ formattedResult: truncateOutput(formattedResult, MAX_RESULT_CHARS),
4047
+ executionId: result.executionId,
4048
+ stepsCount: result.steps.length
4049
+ };
2901
4050
  } catch (error) {
4051
+ await options.onProgress?.({
4052
+ status: "error",
4053
+ error: error.message
4054
+ });
2902
4055
  return {
2903
4056
  success: false,
2904
4057
  error: error.message
@@ -2907,84 +4060,6 @@ Working directory: ${options.workingDirectory}`,
2907
4060
  }
2908
4061
  });
2909
4062
  }
2910
- function formatDiagnosticsResult(diagnosticsMap, workingDirectory) {
2911
- let totalErrors = 0;
2912
- let totalWarnings = 0;
2913
- let totalInfo = 0;
2914
- const files = [];
2915
- for (const [filePath, diagnostics] of Object.entries(diagnosticsMap)) {
2916
- const relativePath = relative4(workingDirectory, filePath);
2917
- let fileErrors = 0;
2918
- let fileWarnings = 0;
2919
- const formattedDiagnostics = diagnostics.map((d) => {
2920
- const severity = getSeverityString(d.severity);
2921
- if (d.severity === 1 /* Error */) {
2922
- fileErrors++;
2923
- totalErrors++;
2924
- } else if (d.severity === 2 /* Warning */) {
2925
- fileWarnings++;
2926
- totalWarnings++;
2927
- } else {
2928
- totalInfo++;
2929
- }
2930
- return {
2931
- severity,
2932
- line: d.range.start.line + 1,
2933
- column: d.range.start.character + 1,
2934
- message: d.message,
2935
- source: d.source,
2936
- code: d.code
2937
- };
2938
- });
2939
- files.push({
2940
- path: filePath,
2941
- relativePath,
2942
- errors: fileErrors,
2943
- warnings: fileWarnings,
2944
- diagnostics: formattedDiagnostics
2945
- });
2946
- }
2947
- files.sort((a, b) => b.errors - a.errors);
2948
- const hasIssues = totalErrors > 0 || totalWarnings > 0;
2949
- return {
2950
- success: true,
2951
- message: hasIssues ? `Found ${totalErrors} error(s) and ${totalWarnings} warning(s) in ${files.length} file(s).` : `No lint errors found in ${Object.keys(diagnosticsMap).length || "any"} file(s).`,
2952
- files,
2953
- totalErrors,
2954
- totalWarnings,
2955
- totalInfo,
2956
- summary: hasIssues ? formatSummary(files) : void 0
2957
- };
2958
- }
2959
- function getSeverityString(severity) {
2960
- switch (severity) {
2961
- case 1 /* Error */:
2962
- return "error";
2963
- case 2 /* Warning */:
2964
- return "warning";
2965
- case 3 /* Information */:
2966
- return "info";
2967
- case 4 /* Hint */:
2968
- return "hint";
2969
- default:
2970
- return "error";
2971
- }
2972
- }
2973
- function formatSummary(files) {
2974
- const lines = [];
2975
- for (const file of files) {
2976
- lines.push(`
2977
- ${file.relativePath}:`);
2978
- for (const d of file.diagnostics.slice(0, 10)) {
2979
- const prefix = d.severity === "error" ? "\u274C" : d.severity === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
2980
- lines.push(` ${prefix} [${d.line}:${d.column}] ${d.message}`);
2981
- }
2982
- if (file.diagnostics.length > 10) {
2983
- lines.push(` ... and ${file.diagnostics.length - 10} more`);
2984
- }
2985
- }
2986
- return lines.join("\n");
2987
- }
2988
4063
 
2989
4064
  // src/tools/index.ts
2990
4065
  function createTools(options) {
@@ -3001,7 +4076,8 @@ function createTools(options) {
3001
4076
  write_file: createWriteFileTool({
3002
4077
  workingDirectory: options.workingDirectory,
3003
4078
  sessionId: options.sessionId,
3004
- enableLSP: options.enableLSP ?? true
4079
+ enableLSP: options.enableLSP ?? true,
4080
+ onProgress: options.onWriteFileProgress
3005
4081
  }),
3006
4082
  todo: createTodoTool({
3007
4083
  sessionId: options.sessionId
@@ -3012,15 +4088,20 @@ function createTools(options) {
3012
4088
  }),
3013
4089
  linter: createLinterTool({
3014
4090
  workingDirectory: options.workingDirectory
4091
+ }),
4092
+ search: createSearchTool({
4093
+ sessionId: options.sessionId,
4094
+ workingDirectory: options.workingDirectory,
4095
+ onProgress: options.onSearchProgress
3015
4096
  })
3016
4097
  };
3017
4098
  }
3018
4099
 
3019
4100
  // src/agent/context.ts
3020
- import { generateText } from "ai";
3021
- import { gateway } from "@ai-sdk/gateway";
4101
+ import { generateText as generateText2 } from "ai";
3022
4102
 
3023
4103
  // src/agent/prompts.ts
4104
+ init_skills();
3024
4105
  import os from "os";
3025
4106
  function getSearchInstructions() {
3026
4107
  const platform3 = process.platform;
@@ -3039,9 +4120,33 @@ function getSearchInstructions() {
3039
4120
  - **If ripgrep (\`rg\`) is installed**: \`rg "pattern" -t ts src/\` - faster and respects .gitignore`;
3040
4121
  }
3041
4122
  async function buildSystemPrompt(options) {
3042
- const { workingDirectory, skillsDirectories, sessionId, customInstructions } = options;
3043
- const skills = await loadAllSkills(skillsDirectories);
3044
- const skillsContext = formatSkillsForContext(skills);
4123
+ const {
4124
+ workingDirectory,
4125
+ skillsDirectories,
4126
+ sessionId,
4127
+ discoveredSkills,
4128
+ activeFiles = [],
4129
+ customInstructions
4130
+ } = options;
4131
+ let alwaysLoadedContent = "";
4132
+ let globMatchedContent = "";
4133
+ let agentsMdContent = "";
4134
+ let onDemandSkillsContext = "";
4135
+ if (discoveredSkills) {
4136
+ const { always, onDemand, all } = await loadAllSkillsFromDiscovered(discoveredSkills);
4137
+ alwaysLoadedContent = formatAlwaysLoadedSkills(always);
4138
+ onDemandSkillsContext = formatSkillsForContext(onDemand);
4139
+ const agentsMd = await loadAgentsMd(discoveredSkills.agentsMdPath);
4140
+ agentsMdContent = formatAgentsMdContent(agentsMd);
4141
+ if (activeFiles.length > 0) {
4142
+ const globMatched = await getGlobMatchedSkills(all, activeFiles, workingDirectory);
4143
+ globMatchedContent = formatGlobMatchedSkills(globMatched);
4144
+ }
4145
+ } else {
4146
+ const { loadAllSkills: loadAllSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
4147
+ const skills = await loadAllSkills2(skillsDirectories);
4148
+ onDemandSkillsContext = formatSkillsForContext(skills);
4149
+ }
3045
4150
  const todos = todoQueries.getBySession(sessionId);
3046
4151
  const todosContext = formatTodosForContext(todos);
3047
4152
  const platform3 = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
@@ -3062,6 +4167,7 @@ You have access to powerful tools for:
3062
4167
  - **linter**: Check files for type errors and lint issues (TypeScript, JavaScript, TSX, JSX)
3063
4168
  - **todo**: Manage your task list to track progress on complex operations
3064
4169
  - **load_skill**: Load specialized knowledge documents for specific tasks
4170
+ - **search**: Semantic search using a subagent - for exploratory questions and finding code by meaning
3065
4171
 
3066
4172
 
3067
4173
  IMPORTANT: If you have zero context of where you are working, always explore it first to understand the structure before doing things for the user.
@@ -3138,6 +4244,9 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
3138
4244
  - Use \`write_file\` with mode "full" only for new files or complete rewrites
3139
4245
  - After making changes, use the \`linter\` tool to check for type errors and lint issues
3140
4246
  - The \`write_file\` tool automatically shows lint errors in its output for TypeScript/JavaScript files
4247
+ - If the user asks to write/create a file, always use \`write_file\` rather than printing the full contents
4248
+ - If the user requests a file but does not provide a path, choose a sensible default (e.g. \`index.html\`) and proceed
4249
+ - For large content (hundreds of lines), avoid placing it in chat output; write to a file instead
3141
4250
 
3142
4251
  ### Linter Tool
3143
4252
  The linter tool uses Language Server Protocol (LSP) to detect type errors and lint issues:
@@ -3149,6 +4258,30 @@ linter({ paths: ["src/"] }) // Check all files in a directory
3149
4258
  Use this proactively after making code changes to catch errors early.
3150
4259
 
3151
4260
  ### Searching and Exploration
4261
+
4262
+ **Choose the right search approach:**
4263
+
4264
+ 1. **Use the \`search\` tool (subagent)** for:
4265
+ - Semantic/exploratory questions: "How does authentication work?", "Where is user data processed?"
4266
+ - Finding code by meaning or concept, not exact text
4267
+ - Understanding how features are implemented across multiple files
4268
+ - Exploring unfamiliar parts of the codebase
4269
+ - Questions like "where", "how", "what does X do"
4270
+
4271
+ The search subagent is a mini-agent that intelligently explores the codebase, reads relevant files, and returns a summary of what it found. It's best for understanding and discovery.
4272
+
4273
+ 2. **Use direct commands (grep/rg, find)** for:
4274
+ - Exact string matches: \`rg "functionName"\`, \`rg "class MyClass"\`
4275
+ - Finding files by name: \`find . -name "*.config.ts"\`
4276
+ - Simple pattern matching when you know exactly what you're looking for
4277
+ - Counting occurrences or listing all matches
4278
+
4279
+ **Examples:**
4280
+ - "Where is the API authentication handled?" \u2192 Use \`search\` tool
4281
+ - "Find all usages of getUserById" \u2192 Use \`rg "getUserById"\`
4282
+ - "How does the payment flow work?" \u2192 Use \`search\` tool
4283
+ - "Find files named config" \u2192 Use \`find . -name "*config*"\`
4284
+
3152
4285
  ${searchInstructions}
3153
4286
 
3154
4287
  ###Follow these principles when designing and implementing software:
@@ -3171,11 +4304,11 @@ ${searchInstructions}
3171
4304
  16. **Diversity** \u2014 Distrust all claims for "one true way"
3172
4305
  17. **Extensibility** \u2014 Design for the future, because it will be here sooner than you think
3173
4306
 
3174
- ###Follow these ruls to be a good agent for the user:
4307
+ ### Follow these rules to be a good agent for the user:
3175
4308
 
3176
- 1. Understand first - Read relevant files before making any changes. Use search tools to explore the codebase and understand the existing patterns and structure.
4309
+ 1. Understand first - Read relevant files before making any changes. Use the \`search\` tool for exploratory questions about how things work, and direct searches (grep/rg) for finding exact strings or file names.
3177
4310
  2. Plan for complexity - If the task involves 3+ steps or has meaningful trade-offs, create a todo list to track progress before implementing.
3178
- 3. Use the right tools - Have specialized tools for reading files, editing code, searching by pattern , running terminal commands, and more. Prefer these over raw shell commands.
4311
+ 3. Use the right tools - Have specialized tools for reading files, editing code, semantic search via subagents, and running terminal commands. Prefer these over raw shell commands.
3179
4312
  4. Work efficiently - When need to do multiple independent things (like reading several files), do them in parallel rather than one at a time.
3180
4313
  5. Be direct - Focus on technical accuracy rather than validation. If see issues with an approach or need clarification, say so.
3181
4314
  6. Verify my work - After making changes, check for linter errors and fix any introduced.
@@ -3188,8 +4321,14 @@ ${searchInstructions}
3188
4321
  - Ask clarifying questions when requirements are ambiguous
3189
4322
  - Report progress on multi-step tasks
3190
4323
 
3191
- ## Skills
3192
- ${skillsContext}
4324
+ ${agentsMdContent}
4325
+
4326
+ ${alwaysLoadedContent}
4327
+
4328
+ ${globMatchedContent}
4329
+
4330
+ ## On-Demand Skills
4331
+ ${onDemandSkillsContext}
3193
4332
 
3194
4333
  ## Current Task List
3195
4334
  ${todosContext}
@@ -3284,8 +4423,8 @@ ${this.summary}`
3284
4423
  try {
3285
4424
  const config = getConfig();
3286
4425
  const summaryPrompt = createSummaryPrompt(historyText);
3287
- const result = await generateText({
3288
- model: gateway(config.defaultModel),
4426
+ const result = await generateText2({
4427
+ model: resolveModel(config.defaultModel),
3289
4428
  prompt: summaryPrompt
3290
4429
  });
3291
4430
  this.summary = result.text;
@@ -3355,7 +4494,9 @@ var Agent = class _Agent {
3355
4494
  sessionId: this.session.id,
3356
4495
  workingDirectory: this.session.workingDirectory,
3357
4496
  skillsDirectories: config.resolvedSkillsDirectories,
3358
- onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0
4497
+ onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
4498
+ onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
4499
+ onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "search", data: progress }) : void 0
3359
4500
  });
3360
4501
  }
3361
4502
  /**
@@ -3464,28 +4605,33 @@ ${prompt}` });
3464
4605
  const systemPrompt = await buildSystemPrompt({
3465
4606
  workingDirectory: this.session.workingDirectory,
3466
4607
  skillsDirectories: config.resolvedSkillsDirectories,
3467
- sessionId: this.session.id
4608
+ sessionId: this.session.id,
4609
+ discoveredSkills: config.discoveredSkills,
4610
+ // TODO: Pass activeFiles from client for glob matching
4611
+ activeFiles: []
3468
4612
  });
3469
4613
  const messages2 = await this.context.getMessages();
3470
4614
  const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
3471
4615
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
3472
- const stream = streamText({
3473
- model: gateway2(this.session.model),
4616
+ const useAnthropic = isAnthropicModel(this.session.model);
4617
+ const stream = streamText2({
4618
+ model: resolveModel(this.session.model),
3474
4619
  system: systemPrompt,
3475
4620
  messages: messages2,
3476
4621
  tools: wrappedTools,
3477
- stopWhen: stepCountIs(500),
4622
+ stopWhen: stepCountIs2(500),
3478
4623
  // Forward abort signal if provided
3479
4624
  abortSignal: options.abortSignal,
3480
4625
  // Enable extended thinking/reasoning for models that support it
3481
- providerOptions: {
4626
+ providerOptions: useAnthropic ? {
3482
4627
  anthropic: {
4628
+ toolStreaming: true,
3483
4629
  thinking: {
3484
4630
  type: "enabled",
3485
4631
  budgetTokens: 1e4
3486
4632
  }
3487
4633
  }
3488
- },
4634
+ } : void 0,
3489
4635
  onStepFinish: async (step) => {
3490
4636
  options.onStepFinish?.(step);
3491
4637
  },
@@ -3515,26 +4661,29 @@ ${prompt}` });
3515
4661
  const systemPrompt = await buildSystemPrompt({
3516
4662
  workingDirectory: this.session.workingDirectory,
3517
4663
  skillsDirectories: config.resolvedSkillsDirectories,
3518
- sessionId: this.session.id
4664
+ sessionId: this.session.id,
4665
+ discoveredSkills: config.discoveredSkills,
4666
+ activeFiles: []
3519
4667
  });
3520
4668
  const messages2 = await this.context.getMessages();
3521
4669
  const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
3522
4670
  const wrappedTools = this.wrapToolsWithApproval(options, tools);
3523
- const result = await generateText2({
3524
- model: gateway2(this.session.model),
4671
+ const useAnthropic = isAnthropicModel(this.session.model);
4672
+ const result = await generateText3({
4673
+ model: resolveModel(this.session.model),
3525
4674
  system: systemPrompt,
3526
4675
  messages: messages2,
3527
4676
  tools: wrappedTools,
3528
- stopWhen: stepCountIs(500),
4677
+ stopWhen: stepCountIs2(500),
3529
4678
  // Enable extended thinking/reasoning for models that support it
3530
- providerOptions: {
4679
+ providerOptions: useAnthropic ? {
3531
4680
  anthropic: {
3532
4681
  thinking: {
3533
4682
  type: "enabled",
3534
4683
  budgetTokens: 1e4
3535
4684
  }
3536
4685
  }
3537
- }
4686
+ } : void 0
3538
4687
  });
3539
4688
  const responseMessages = result.response.messages;
3540
4689
  this.context.addResponseMessages(responseMessages);
@@ -3556,11 +4705,11 @@ ${prompt}` });
3556
4705
  wrappedTools[name] = originalTool;
3557
4706
  continue;
3558
4707
  }
3559
- wrappedTools[name] = tool7({
4708
+ wrappedTools[name] = tool9({
3560
4709
  description: originalTool.description || "",
3561
- inputSchema: originalTool.inputSchema || z8.object({}),
4710
+ inputSchema: originalTool.inputSchema || z10.object({}),
3562
4711
  execute: async (input, toolOptions) => {
3563
- const toolCallId = toolOptions.toolCallId || nanoid3();
4712
+ const toolCallId = toolOptions.toolCallId || nanoid4();
3564
4713
  const execution = toolExecutionQueries.create({
3565
4714
  sessionId: this.session.id,
3566
4715
  toolName: name,
@@ -3572,8 +4721,8 @@ ${prompt}` });
3572
4721
  this.pendingApprovals.set(toolCallId, execution);
3573
4722
  options.onApprovalRequired?.(execution);
3574
4723
  sessionQueries.updateStatus(this.session.id, "waiting");
3575
- const approved = await new Promise((resolve10) => {
3576
- approvalResolvers.set(toolCallId, { resolve: resolve10, sessionId: this.session.id });
4724
+ const approved = await new Promise((resolve11) => {
4725
+ approvalResolvers.set(toolCallId, { resolve: resolve11, sessionId: this.session.id });
3577
4726
  });
3578
4727
  const resolverData = approvalResolvers.get(toolCallId);
3579
4728
  approvalResolvers.delete(toolCallId);
@@ -3668,18 +4817,18 @@ ${prompt}` });
3668
4817
 
3669
4818
  // src/server/routes/sessions.ts
3670
4819
  var sessions2 = new Hono();
3671
- var createSessionSchema = z9.object({
3672
- name: z9.string().optional(),
3673
- workingDirectory: z9.string().optional(),
3674
- model: z9.string().optional(),
3675
- toolApprovals: z9.record(z9.string(), z9.boolean()).optional()
4820
+ var createSessionSchema = z11.object({
4821
+ name: z11.string().optional(),
4822
+ workingDirectory: z11.string().optional(),
4823
+ model: z11.string().optional(),
4824
+ toolApprovals: z11.record(z11.string(), z11.boolean()).optional()
3676
4825
  });
3677
- var paginationQuerySchema = z9.object({
3678
- limit: z9.string().optional(),
3679
- offset: z9.string().optional()
4826
+ var paginationQuerySchema = z11.object({
4827
+ limit: z11.string().optional(),
4828
+ offset: z11.string().optional()
3680
4829
  });
3681
- var messagesQuerySchema = z9.object({
3682
- limit: z9.string().optional()
4830
+ var messagesQuerySchema = z11.object({
4831
+ limit: z11.string().optional()
3683
4832
  });
3684
4833
  sessions2.get(
3685
4834
  "/",
@@ -3818,10 +4967,10 @@ sessions2.get("/:id/tools", async (c) => {
3818
4967
  count: executions.length
3819
4968
  });
3820
4969
  });
3821
- var updateSessionSchema = z9.object({
3822
- model: z9.string().optional(),
3823
- name: z9.string().optional(),
3824
- toolApprovals: z9.record(z9.string(), z9.boolean()).optional()
4970
+ var updateSessionSchema = z11.object({
4971
+ model: z11.string().optional(),
4972
+ name: z11.string().optional(),
4973
+ toolApprovals: z11.record(z11.string(), z11.boolean()).optional()
3825
4974
  });
3826
4975
  sessions2.patch(
3827
4976
  "/:id",
@@ -4020,11 +5169,11 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
4020
5169
  });
4021
5170
  function getAttachmentsDir(sessionId) {
4022
5171
  const appDataDir = getAppDataDirectory();
4023
- return join3(appDataDir, "attachments", sessionId);
5172
+ return join4(appDataDir, "attachments", sessionId);
4024
5173
  }
4025
5174
  function ensureAttachmentsDir(sessionId) {
4026
5175
  const dir = getAttachmentsDir(sessionId);
4027
- if (!existsSync10(dir)) {
5176
+ if (!existsSync11(dir)) {
4028
5177
  mkdirSync3(dir, { recursive: true });
4029
5178
  }
4030
5179
  return dir;
@@ -4036,12 +5185,12 @@ sessions2.get("/:id/attachments", async (c) => {
4036
5185
  return c.json({ error: "Session not found" }, 404);
4037
5186
  }
4038
5187
  const dir = getAttachmentsDir(sessionId);
4039
- if (!existsSync10(dir)) {
5188
+ if (!existsSync11(dir)) {
4040
5189
  return c.json({ sessionId, attachments: [], count: 0 });
4041
5190
  }
4042
5191
  const files = readdirSync(dir);
4043
5192
  const attachments = files.map((filename) => {
4044
- const filePath = join3(dir, filename);
5193
+ const filePath = join4(dir, filename);
4045
5194
  const stats = statSync(filePath);
4046
5195
  return {
4047
5196
  id: filename.split("_")[0],
@@ -4073,10 +5222,10 @@ sessions2.post("/:id/attachments", async (c) => {
4073
5222
  return c.json({ error: "No file provided" }, 400);
4074
5223
  }
4075
5224
  const dir = ensureAttachmentsDir(sessionId);
4076
- const id = nanoid4(10);
4077
- const ext = extname5(file.name) || "";
5225
+ const id = nanoid5(10);
5226
+ const ext = extname6(file.name) || "";
4078
5227
  const safeFilename = `${id}_${basename2(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
4079
- const filePath = join3(dir, safeFilename);
5228
+ const filePath = join4(dir, safeFilename);
4080
5229
  const arrayBuffer = await file.arrayBuffer();
4081
5230
  writeFileSync2(filePath, Buffer.from(arrayBuffer));
4082
5231
  return c.json({
@@ -4099,10 +5248,10 @@ sessions2.post("/:id/attachments", async (c) => {
4099
5248
  return c.json({ error: "Missing filename or data" }, 400);
4100
5249
  }
4101
5250
  const dir = ensureAttachmentsDir(sessionId);
4102
- const id = nanoid4(10);
4103
- const ext = extname5(body.filename) || "";
5251
+ const id = nanoid5(10);
5252
+ const ext = extname6(body.filename) || "";
4104
5253
  const safeFilename = `${id}_${basename2(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
4105
- const filePath = join3(dir, safeFilename);
5254
+ const filePath = join4(dir, safeFilename);
4106
5255
  let base64Data = body.data;
4107
5256
  if (base64Data.includes(",")) {
4108
5257
  base64Data = base64Data.split(",")[1];
@@ -4131,7 +5280,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
4131
5280
  return c.json({ error: "Session not found" }, 404);
4132
5281
  }
4133
5282
  const dir = getAttachmentsDir(sessionId);
4134
- if (!existsSync10(dir)) {
5283
+ if (!existsSync11(dir)) {
4135
5284
  return c.json({ error: "Attachment not found" }, 404);
4136
5285
  }
4137
5286
  const files = readdirSync(dir);
@@ -4139,17 +5288,154 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
4139
5288
  if (!file) {
4140
5289
  return c.json({ error: "Attachment not found" }, 404);
4141
5290
  }
4142
- const filePath = join3(dir, file);
5291
+ const filePath = join4(dir, file);
4143
5292
  unlinkSync(filePath);
4144
5293
  return c.json({ success: true, id: attachmentId });
4145
5294
  });
5295
+ var filesQuerySchema = z11.object({
5296
+ query: z11.string().optional(),
5297
+ // Filter query (e.g., "src/com" to match "src/components")
5298
+ limit: z11.string().optional()
5299
+ // Max results (default 50)
5300
+ });
5301
+ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
5302
+ "node_modules",
5303
+ ".git",
5304
+ ".next",
5305
+ "dist",
5306
+ "build",
5307
+ ".turbo",
5308
+ ".cache",
5309
+ "coverage",
5310
+ "__pycache__",
5311
+ ".pytest_cache",
5312
+ "venv",
5313
+ ".venv",
5314
+ "target",
5315
+ // Rust
5316
+ ".idea",
5317
+ ".vscode"
5318
+ ]);
5319
+ var IGNORED_EXTENSIONS = /* @__PURE__ */ new Set([
5320
+ ".pyc",
5321
+ ".pyo",
5322
+ ".class",
5323
+ ".o",
5324
+ ".obj",
5325
+ ".exe",
5326
+ ".dll",
5327
+ ".so",
5328
+ ".dylib"
5329
+ ]);
5330
+ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = []) {
5331
+ if (results.length >= limit) {
5332
+ return results;
5333
+ }
5334
+ try {
5335
+ const entries = await readdir4(currentDir, { withFileTypes: true });
5336
+ const queryLower = query.toLowerCase();
5337
+ for (const entry of entries) {
5338
+ if (results.length >= limit) break;
5339
+ const fullPath = join4(currentDir, entry.name);
5340
+ const relativePath = relative7(baseDir, fullPath);
5341
+ if (entry.isDirectory() && IGNORED_DIRECTORIES.has(entry.name)) {
5342
+ continue;
5343
+ }
5344
+ if (entry.name.startsWith(".")) {
5345
+ continue;
5346
+ }
5347
+ const ext = extname6(entry.name).toLowerCase();
5348
+ if (IGNORED_EXTENSIONS.has(ext)) {
5349
+ continue;
5350
+ }
5351
+ const matchesQuery = !query || relativePath.toLowerCase().includes(queryLower) || entry.name.toLowerCase().includes(queryLower);
5352
+ if (entry.isDirectory()) {
5353
+ if (matchesQuery) {
5354
+ results.push({
5355
+ path: relativePath,
5356
+ name: entry.name,
5357
+ type: "folder"
5358
+ });
5359
+ }
5360
+ const shouldRecurse = !query || relativePath.toLowerCase().startsWith(queryLower) || queryLower.startsWith(relativePath.toLowerCase());
5361
+ if (shouldRecurse && results.length < limit) {
5362
+ await listWorkspaceFiles(baseDir, fullPath, query, limit, results);
5363
+ }
5364
+ } else if (entry.isFile()) {
5365
+ if (matchesQuery) {
5366
+ results.push({
5367
+ path: relativePath,
5368
+ name: entry.name,
5369
+ type: "file",
5370
+ extension: ext || void 0
5371
+ });
5372
+ }
5373
+ }
5374
+ }
5375
+ } catch {
5376
+ }
5377
+ return results;
5378
+ }
5379
+ sessions2.get(
5380
+ "/:id/files",
5381
+ zValidator("query", filesQuerySchema),
5382
+ async (c) => {
5383
+ const sessionId = c.req.param("id");
5384
+ const { query = "", limit: limitStr = "50" } = c.req.valid("query");
5385
+ const limit = Math.min(parseInt(limitStr) || 50, 100);
5386
+ const session = sessionQueries.getById(sessionId);
5387
+ if (!session) {
5388
+ return c.json({ error: "Session not found" }, 404);
5389
+ }
5390
+ const workingDirectory = session.workingDirectory;
5391
+ if (!existsSync11(workingDirectory)) {
5392
+ return c.json({
5393
+ sessionId,
5394
+ workingDirectory,
5395
+ files: [],
5396
+ count: 0,
5397
+ error: "Working directory does not exist"
5398
+ });
5399
+ }
5400
+ try {
5401
+ const files = await listWorkspaceFiles(
5402
+ workingDirectory,
5403
+ workingDirectory,
5404
+ query,
5405
+ limit
5406
+ );
5407
+ files.sort((a, b) => {
5408
+ if (a.type !== b.type) {
5409
+ return a.type === "folder" ? -1 : 1;
5410
+ }
5411
+ return a.path.localeCompare(b.path);
5412
+ });
5413
+ return c.json({
5414
+ sessionId,
5415
+ workingDirectory,
5416
+ files,
5417
+ count: files.length,
5418
+ query
5419
+ });
5420
+ } catch (err) {
5421
+ console.error("Failed to list workspace files:", err);
5422
+ return c.json({
5423
+ error: "Failed to list files",
5424
+ sessionId,
5425
+ workingDirectory,
5426
+ files: [],
5427
+ count: 0
5428
+ }, 500);
5429
+ }
5430
+ }
5431
+ );
4146
5432
 
4147
5433
  // src/server/routes/agents.ts
4148
5434
  import { Hono as Hono2 } from "hono";
4149
5435
  import { zValidator as zValidator2 } from "@hono/zod-validator";
4150
- import { z as z10 } from "zod";
4151
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
4152
- import { join as join4 } from "path";
5436
+ import { z as z12 } from "zod";
5437
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
5438
+ import { join as join5 } from "path";
4153
5439
 
4154
5440
  // src/server/resumable-stream.ts
4155
5441
  import { createResumableStreamContext } from "resumable-stream/generic";
@@ -4224,41 +5510,107 @@ var streamContext = createResumableStreamContext({
4224
5510
  });
4225
5511
 
4226
5512
  // src/server/routes/agents.ts
4227
- import { nanoid as nanoid5 } from "nanoid";
5513
+ import { nanoid as nanoid6 } from "nanoid";
5514
+ var MAX_TOOL_INPUT_LENGTH = 8 * 1024;
5515
+ var MAX_TOOL_INPUT_PREVIEW = 2 * 1024;
5516
+ var MAX_TOOL_ARGS_CHUNK = 2 * 1024;
5517
+ function sanitizeToolInput(toolName, input) {
5518
+ if (toolName !== "write_file" || !input || typeof input !== "object") {
5519
+ return input;
5520
+ }
5521
+ const data = input;
5522
+ let changed = false;
5523
+ const next = { ...data };
5524
+ const content = typeof data.content === "string" ? data.content : void 0;
5525
+ if (content && content.length > MAX_TOOL_INPUT_LENGTH) {
5526
+ next.content = `${content.slice(0, MAX_TOOL_INPUT_PREVIEW)}
5527
+ ... (truncated)`;
5528
+ next.contentLength = content.length;
5529
+ next.contentTruncated = true;
5530
+ changed = true;
5531
+ }
5532
+ const oldString = typeof data.old_string === "string" ? data.old_string : void 0;
5533
+ if (oldString && oldString.length > MAX_TOOL_INPUT_LENGTH) {
5534
+ next.old_string = `${oldString.slice(0, MAX_TOOL_INPUT_PREVIEW)}
5535
+ ... (truncated)`;
5536
+ next.oldStringLength = oldString.length;
5537
+ next.oldStringTruncated = true;
5538
+ changed = true;
5539
+ }
5540
+ const newString = typeof data.new_string === "string" ? data.new_string : void 0;
5541
+ if (newString && newString.length > MAX_TOOL_INPUT_LENGTH) {
5542
+ next.new_string = `${newString.slice(0, MAX_TOOL_INPUT_PREVIEW)}
5543
+ ... (truncated)`;
5544
+ next.newStringLength = newString.length;
5545
+ next.newStringTruncated = true;
5546
+ changed = true;
5547
+ }
5548
+ if (changed) {
5549
+ console.log("[TOOL-INPUT] Truncated write_file input for streaming payload size");
5550
+ }
5551
+ return changed ? next : input;
5552
+ }
5553
+ function buildToolArgsText(input) {
5554
+ try {
5555
+ return JSON.stringify(input ?? {});
5556
+ } catch {
5557
+ return "{}";
5558
+ }
5559
+ }
5560
+ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId, toolName, input) {
5561
+ if (toolCallStarts.has(toolCallId)) return;
5562
+ toolCallStarts.add(toolCallId);
5563
+ await writeSSE(JSON.stringify({
5564
+ type: "tool-input-start",
5565
+ toolCallId,
5566
+ toolName
5567
+ }));
5568
+ if (toolName !== "write_file") return;
5569
+ const argsText = buildToolArgsText(input);
5570
+ for (let i = 0; i < argsText.length; i += MAX_TOOL_ARGS_CHUNK) {
5571
+ const chunk = argsText.slice(i, i + MAX_TOOL_ARGS_CHUNK);
5572
+ await writeSSE(JSON.stringify({
5573
+ type: "tool-input-delta",
5574
+ toolCallId,
5575
+ argsTextDelta: chunk
5576
+ }));
5577
+ await new Promise((resolve11) => setTimeout(resolve11, 0));
5578
+ }
5579
+ }
4228
5580
  var agents = new Hono2();
4229
- var attachmentSchema = z10.object({
4230
- type: z10.enum(["image", "file"]),
4231
- data: z10.string(),
5581
+ var attachmentSchema = z12.object({
5582
+ type: z12.enum(["image", "file"]),
5583
+ data: z12.string(),
4232
5584
  // base64 data URL or raw base64
4233
- mediaType: z10.string().optional(),
4234
- filename: z10.string().optional()
5585
+ mediaType: z12.string().optional(),
5586
+ filename: z12.string().optional()
4235
5587
  });
4236
- var runPromptSchema = z10.object({
4237
- prompt: z10.string(),
5588
+ var runPromptSchema = z12.object({
5589
+ prompt: z12.string(),
4238
5590
  // Can be empty if attachments are provided
4239
- attachments: z10.array(attachmentSchema).optional()
5591
+ attachments: z12.array(attachmentSchema).optional()
4240
5592
  }).refine(
4241
5593
  (data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
4242
5594
  { message: "Either prompt or attachments must be provided" }
4243
5595
  );
4244
- var quickStartSchema = z10.object({
4245
- prompt: z10.string().min(1),
4246
- name: z10.string().optional(),
4247
- workingDirectory: z10.string().optional(),
4248
- model: z10.string().optional(),
4249
- toolApprovals: z10.record(z10.string(), z10.boolean()).optional()
5596
+ var quickStartSchema = z12.object({
5597
+ prompt: z12.string().min(1),
5598
+ name: z12.string().optional(),
5599
+ workingDirectory: z12.string().optional(),
5600
+ model: z12.string().optional(),
5601
+ toolApprovals: z12.record(z12.string(), z12.boolean()).optional()
4250
5602
  });
4251
- var rejectSchema = z10.object({
4252
- reason: z10.string().optional()
5603
+ var rejectSchema = z12.object({
5604
+ reason: z12.string().optional()
4253
5605
  }).optional();
4254
5606
  var streamAbortControllers = /* @__PURE__ */ new Map();
4255
5607
  function getAttachmentsDirectory(sessionId) {
4256
5608
  const appDataDir = getAppDataDirectory();
4257
- return join4(appDataDir, "attachments", sessionId);
5609
+ return join5(appDataDir, "attachments", sessionId);
4258
5610
  }
4259
5611
  function saveAttachmentToDisk(sessionId, attachment, index) {
4260
5612
  const attachmentsDir = getAttachmentsDirectory(sessionId);
4261
- if (!existsSync11(attachmentsDir)) {
5613
+ if (!existsSync12(attachmentsDir)) {
4262
5614
  mkdirSync4(attachmentsDir, { recursive: true });
4263
5615
  }
4264
5616
  let filename = attachment.filename;
@@ -4270,7 +5622,7 @@ function saveAttachmentToDisk(sessionId, attachment, index) {
4270
5622
  if (base64Data.includes(",")) {
4271
5623
  base64Data = base64Data.split(",")[1];
4272
5624
  }
4273
- const filePath = join4(attachmentsDir, filename);
5625
+ const filePath = join5(attachmentsDir, filename);
4274
5626
  const buffer = Buffer.from(base64Data, "base64");
4275
5627
  writeFileSync3(filePath, buffer);
4276
5628
  return filePath;
@@ -4303,6 +5655,7 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
4303
5655
  const { readable, writable } = new TransformStream();
4304
5656
  const writer = writable.getWriter();
4305
5657
  let writerClosed = false;
5658
+ const toolCallStarts = /* @__PURE__ */ new Set();
4306
5659
  const abortController = new AbortController();
4307
5660
  streamAbortControllers.set(streamId, abortController);
4308
5661
  const writeSSE = async (data) => {
@@ -4411,11 +5764,32 @@ ${prompt}` });
4411
5764
  }));
4412
5765
  },
4413
5766
  onToolProgress: async (progress) => {
5767
+ const status = progress.data?.status || "no-status";
5768
+ const contentLength = typeof progress.data?.content === "string" ? progress.data.content.length : void 0;
5769
+ const chunkIndex = progress.data?.chunkIndex;
5770
+ const chunkCount = progress.data?.chunkCount;
5771
+ console.log(
5772
+ "[TOOL-PROGRESS] Sending:",
5773
+ progress.toolName,
5774
+ status,
5775
+ contentLength !== void 0 ? `contentLength=${contentLength}` : "",
5776
+ chunkIndex !== void 0 || chunkCount !== void 0 ? `chunk=${chunkIndex}/${chunkCount}` : ""
5777
+ );
4414
5778
  await writeSSE(JSON.stringify({
4415
5779
  type: "tool-progress",
4416
5780
  toolName: progress.toolName,
4417
5781
  data: progress.data
4418
5782
  }));
5783
+ if (progress.toolName === "write_file" && status === "content") {
5784
+ await writeSSE(JSON.stringify({
5785
+ type: "debug",
5786
+ label: "write-file-progress",
5787
+ contentLength,
5788
+ chunkIndex,
5789
+ chunkCount
5790
+ }));
5791
+ await new Promise((resolve11) => setTimeout(resolve11, 0));
5792
+ }
4419
5793
  },
4420
5794
  onStepFinish: async () => {
4421
5795
  await writeSSE(JSON.stringify({ type: "finish-step" }));
@@ -4457,6 +5831,7 @@ ${prompt}` });
4457
5831
  toolCallId: p.toolCallId,
4458
5832
  toolName: p.toolName
4459
5833
  }));
5834
+ toolCallStarts.add(p.toolCallId);
4460
5835
  } else if (part.type === "tool-call-delta") {
4461
5836
  const p = part;
4462
5837
  await writeSSE(JSON.stringify({
@@ -4465,11 +5840,23 @@ ${prompt}` });
4465
5840
  argsTextDelta: p.argsTextDelta
4466
5841
  }));
4467
5842
  } else if (part.type === "tool-call") {
5843
+ await emitSyntheticToolStreaming(
5844
+ writeSSE,
5845
+ toolCallStarts,
5846
+ part.toolCallId,
5847
+ part.toolName,
5848
+ part.input
5849
+ );
4468
5850
  await writeSSE(JSON.stringify({
4469
5851
  type: "tool-input-available",
4470
5852
  toolCallId: part.toolCallId,
4471
5853
  toolName: part.toolName,
4472
- input: part.input
5854
+ input: sanitizeToolInput(part.toolName, part.input)
5855
+ }));
5856
+ await writeSSE(JSON.stringify({
5857
+ type: "debug",
5858
+ label: "tool-input-available",
5859
+ toolName: part.toolName
4473
5860
  }));
4474
5861
  } else if (part.type === "tool-result") {
4475
5862
  await writeSSE(JSON.stringify({
@@ -4588,7 +5975,7 @@ ${prompt}` });
4588
5975
  userMessageContent = prompt;
4589
5976
  }
4590
5977
  messageQueries.create(id, { role: "user", content: userMessageContent });
4591
- const streamId = `stream_${id}_${nanoid5(10)}`;
5978
+ const streamId = `stream_${id}_${nanoid6(10)}`;
4592
5979
  activeStreamQueries.create(id, streamId);
4593
5980
  const stream = await streamContext.resumableStream(
4594
5981
  streamId,
@@ -4785,13 +6172,14 @@ agents.post(
4785
6172
  sessionConfig: body.toolApprovals ? { toolApprovals: body.toolApprovals } : void 0
4786
6173
  });
4787
6174
  const session = agent.getSession();
4788
- const streamId = `stream_${session.id}_${nanoid5(10)}`;
6175
+ const streamId = `stream_${session.id}_${nanoid6(10)}`;
4789
6176
  await createCheckpoint(session.id, session.workingDirectory, 0);
4790
6177
  activeStreamQueries.create(session.id, streamId);
4791
6178
  const createQuickStreamProducer = () => {
4792
6179
  const { readable, writable } = new TransformStream();
4793
6180
  const writer = writable.getWriter();
4794
6181
  let writerClosed = false;
6182
+ const toolCallStarts = /* @__PURE__ */ new Set();
4795
6183
  const abortController = new AbortController();
4796
6184
  streamAbortControllers.set(streamId, abortController);
4797
6185
  const writeSSE = async (data) => {
@@ -4837,11 +6225,32 @@ agents.post(
4837
6225
  abortSignal: abortController.signal,
4838
6226
  // Use our managed abort controller, NOT client signal
4839
6227
  onToolProgress: async (progress) => {
6228
+ const status = progress.data?.status || "no-status";
6229
+ const contentLength = typeof progress.data?.content === "string" ? progress.data.content.length : void 0;
6230
+ const chunkIndex = progress.data?.chunkIndex;
6231
+ const chunkCount = progress.data?.chunkCount;
6232
+ console.log(
6233
+ "[TOOL-PROGRESS] Sending:",
6234
+ progress.toolName,
6235
+ status,
6236
+ contentLength !== void 0 ? `contentLength=${contentLength}` : "",
6237
+ chunkIndex !== void 0 || chunkCount !== void 0 ? `chunk=${chunkIndex}/${chunkCount}` : ""
6238
+ );
4840
6239
  await writeSSE(JSON.stringify({
4841
6240
  type: "tool-progress",
4842
6241
  toolName: progress.toolName,
4843
6242
  data: progress.data
4844
6243
  }));
6244
+ if (progress.toolName === "write_file" && status === "content") {
6245
+ await writeSSE(JSON.stringify({
6246
+ type: "debug",
6247
+ label: "write-file-progress",
6248
+ contentLength,
6249
+ chunkIndex,
6250
+ chunkCount
6251
+ }));
6252
+ await new Promise((resolve11) => setTimeout(resolve11, 0));
6253
+ }
4845
6254
  },
4846
6255
  onStepFinish: async () => {
4847
6256
  await writeSSE(JSON.stringify({ type: "finish-step" }));
@@ -4883,6 +6292,7 @@ agents.post(
4883
6292
  toolCallId: p.toolCallId,
4884
6293
  toolName: p.toolName
4885
6294
  }));
6295
+ toolCallStarts.add(p.toolCallId);
4886
6296
  } else if (part.type === "tool-call-delta") {
4887
6297
  const p = part;
4888
6298
  await writeSSE(JSON.stringify({
@@ -4891,11 +6301,23 @@ agents.post(
4891
6301
  argsTextDelta: p.argsTextDelta
4892
6302
  }));
4893
6303
  } else if (part.type === "tool-call") {
6304
+ await emitSyntheticToolStreaming(
6305
+ writeSSE,
6306
+ toolCallStarts,
6307
+ part.toolCallId,
6308
+ part.toolName,
6309
+ part.input
6310
+ );
4894
6311
  await writeSSE(JSON.stringify({
4895
6312
  type: "tool-input-available",
4896
6313
  toolCallId: part.toolCallId,
4897
6314
  toolName: part.toolName,
4898
- input: part.input
6315
+ input: sanitizeToolInput(part.toolName, part.input)
6316
+ }));
6317
+ await writeSSE(JSON.stringify({
6318
+ type: "debug",
6319
+ label: "tool-input-available",
6320
+ toolName: part.toolName
4899
6321
  }));
4900
6322
  } else if (part.type === "tool-result") {
4901
6323
  await writeSSE(JSON.stringify({
@@ -4963,7 +6385,28 @@ agents.post(
4963
6385
  // src/server/routes/health.ts
4964
6386
  import { Hono as Hono3 } from "hono";
4965
6387
  import { zValidator as zValidator3 } from "@hono/zod-validator";
4966
- import { z as z11 } from "zod";
6388
+ import { z as z13 } from "zod";
6389
+ import { readFileSync as readFileSync3 } from "fs";
6390
+ import { fileURLToPath as fileURLToPath2 } from "url";
6391
+ import { dirname as dirname6, join as join6 } from "path";
6392
+ var __filename = fileURLToPath2(import.meta.url);
6393
+ var __dirname = dirname6(__filename);
6394
+ var packageJsonPath = join6(__dirname, "../../../package.json");
6395
+ var currentVersion = "0.0.0";
6396
+ var packageName = "sparkecoder";
6397
+ try {
6398
+ const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
6399
+ currentVersion = packageJson.version || "0.0.0";
6400
+ packageName = packageJson.name || "sparkecoder";
6401
+ } catch {
6402
+ try {
6403
+ const devPackageJsonPath = join6(__dirname, "../../package.json");
6404
+ const packageJson = JSON.parse(readFileSync3(devPackageJsonPath, "utf-8"));
6405
+ currentVersion = packageJson.version || "0.0.0";
6406
+ packageName = packageJson.name || "sparkecoder";
6407
+ } catch {
6408
+ }
6409
+ }
4967
6410
  var health = new Hono3();
4968
6411
  health.get("/", async (c) => {
4969
6412
  const config = getConfig();
@@ -4972,7 +6415,7 @@ health.get("/", async (c) => {
4972
6415
  const hasApiKey = gatewayKey?.configured ?? false;
4973
6416
  return c.json({
4974
6417
  status: "ok",
4975
- version: "0.1.0",
6418
+ version: currentVersion,
4976
6419
  uptime: process.uptime(),
4977
6420
  apiKeyConfigured: hasApiKey,
4978
6421
  config: {
@@ -4984,6 +6427,42 @@ health.get("/", async (c) => {
4984
6427
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4985
6428
  });
4986
6429
  });
6430
+ health.get("/version", async (c) => {
6431
+ let latestVersion = currentVersion;
6432
+ let updateAvailable = false;
6433
+ let error;
6434
+ try {
6435
+ const npmResponse = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
6436
+ headers: { "Accept": "application/json" },
6437
+ signal: AbortSignal.timeout(5e3)
6438
+ // 5 second timeout
6439
+ });
6440
+ if (npmResponse.ok) {
6441
+ const npmData = await npmResponse.json();
6442
+ latestVersion = npmData.version || currentVersion;
6443
+ const parseVersion = (v) => {
6444
+ const parts = v.replace(/^v/, "").split(".").map(Number);
6445
+ return { major: parts[0] || 0, minor: parts[1] || 0, patch: parts[2] || 0 };
6446
+ };
6447
+ const current = parseVersion(currentVersion);
6448
+ const latest = parseVersion(latestVersion);
6449
+ updateAvailable = latest.major > current.major || latest.major === current.major && latest.minor > current.minor || latest.major === current.major && latest.minor === current.minor && latest.patch > current.patch;
6450
+ } else {
6451
+ error = `npm registry returned ${npmResponse.status}`;
6452
+ }
6453
+ } catch (err) {
6454
+ error = err instanceof Error ? err.message : "Failed to check for updates";
6455
+ }
6456
+ return c.json({
6457
+ packageName,
6458
+ currentVersion,
6459
+ latestVersion,
6460
+ updateAvailable,
6461
+ updateCommand: updateAvailable ? `npm install -g ${packageName}@latest` : null,
6462
+ error,
6463
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
6464
+ });
6465
+ });
4987
6466
  health.get("/ready", async (c) => {
4988
6467
  try {
4989
6468
  getConfig();
@@ -5009,9 +6488,9 @@ health.get("/api-keys", async (c) => {
5009
6488
  supportedProviders: SUPPORTED_PROVIDERS
5010
6489
  });
5011
6490
  });
5012
- var setApiKeySchema = z11.object({
5013
- provider: z11.string(),
5014
- apiKey: z11.string().min(1)
6491
+ var setApiKeySchema = z13.object({
6492
+ provider: z13.string(),
6493
+ apiKey: z13.string().min(1)
5015
6494
  });
5016
6495
  health.post(
5017
6496
  "/api-keys",
@@ -5050,12 +6529,12 @@ health.delete("/api-keys/:provider", async (c) => {
5050
6529
  // src/server/routes/terminals.ts
5051
6530
  import { Hono as Hono4 } from "hono";
5052
6531
  import { zValidator as zValidator4 } from "@hono/zod-validator";
5053
- import { z as z12 } from "zod";
6532
+ import { z as z14 } from "zod";
5054
6533
  var terminals2 = new Hono4();
5055
- var spawnSchema = z12.object({
5056
- command: z12.string(),
5057
- cwd: z12.string().optional(),
5058
- name: z12.string().optional()
6534
+ var spawnSchema = z14.object({
6535
+ command: z14.string(),
6536
+ cwd: z14.string().optional(),
6537
+ name: z14.string().optional()
5059
6538
  });
5060
6539
  terminals2.post(
5061
6540
  "/:sessionId/terminals",
@@ -5136,8 +6615,8 @@ terminals2.get("/:sessionId/terminals/:terminalId", async (c) => {
5136
6615
  // We don't track exit codes in tmux mode
5137
6616
  });
5138
6617
  });
5139
- var logsQuerySchema = z12.object({
5140
- tail: z12.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
6618
+ var logsQuerySchema = z14.object({
6619
+ tail: z14.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
5141
6620
  });
5142
6621
  terminals2.get(
5143
6622
  "/:sessionId/terminals/:terminalId/logs",
@@ -5161,8 +6640,8 @@ terminals2.get(
5161
6640
  });
5162
6641
  }
5163
6642
  );
5164
- var killSchema = z12.object({
5165
- signal: z12.enum(["SIGTERM", "SIGKILL"]).optional()
6643
+ var killSchema = z14.object({
6644
+ signal: z14.enum(["SIGTERM", "SIGKILL"]).optional()
5166
6645
  });
5167
6646
  terminals2.post(
5168
6647
  "/:sessionId/terminals/:terminalId/kill",
@@ -5176,8 +6655,8 @@ terminals2.post(
5176
6655
  return c.json({ success: true, message: "Terminal killed" });
5177
6656
  }
5178
6657
  );
5179
- var writeSchema = z12.object({
5180
- input: z12.string()
6658
+ var writeSchema = z14.object({
6659
+ input: z14.string()
5181
6660
  });
5182
6661
  terminals2.post(
5183
6662
  "/:sessionId/terminals/:terminalId/write",
@@ -5359,10 +6838,10 @@ data: ${JSON.stringify({ status: "stopped" })}
5359
6838
  });
5360
6839
 
5361
6840
  // src/utils/dependencies.ts
5362
- import { exec as exec4 } from "child_process";
5363
- import { promisify as promisify4 } from "util";
6841
+ import { exec as exec5 } from "child_process";
6842
+ import { promisify as promisify5 } from "util";
5364
6843
  import { platform as platform2 } from "os";
5365
- var execAsync4 = promisify4(exec4);
6844
+ var execAsync5 = promisify5(exec5);
5366
6845
  function getInstallInstructions() {
5367
6846
  const os2 = platform2();
5368
6847
  if (os2 === "darwin") {
@@ -5395,7 +6874,7 @@ Install tmux:
5395
6874
  }
5396
6875
  async function checkTmux() {
5397
6876
  try {
5398
- const { stdout } = await execAsync4("tmux -V", { timeout: 5e3 });
6877
+ const { stdout } = await execAsync5("tmux -V", { timeout: 5e3 });
5399
6878
  const version = stdout.trim();
5400
6879
  return {
5401
6880
  available: true,
@@ -5439,21 +6918,21 @@ async function tryAutoInstallTmux() {
5439
6918
  try {
5440
6919
  if (os2 === "darwin") {
5441
6920
  try {
5442
- await execAsync4("which brew", { timeout: 5e3 });
6921
+ await execAsync5("which brew", { timeout: 5e3 });
5443
6922
  } catch {
5444
6923
  return false;
5445
6924
  }
5446
6925
  console.log("\u{1F4E6} Installing tmux via Homebrew...");
5447
- await execAsync4("brew install tmux", { timeout: 3e5 });
6926
+ await execAsync5("brew install tmux", { timeout: 3e5 });
5448
6927
  console.log("\u2705 tmux installed successfully");
5449
6928
  return true;
5450
6929
  }
5451
6930
  if (os2 === "linux") {
5452
6931
  try {
5453
- await execAsync4("which apt-get", { timeout: 5e3 });
6932
+ await execAsync5("which apt-get", { timeout: 5e3 });
5454
6933
  console.log("\u{1F4E6} Installing tmux via apt-get...");
5455
6934
  console.log(" (This may require sudo password)");
5456
- await execAsync4("sudo apt-get update && sudo apt-get install -y tmux", {
6935
+ await execAsync5("sudo apt-get update && sudo apt-get install -y tmux", {
5457
6936
  timeout: 3e5
5458
6937
  });
5459
6938
  console.log("\u2705 tmux installed successfully");
@@ -5461,9 +6940,9 @@ async function tryAutoInstallTmux() {
5461
6940
  } catch {
5462
6941
  }
5463
6942
  try {
5464
- await execAsync4("which dnf", { timeout: 5e3 });
6943
+ await execAsync5("which dnf", { timeout: 5e3 });
5465
6944
  console.log("\u{1F4E6} Installing tmux via dnf...");
5466
- await execAsync4("sudo dnf install -y tmux", { timeout: 3e5 });
6945
+ await execAsync5("sudo dnf install -y tmux", { timeout: 3e5 });
5467
6946
  console.log("\u2705 tmux installed successfully");
5468
6947
  return true;
5469
6948
  } catch {
@@ -5497,13 +6976,13 @@ var DEFAULT_WEB_PORT = 6969;
5497
6976
  var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
5498
6977
  function getWebDirectory() {
5499
6978
  try {
5500
- const currentDir = dirname6(fileURLToPath2(import.meta.url));
5501
- const webDir = resolve8(currentDir, "..", "web");
5502
- if (existsSync12(webDir) && existsSync12(join5(webDir, "package.json"))) {
6979
+ const currentDir = dirname7(fileURLToPath3(import.meta.url));
6980
+ const webDir = resolve9(currentDir, "..", "web");
6981
+ if (existsSync13(webDir) && existsSync13(join7(webDir, "package.json"))) {
5503
6982
  return webDir;
5504
6983
  }
5505
- const altWebDir = resolve8(currentDir, "..", "..", "web");
5506
- if (existsSync12(altWebDir) && existsSync12(join5(altWebDir, "package.json"))) {
6984
+ const altWebDir = resolve9(currentDir, "..", "..", "web");
6985
+ if (existsSync13(altWebDir) && existsSync13(join7(altWebDir, "package.json"))) {
5507
6986
  return altWebDir;
5508
6987
  }
5509
6988
  return null;
@@ -5526,18 +7005,18 @@ async function isSparkcoderWebRunning(port) {
5526
7005
  }
5527
7006
  }
5528
7007
  function isPortInUse(port) {
5529
- return new Promise((resolve10) => {
7008
+ return new Promise((resolve11) => {
5530
7009
  const server = createNetServer();
5531
7010
  server.once("error", (err) => {
5532
7011
  if (err.code === "EADDRINUSE") {
5533
- resolve10(true);
7012
+ resolve11(true);
5534
7013
  } else {
5535
- resolve10(false);
7014
+ resolve11(false);
5536
7015
  }
5537
7016
  });
5538
7017
  server.once("listening", () => {
5539
7018
  server.close();
5540
- resolve10(false);
7019
+ resolve11(false);
5541
7020
  });
5542
7021
  server.listen(port, "0.0.0.0");
5543
7022
  });
@@ -5561,30 +7040,30 @@ async function findWebPort(preferredPort) {
5561
7040
  return { port: preferredPort, alreadyRunning: false };
5562
7041
  }
5563
7042
  function hasProductionBuild(webDir) {
5564
- const buildIdPath = join5(webDir, ".next", "BUILD_ID");
5565
- return existsSync12(buildIdPath);
7043
+ const buildIdPath = join7(webDir, ".next", "BUILD_ID");
7044
+ return existsSync13(buildIdPath);
5566
7045
  }
5567
7046
  function hasSourceFiles(webDir) {
5568
- const appDir = join5(webDir, "src", "app");
5569
- const pagesDir = join5(webDir, "src", "pages");
5570
- const rootAppDir = join5(webDir, "app");
5571
- const rootPagesDir = join5(webDir, "pages");
5572
- return existsSync12(appDir) || existsSync12(pagesDir) || existsSync12(rootAppDir) || existsSync12(rootPagesDir);
7047
+ const appDir = join7(webDir, "src", "app");
7048
+ const pagesDir = join7(webDir, "src", "pages");
7049
+ const rootAppDir = join7(webDir, "app");
7050
+ const rootPagesDir = join7(webDir, "pages");
7051
+ return existsSync13(appDir) || existsSync13(pagesDir) || existsSync13(rootAppDir) || existsSync13(rootPagesDir);
5573
7052
  }
5574
7053
  function getStandaloneServerPath(webDir) {
5575
7054
  const possiblePaths = [
5576
- join5(webDir, ".next", "standalone", "server.js"),
5577
- join5(webDir, ".next", "standalone", "web", "server.js")
7055
+ join7(webDir, ".next", "standalone", "server.js"),
7056
+ join7(webDir, ".next", "standalone", "web", "server.js")
5578
7057
  ];
5579
7058
  for (const serverPath of possiblePaths) {
5580
- if (existsSync12(serverPath)) {
7059
+ if (existsSync13(serverPath)) {
5581
7060
  return serverPath;
5582
7061
  }
5583
7062
  }
5584
7063
  return null;
5585
7064
  }
5586
7065
  function runCommand(command, args, cwd, env) {
5587
- return new Promise((resolve10) => {
7066
+ return new Promise((resolve11) => {
5588
7067
  const child = spawn2(command, args, {
5589
7068
  cwd,
5590
7069
  stdio: ["ignore", "pipe", "pipe"],
@@ -5599,10 +7078,10 @@ function runCommand(command, args, cwd, env) {
5599
7078
  output += data.toString();
5600
7079
  });
5601
7080
  child.on("close", (code) => {
5602
- resolve10({ success: code === 0, output });
7081
+ resolve11({ success: code === 0, output });
5603
7082
  });
5604
7083
  child.on("error", (err) => {
5605
- resolve10({ success: false, output: err.message });
7084
+ resolve11({ success: false, output: err.message });
5606
7085
  });
5607
7086
  });
5608
7087
  }
@@ -5617,13 +7096,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
5617
7096
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
5618
7097
  return { process: null, port: actualPort };
5619
7098
  }
5620
- const usePnpm = existsSync12(join5(webDir, "pnpm-lock.yaml"));
5621
- const useNpm = !usePnpm && existsSync12(join5(webDir, "package-lock.json"));
7099
+ const usePnpm = existsSync13(join7(webDir, "pnpm-lock.yaml"));
7100
+ const useNpm = !usePnpm && existsSync13(join7(webDir, "package-lock.json"));
5622
7101
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
5623
7102
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
5624
7103
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
5625
7104
  const runtimeConfig = { apiBaseUrl: apiUrl };
5626
- const runtimeConfigPath = join5(webDir, "runtime-config.json");
7105
+ const runtimeConfigPath = join7(webDir, "runtime-config.json");
5627
7106
  try {
5628
7107
  writeFileSync4(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
5629
7108
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
@@ -5645,7 +7124,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
5645
7124
  if (standaloneServerPath) {
5646
7125
  command = "node";
5647
7126
  args = ["server.js"];
5648
- cwd = dirname6(standaloneServerPath);
7127
+ cwd = dirname7(standaloneServerPath);
5649
7128
  webEnv.PORT = String(actualPort);
5650
7129
  webEnv.HOSTNAME = "0.0.0.0";
5651
7130
  if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
@@ -5686,10 +7165,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
5686
7165
  let started = false;
5687
7166
  let exited = false;
5688
7167
  let exitCode = null;
5689
- const startedPromise = new Promise((resolve10) => {
7168
+ const startedPromise = new Promise((resolve11) => {
5690
7169
  const timeout = setTimeout(() => {
5691
7170
  if (!started && !exited) {
5692
- resolve10(false);
7171
+ resolve11(false);
5693
7172
  }
5694
7173
  }, startupTimeout);
5695
7174
  child.stdout?.on("data", (data) => {
@@ -5703,7 +7182,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
5703
7182
  if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
5704
7183
  started = true;
5705
7184
  clearTimeout(timeout);
5706
- resolve10(true);
7185
+ resolve11(true);
5707
7186
  }
5708
7187
  });
5709
7188
  child.stderr?.on("data", (data) => {
@@ -5715,14 +7194,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
5715
7194
  child.on("error", (err) => {
5716
7195
  if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
5717
7196
  clearTimeout(timeout);
5718
- resolve10(false);
7197
+ resolve11(false);
5719
7198
  });
5720
7199
  child.on("exit", (code) => {
5721
7200
  exited = true;
5722
7201
  exitCode = code;
5723
7202
  if (!started) {
5724
7203
  clearTimeout(timeout);
5725
- resolve10(false);
7204
+ resolve11(false);
5726
7205
  }
5727
7206
  webUIProcess = null;
5728
7207
  });
@@ -5815,7 +7294,7 @@ async function startServer(options = {}) {
5815
7294
  if (options.workingDirectory) {
5816
7295
  config.resolvedWorkingDirectory = options.workingDirectory;
5817
7296
  }
5818
- if (!existsSync12(config.resolvedWorkingDirectory)) {
7297
+ if (!existsSync13(config.resolvedWorkingDirectory)) {
5819
7298
  mkdirSync5(config.resolvedWorkingDirectory, { recursive: true });
5820
7299
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
5821
7300
  }
@@ -6319,8 +7798,8 @@ function generateOpenAPISpec() {
6319
7798
  }
6320
7799
 
6321
7800
  // src/cli.ts
6322
- import { writeFileSync as writeFileSync5, existsSync as existsSync13 } from "fs";
6323
- import { resolve as resolve9, join as join6 } from "path";
7801
+ import { writeFileSync as writeFileSync5, existsSync as existsSync14 } from "fs";
7802
+ import { resolve as resolve10, join as join8 } from "path";
6324
7803
  async function apiRequest(baseUrl, path, options = {}) {
6325
7804
  const url = `${baseUrl}${path}`;
6326
7805
  const init = {
@@ -6355,13 +7834,13 @@ async function getActiveStream(baseUrl, sessionId) {
6355
7834
  return { hasActiveStream: false };
6356
7835
  }
6357
7836
  function promptApproval(rl, toolName, input) {
6358
- return new Promise((resolve10) => {
7837
+ return new Promise((resolve11) => {
6359
7838
  const inputStr = JSON.stringify(input);
6360
7839
  const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
6361
7840
  console.log(chalk.dim(` Command: ${truncatedInput}`));
6362
7841
  rl.question(chalk.yellow(` Approve? [y/n]: `), (answer) => {
6363
7842
  const approved = answer.toLowerCase().startsWith("y");
6364
- resolve10(approved);
7843
+ resolve11(approved);
6365
7844
  });
6366
7845
  });
6367
7846
  }
@@ -6544,9 +8023,9 @@ async function runChat(options) {
6544
8023
  input: process.stdin,
6545
8024
  output: process.stdout
6546
8025
  });
6547
- const apiKey = await new Promise((resolve10) => {
8026
+ const apiKey = await new Promise((resolve11) => {
6548
8027
  keyRl.question(chalk.cyan("Enter your AI Gateway API key: "), (answer) => {
6549
- resolve10(answer.trim());
8028
+ resolve11(answer.trim());
6550
8029
  });
6551
8030
  });
6552
8031
  keyRl.close();
@@ -6877,13 +8356,13 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
6877
8356
  let configLocation;
6878
8357
  if (options.global) {
6879
8358
  const appDataDir = ensureAppDataDirectory();
6880
- configPath = join6(appDataDir, "sparkecoder.config.json");
8359
+ configPath = join8(appDataDir, "sparkecoder.config.json");
6881
8360
  configLocation = "global";
6882
8361
  } else {
6883
- configPath = resolve9(process.cwd(), "sparkecoder.config.json");
8362
+ configPath = resolve10(process.cwd(), "sparkecoder.config.json");
6884
8363
  configLocation = "local";
6885
8364
  }
6886
- if (existsSync13(configPath) && !options.force) {
8365
+ if (existsSync14(configPath) && !options.force) {
6887
8366
  console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
6888
8367
  console.log(chalk.dim(` ${configPath}`));
6889
8368
  return;