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.
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1308 -212
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +1933 -454
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +20 -3
- package/dist/db/index.js +97 -0
- package/dist/db/index.js.map +1 -1
- package/dist/{index-CNwLFGiZ.d.ts → index-BblbmG_0.d.ts} +19 -4
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1913 -434
- package/dist/index.js.map +1 -1
- package/dist/{schema-Df7MU3nM.d.ts → schema-D_8A4k01.d.ts} +246 -2
- package/dist/search-ybREg7F_.d.ts +254 -0
- package/dist/server/index.js +1918 -439
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +7 -56
- package/dist/tools/index.js +894 -27
- package/dist/tools/index.js.map +1 -1
- package/package.json +5 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/app-path-routes-manifest.json +4 -0
- package/web/.next/standalone/web/.next/build-manifest.json +7 -6
- package/web/.next/standalone/web/.next/prerender-manifest.json +99 -3
- package/web/.next/standalone/web/.next/required-server-files.json +28 -4
- package/web/.next/standalone/web/.next/routes-manifest.json +24 -0
- package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/(main)/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/_global-error/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/_not-found/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +86 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +36 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +36 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +22 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +268 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +82 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +82 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +66 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +242 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +87 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +87 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +72 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs.html +74 -0
- package/web/.next/standalone/web/.next/server/app/docs.meta +15 -0
- package/web/.next/standalone/web/.next/server/app/docs.rsc +34 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +34 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +20 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app-paths-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5f58fd73._.js → 2374f_244589df._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_84859a94._.js → 2374f_41a27541._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_65fcfd95._.js → 2374f_47c9e2d5._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f1038f7c._.js → 2374f_4bf2df9d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_663d1038._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_954e49c0._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_387a1437._.js → 2374f_c33b095a._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_cfd0137a._.js → 2374f_fa61fbb2._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_741f6b67._.js → 2374f_fb82ac0d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_96bca05b._.js → 2374f_next_dist_bbe64674._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7f04455b._.js → [root-of-the-server]__1e06ddf7._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2b151e1c._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2dbf511a._.js +9 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__397fadd4._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44bd8bd1._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__70cecda8._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9fdf9974._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f18f92f4._.js → [root-of-the-server]__b050bb8f._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d3034cd2._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c3a1e22c._.js → [root-of-the-server]__ef2713cf._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f764bebe._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_046bf7db._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_656c1e45._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_c7618534._.js → web_76ccf09f._.js} +4 -4
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_installation_page_actions_52cc0648.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_page_actions_4fe77da8.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_skills_page_actions_251df2e1.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_tools_page_actions_3e6382b0.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_a565dc94._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_b1cce0b7._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_b42ed1be._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_c0c2bee4._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_eea9c122._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_ff00a5c3._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +3 -0
- package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +5 -4
- package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/next-font-manifest.json +16 -0
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/standalone/web/.next/static/{static/chunks/5e5b485d77ac0d8f.js → chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/standalone/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/af22745850132107.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/.next/standalone/web/.next/static/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/{static/chunks/5e5b485d77ac0d8f.js → standalone/web/.next/static/static/chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/standalone/web/.next/static/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/af22745850132107.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/.next/standalone/web/mdx-components.tsx +119 -0
- package/web/.next/standalone/web/next.config.ts +15 -1
- package/web/.next/standalone/web/package-lock.json +559 -4
- package/web/.next/standalone/web/package.json +4 -0
- package/web/.next/standalone/web/runtime-config.json +1 -1
- package/web/.next/standalone/web/server.js +1 -1
- package/web/.next/standalone/web/src/app/(main)/page.tsx +127 -5
- package/web/.next/standalone/web/src/app/docs/installation/page.mdx +128 -0
- package/web/.next/standalone/web/src/app/docs/layout.tsx +74 -0
- package/web/.next/standalone/web/src/app/docs/page.mdx +90 -0
- package/web/.next/standalone/web/src/app/docs/skills/page.mdx +334 -0
- package/web/.next/standalone/web/src/app/docs/tools/page.mdx +300 -0
- package/web/.next/standalone/web/src/components/ai-elements/mention-input.tsx +809 -0
- package/web/.next/standalone/web/src/components/ai-elements/search-tool.tsx +400 -0
- package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +275 -0
- package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +19 -5
- package/web/.next/standalone/web/src/components/chat-interface.tsx +525 -71
- package/web/.next/standalone/web/src/hooks/use-workspace-files.ts +108 -0
- package/web/.next/standalone/web/src/lib/api.ts +90 -4
- package/web/.next/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/{standalone/web/.next/static/chunks/5e5b485d77ac0d8f.js → static/chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/static/chunks/af22745850132107.css +1 -0
- package/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/package.json +4 -0
- package/dist/bash-CGAqW7HR.d.ts +0 -80
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_814be2c9._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__3ec22171._.js +0 -9
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__513c6b45._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +0 -7
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_e6034803._.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/standalone/web/.next/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- package/web/.next/standalone/web/.next/static/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/3bb454ca848ec78e.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/standalone/web/.next/static/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/cb355fac10c6ad11.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- package/web/.next/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
- package/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
- package/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
- /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
package/dist/agent/index.js
CHANGED
|
@@ -1,13 +1,426 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/config/types.ts
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, SparkcoderConfigSchema;
|
|
14
|
+
var init_types = __esm({
|
|
15
|
+
"src/config/types.ts"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
ToolApprovalConfigSchema = z.object({
|
|
18
|
+
bash: z.boolean().optional().default(true),
|
|
19
|
+
write_file: z.boolean().optional().default(false),
|
|
20
|
+
read_file: z.boolean().optional().default(false),
|
|
21
|
+
load_skill: z.boolean().optional().default(false),
|
|
22
|
+
todo: z.boolean().optional().default(false)
|
|
23
|
+
});
|
|
24
|
+
SkillMetadataSchema = z.object({
|
|
25
|
+
name: z.string(),
|
|
26
|
+
description: z.string(),
|
|
27
|
+
// Whether to always inject this skill into context (vs on-demand loading)
|
|
28
|
+
alwaysApply: z.boolean().optional().default(false),
|
|
29
|
+
// Glob patterns - auto-inject when working with matching files
|
|
30
|
+
globs: z.array(z.string()).optional().default([])
|
|
31
|
+
});
|
|
32
|
+
SessionConfigSchema = z.object({
|
|
33
|
+
toolApprovals: z.record(z.string(), z.boolean()).optional(),
|
|
34
|
+
approvalWebhook: z.string().url().optional(),
|
|
35
|
+
skillsDirectory: z.string().optional(),
|
|
36
|
+
maxContextChars: z.number().optional().default(2e5)
|
|
37
|
+
});
|
|
38
|
+
SparkcoderConfigSchema = z.object({
|
|
39
|
+
// Default model to use (Vercel AI Gateway format)
|
|
40
|
+
defaultModel: z.string().default("anthropic/claude-opus-4-5"),
|
|
41
|
+
// Working directory for file operations
|
|
42
|
+
workingDirectory: z.string().optional(),
|
|
43
|
+
// Tool approval settings
|
|
44
|
+
toolApprovals: ToolApprovalConfigSchema.optional().default({}),
|
|
45
|
+
// Approval webhook URL (called when approval is needed)
|
|
46
|
+
approvalWebhook: z.string().url().optional(),
|
|
47
|
+
// Skills configuration
|
|
48
|
+
skills: z.object({
|
|
49
|
+
// Directory containing skill files
|
|
50
|
+
directory: z.string().optional().default("./skills"),
|
|
51
|
+
// Additional skill directories to include
|
|
52
|
+
additionalDirectories: z.array(z.string()).optional().default([])
|
|
53
|
+
}).optional().default({}),
|
|
54
|
+
// Context management
|
|
55
|
+
context: z.object({
|
|
56
|
+
// Maximum context size before summarization (in characters)
|
|
57
|
+
maxChars: z.number().optional().default(2e5),
|
|
58
|
+
// Enable automatic summarization
|
|
59
|
+
autoSummarize: z.boolean().optional().default(true),
|
|
60
|
+
// Number of recent messages to keep after summarization
|
|
61
|
+
keepRecentMessages: z.number().optional().default(10)
|
|
62
|
+
}).optional().default({}),
|
|
63
|
+
// Server configuration
|
|
64
|
+
server: z.object({
|
|
65
|
+
port: z.number().default(3141),
|
|
66
|
+
host: z.string().default("127.0.0.1"),
|
|
67
|
+
// Public URL for web UI to connect to API (for Docker/remote access)
|
|
68
|
+
// If not set, defaults to http://{host}:{port}
|
|
69
|
+
publicUrl: z.string().url().optional()
|
|
70
|
+
}).default({ port: 3141, host: "127.0.0.1" }),
|
|
71
|
+
// Database path
|
|
72
|
+
databasePath: z.string().optional().default("./sparkecoder.db")
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// src/skills/index.ts
|
|
78
|
+
var skills_exports = {};
|
|
79
|
+
__export(skills_exports, {
|
|
80
|
+
formatAgentsMdContent: () => formatAgentsMdContent,
|
|
81
|
+
formatAlwaysLoadedSkills: () => formatAlwaysLoadedSkills,
|
|
82
|
+
formatGlobMatchedSkills: () => formatGlobMatchedSkills,
|
|
83
|
+
formatSkillsForContext: () => formatSkillsForContext,
|
|
84
|
+
getGlobMatchedSkills: () => getGlobMatchedSkills,
|
|
85
|
+
loadAgentsMd: () => loadAgentsMd,
|
|
86
|
+
loadAllSkills: () => loadAllSkills,
|
|
87
|
+
loadAllSkillsFromDiscovered: () => loadAllSkillsFromDiscovered,
|
|
88
|
+
loadSkillContent: () => loadSkillContent,
|
|
89
|
+
loadSkillsFromDirectory: () => loadSkillsFromDirectory
|
|
90
|
+
});
|
|
91
|
+
import { readFile as readFile6, readdir } from "fs/promises";
|
|
92
|
+
import { resolve as resolve6, basename, extname as extname3, relative as relative4 } from "path";
|
|
93
|
+
import { existsSync as existsSync8 } from "fs";
|
|
94
|
+
import { minimatch } from "minimatch";
|
|
95
|
+
function parseSkillFrontmatter(content) {
|
|
96
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
97
|
+
if (!frontmatterMatch) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const [, frontmatter, body] = frontmatterMatch;
|
|
101
|
+
try {
|
|
102
|
+
const lines = frontmatter.split("\n");
|
|
103
|
+
const data = {};
|
|
104
|
+
let currentArray = null;
|
|
105
|
+
let currentArrayKey = null;
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (currentArrayKey && line.trim().startsWith("-")) {
|
|
108
|
+
let value = line.trim().slice(1).trim();
|
|
109
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
110
|
+
value = value.slice(1, -1);
|
|
111
|
+
}
|
|
112
|
+
currentArray?.push(value);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (currentArrayKey && currentArray) {
|
|
116
|
+
data[currentArrayKey] = currentArray;
|
|
117
|
+
currentArray = null;
|
|
118
|
+
currentArrayKey = null;
|
|
119
|
+
}
|
|
120
|
+
const colonIndex = line.indexOf(":");
|
|
121
|
+
if (colonIndex > 0) {
|
|
122
|
+
const key = line.slice(0, colonIndex).trim();
|
|
123
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
124
|
+
if (value === "" || value === "[]") {
|
|
125
|
+
currentArrayKey = key;
|
|
126
|
+
currentArray = [];
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
130
|
+
const arrayContent = value.slice(1, -1);
|
|
131
|
+
const items = arrayContent.split(",").map((item) => {
|
|
132
|
+
let trimmed = item.trim();
|
|
133
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
134
|
+
trimmed = trimmed.slice(1, -1);
|
|
135
|
+
}
|
|
136
|
+
return trimmed;
|
|
137
|
+
}).filter((item) => item.length > 0);
|
|
138
|
+
data[key] = items;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
142
|
+
value = value.slice(1, -1);
|
|
143
|
+
}
|
|
144
|
+
if (value === "true") {
|
|
145
|
+
data[key] = true;
|
|
146
|
+
} else if (value === "false") {
|
|
147
|
+
data[key] = false;
|
|
148
|
+
} else {
|
|
149
|
+
data[key] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (currentArrayKey && currentArray) {
|
|
154
|
+
data[currentArrayKey] = currentArray;
|
|
155
|
+
}
|
|
156
|
+
const metadata = SkillMetadataSchema.parse(data);
|
|
157
|
+
return { metadata, body: body.trim() };
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function getSkillNameFromPath(filePath) {
|
|
163
|
+
return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
164
|
+
}
|
|
165
|
+
async function loadSkillsFromDirectory(directory, options = {}) {
|
|
166
|
+
const {
|
|
167
|
+
priority = 50,
|
|
168
|
+
defaultLoadType = "on_demand",
|
|
169
|
+
forceAlwaysApply = false
|
|
170
|
+
} = options;
|
|
171
|
+
if (!existsSync8(directory)) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
const skills = [];
|
|
175
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
let filePath;
|
|
178
|
+
let fileName;
|
|
179
|
+
if (entry.isDirectory()) {
|
|
180
|
+
const skillMdPath = resolve6(directory, entry.name, "SKILL.md");
|
|
181
|
+
if (existsSync8(skillMdPath)) {
|
|
182
|
+
filePath = skillMdPath;
|
|
183
|
+
fileName = entry.name;
|
|
184
|
+
} else {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
} else if (entry.name.endsWith(".md") || entry.name.endsWith(".mdc")) {
|
|
188
|
+
filePath = resolve6(directory, entry.name);
|
|
189
|
+
fileName = entry.name;
|
|
190
|
+
} else {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const content = await readFile6(filePath, "utf-8");
|
|
194
|
+
const parsed = parseSkillFrontmatter(content);
|
|
195
|
+
if (parsed) {
|
|
196
|
+
const alwaysApply = forceAlwaysApply || parsed.metadata.alwaysApply;
|
|
197
|
+
const loadType = alwaysApply ? "always" : defaultLoadType;
|
|
198
|
+
skills.push({
|
|
199
|
+
name: parsed.metadata.name,
|
|
200
|
+
description: parsed.metadata.description,
|
|
201
|
+
filePath,
|
|
202
|
+
alwaysApply,
|
|
203
|
+
globs: parsed.metadata.globs,
|
|
204
|
+
loadType,
|
|
205
|
+
priority,
|
|
206
|
+
sourceDir: directory
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
const name = getSkillNameFromPath(filePath);
|
|
210
|
+
const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
|
|
211
|
+
skills.push({
|
|
212
|
+
name,
|
|
213
|
+
description: firstParagraph.replace(/^#\s*/, "").trim(),
|
|
214
|
+
filePath,
|
|
215
|
+
alwaysApply: forceAlwaysApply,
|
|
216
|
+
globs: [],
|
|
217
|
+
loadType: forceAlwaysApply ? "always" : defaultLoadType,
|
|
218
|
+
priority,
|
|
219
|
+
sourceDir: directory
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return skills;
|
|
224
|
+
}
|
|
225
|
+
async function loadAllSkills(directories) {
|
|
226
|
+
const allSkills = [];
|
|
227
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
228
|
+
for (const dir of directories) {
|
|
229
|
+
const skills = await loadSkillsFromDirectory(dir);
|
|
230
|
+
for (const skill of skills) {
|
|
231
|
+
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
232
|
+
seenNames.add(skill.name.toLowerCase());
|
|
233
|
+
allSkills.push(skill);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return allSkills;
|
|
238
|
+
}
|
|
239
|
+
async function loadAllSkillsFromDiscovered(discovered) {
|
|
240
|
+
const allSkills = [];
|
|
241
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
242
|
+
for (const { path, priority } of discovered.alwaysLoadedDirs) {
|
|
243
|
+
const skills = await loadSkillsFromDirectory(path, {
|
|
244
|
+
priority,
|
|
245
|
+
defaultLoadType: "always",
|
|
246
|
+
forceAlwaysApply: true
|
|
247
|
+
});
|
|
248
|
+
for (const skill of skills) {
|
|
249
|
+
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
250
|
+
seenNames.add(skill.name.toLowerCase());
|
|
251
|
+
allSkills.push(skill);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
for (const { path, priority } of discovered.onDemandDirs) {
|
|
256
|
+
const skills = await loadSkillsFromDirectory(path, {
|
|
257
|
+
priority,
|
|
258
|
+
defaultLoadType: "on_demand",
|
|
259
|
+
forceAlwaysApply: false
|
|
260
|
+
});
|
|
261
|
+
for (const skill of skills) {
|
|
262
|
+
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
263
|
+
seenNames.add(skill.name.toLowerCase());
|
|
264
|
+
allSkills.push(skill);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const alwaysSkills = allSkills.filter((s) => s.alwaysApply || s.loadType === "always");
|
|
269
|
+
const onDemandSkills = allSkills.filter((s) => !s.alwaysApply && s.loadType !== "always");
|
|
270
|
+
const alwaysWithContent = await Promise.all(
|
|
271
|
+
alwaysSkills.map(async (skill) => {
|
|
272
|
+
const content = await readFile6(skill.filePath, "utf-8");
|
|
273
|
+
const parsed = parseSkillFrontmatter(content);
|
|
274
|
+
return {
|
|
275
|
+
...skill,
|
|
276
|
+
content: parsed ? parsed.body : content
|
|
277
|
+
};
|
|
278
|
+
})
|
|
279
|
+
);
|
|
280
|
+
return {
|
|
281
|
+
always: alwaysWithContent,
|
|
282
|
+
onDemand: onDemandSkills,
|
|
283
|
+
all: allSkills
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
|
|
287
|
+
if (activeFiles.length === 0) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
const relativeFiles = activeFiles.map((f) => {
|
|
291
|
+
if (f.startsWith(workingDirectory)) {
|
|
292
|
+
return relative4(workingDirectory, f);
|
|
293
|
+
}
|
|
294
|
+
return f;
|
|
295
|
+
});
|
|
296
|
+
const matchedSkills = skills.filter((skill) => {
|
|
297
|
+
if (skill.alwaysApply || skill.loadType === "always") {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (!skill.globs || skill.globs.length === 0) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
return relativeFiles.some(
|
|
304
|
+
(file) => skill.globs.some((pattern) => minimatch(file, pattern, { matchBase: true }))
|
|
305
|
+
);
|
|
306
|
+
});
|
|
307
|
+
const matchedWithContent = await Promise.all(
|
|
308
|
+
matchedSkills.map(async (skill) => {
|
|
309
|
+
const content = await readFile6(skill.filePath, "utf-8");
|
|
310
|
+
const parsed = parseSkillFrontmatter(content);
|
|
311
|
+
return {
|
|
312
|
+
...skill,
|
|
313
|
+
content: parsed ? parsed.body : content,
|
|
314
|
+
loadType: "glob_matched"
|
|
315
|
+
};
|
|
316
|
+
})
|
|
317
|
+
);
|
|
318
|
+
return matchedWithContent;
|
|
319
|
+
}
|
|
320
|
+
async function loadAgentsMd(agentsMdPath) {
|
|
321
|
+
if (!agentsMdPath || !existsSync8(agentsMdPath)) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
const content = await readFile6(agentsMdPath, "utf-8");
|
|
325
|
+
return content;
|
|
326
|
+
}
|
|
327
|
+
async function loadSkillContent(skillName, directories) {
|
|
328
|
+
const allSkills = await loadAllSkills(directories);
|
|
329
|
+
const skill = allSkills.find(
|
|
330
|
+
(s) => s.name.toLowerCase() === skillName.toLowerCase()
|
|
331
|
+
);
|
|
332
|
+
if (!skill) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
const content = await readFile6(skill.filePath, "utf-8");
|
|
336
|
+
const parsed = parseSkillFrontmatter(content);
|
|
337
|
+
return {
|
|
338
|
+
...skill,
|
|
339
|
+
content: parsed ? parsed.body : content
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function formatSkillsForContext(skills) {
|
|
343
|
+
const onDemandSkills = skills.filter((s) => !s.alwaysApply && s.loadType !== "always");
|
|
344
|
+
if (onDemandSkills.length === 0) {
|
|
345
|
+
return "No on-demand skills available.";
|
|
346
|
+
}
|
|
347
|
+
const lines = ["Available skills (use load_skill tool to load into context):"];
|
|
348
|
+
for (const skill of onDemandSkills) {
|
|
349
|
+
const globInfo = skill.globs?.length ? ` [auto-loads for: ${skill.globs.join(", ")}]` : "";
|
|
350
|
+
lines.push(`- ${skill.name}: ${skill.description}${globInfo}`);
|
|
351
|
+
}
|
|
352
|
+
return lines.join("\n");
|
|
353
|
+
}
|
|
354
|
+
function formatAlwaysLoadedSkills(skills) {
|
|
355
|
+
if (skills.length === 0) {
|
|
356
|
+
return "";
|
|
357
|
+
}
|
|
358
|
+
const sections = [];
|
|
359
|
+
for (const skill of skills) {
|
|
360
|
+
sections.push(`### ${skill.name}
|
|
361
|
+
|
|
362
|
+
${skill.content}`);
|
|
363
|
+
}
|
|
364
|
+
return `## Active Rules & Skills (Always Loaded)
|
|
365
|
+
|
|
366
|
+
${sections.join("\n\n---\n\n")}`;
|
|
367
|
+
}
|
|
368
|
+
function formatGlobMatchedSkills(skills) {
|
|
369
|
+
if (skills.length === 0) {
|
|
370
|
+
return "";
|
|
371
|
+
}
|
|
372
|
+
const sections = [];
|
|
373
|
+
for (const skill of skills) {
|
|
374
|
+
sections.push(`### ${skill.name}
|
|
375
|
+
|
|
376
|
+
${skill.content}`);
|
|
377
|
+
}
|
|
378
|
+
return `## Context-Relevant Skills (Auto-loaded based on active files)
|
|
379
|
+
|
|
380
|
+
${sections.join("\n\n---\n\n")}`;
|
|
381
|
+
}
|
|
382
|
+
function formatAgentsMdContent(content) {
|
|
383
|
+
if (!content) {
|
|
384
|
+
return "";
|
|
385
|
+
}
|
|
386
|
+
return `## Project Instructions (AGENTS.md)
|
|
387
|
+
|
|
388
|
+
${content}`;
|
|
389
|
+
}
|
|
390
|
+
var init_skills = __esm({
|
|
391
|
+
"src/skills/index.ts"() {
|
|
392
|
+
"use strict";
|
|
393
|
+
init_types();
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
1
397
|
// src/agent/index.ts
|
|
2
398
|
import {
|
|
3
|
-
streamText,
|
|
4
|
-
generateText as
|
|
5
|
-
tool as
|
|
6
|
-
stepCountIs
|
|
399
|
+
streamText as streamText2,
|
|
400
|
+
generateText as generateText3,
|
|
401
|
+
tool as tool9,
|
|
402
|
+
stepCountIs as stepCountIs2
|
|
7
403
|
} from "ai";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
404
|
+
|
|
405
|
+
// src/agent/model.ts
|
|
406
|
+
import { gateway } from "@ai-sdk/gateway";
|
|
407
|
+
var ANTHROPIC_PREFIX = "anthropic/";
|
|
408
|
+
function isAnthropicModel(modelId) {
|
|
409
|
+
const normalized = modelId.trim().toLowerCase();
|
|
410
|
+
return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
|
|
411
|
+
}
|
|
412
|
+
function resolveModel(modelId) {
|
|
413
|
+
return gateway(modelId.trim());
|
|
414
|
+
}
|
|
415
|
+
var SUBAGENT_MODELS = {
|
|
416
|
+
search: "google/gemini-2.0-flash",
|
|
417
|
+
analyze: "google/gemini-2.0-flash",
|
|
418
|
+
default: "google/gemini-2.0-flash"
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/agent/index.ts
|
|
422
|
+
import { z as z10 } from "zod";
|
|
423
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
11
424
|
|
|
12
425
|
// src/db/index.ts
|
|
13
426
|
import Database from "better-sqlite3";
|
|
@@ -115,6 +528,26 @@ var fileBackups = sqliteTable("file_backups", {
|
|
|
115
528
|
existed: integer("existed", { mode: "boolean" }).notNull().default(true),
|
|
116
529
|
createdAt: integer("created_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date())
|
|
117
530
|
});
|
|
531
|
+
var subagentExecutions = sqliteTable("subagent_executions", {
|
|
532
|
+
id: text("id").primaryKey(),
|
|
533
|
+
sessionId: text("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
534
|
+
toolCallId: text("tool_call_id").notNull(),
|
|
535
|
+
// The tool call that spawned this subagent
|
|
536
|
+
subagentType: text("subagent_type").notNull(),
|
|
537
|
+
// e.g., 'search', 'analyze', etc.
|
|
538
|
+
task: text("task").notNull(),
|
|
539
|
+
// The task/query given to the subagent
|
|
540
|
+
model: text("model").notNull(),
|
|
541
|
+
// The model used (e.g., 'gemini-2.0-flash')
|
|
542
|
+
status: text("status", { enum: ["running", "completed", "error", "cancelled"] }).notNull().default("running"),
|
|
543
|
+
// Steps taken by the subagent (stored as JSON array)
|
|
544
|
+
steps: text("steps", { mode: "json" }).$type().default([]),
|
|
545
|
+
// Final result/output
|
|
546
|
+
result: text("result", { mode: "json" }),
|
|
547
|
+
error: text("error"),
|
|
548
|
+
startedAt: integer("started_at", { mode: "timestamp" }).notNull().$defaultFn(() => /* @__PURE__ */ new Date()),
|
|
549
|
+
completedAt: integer("completed_at", { mode: "timestamp" })
|
|
550
|
+
});
|
|
118
551
|
|
|
119
552
|
// src/db/index.ts
|
|
120
553
|
var db = null;
|
|
@@ -420,69 +853,70 @@ var fileBackupQueries = {
|
|
|
420
853
|
return result.changes;
|
|
421
854
|
}
|
|
422
855
|
};
|
|
856
|
+
var subagentQueries = {
|
|
857
|
+
create(data) {
|
|
858
|
+
const id = nanoid();
|
|
859
|
+
const result = getDb().insert(subagentExecutions).values({
|
|
860
|
+
id,
|
|
861
|
+
sessionId: data.sessionId,
|
|
862
|
+
toolCallId: data.toolCallId,
|
|
863
|
+
subagentType: data.subagentType,
|
|
864
|
+
task: data.task,
|
|
865
|
+
model: data.model,
|
|
866
|
+
status: "running",
|
|
867
|
+
steps: [],
|
|
868
|
+
startedAt: /* @__PURE__ */ new Date()
|
|
869
|
+
}).returning().get();
|
|
870
|
+
return result;
|
|
871
|
+
},
|
|
872
|
+
getById(id) {
|
|
873
|
+
return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.id, id)).get();
|
|
874
|
+
},
|
|
875
|
+
getByToolCallId(toolCallId) {
|
|
876
|
+
return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.toolCallId, toolCallId)).get();
|
|
877
|
+
},
|
|
878
|
+
getBySession(sessionId) {
|
|
879
|
+
return getDb().select().from(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).orderBy(desc(subagentExecutions.startedAt)).all();
|
|
880
|
+
},
|
|
881
|
+
addStep(id, step) {
|
|
882
|
+
const existing = this.getById(id);
|
|
883
|
+
if (!existing) return void 0;
|
|
884
|
+
const currentSteps = existing.steps || [];
|
|
885
|
+
const newSteps = [...currentSteps, step];
|
|
886
|
+
return getDb().update(subagentExecutions).set({ steps: newSteps }).where(eq(subagentExecutions.id, id)).returning().get();
|
|
887
|
+
},
|
|
888
|
+
complete(id, result) {
|
|
889
|
+
return getDb().update(subagentExecutions).set({
|
|
890
|
+
status: "completed",
|
|
891
|
+
result,
|
|
892
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
893
|
+
}).where(eq(subagentExecutions.id, id)).returning().get();
|
|
894
|
+
},
|
|
895
|
+
markError(id, error) {
|
|
896
|
+
return getDb().update(subagentExecutions).set({
|
|
897
|
+
status: "error",
|
|
898
|
+
error,
|
|
899
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
900
|
+
}).where(eq(subagentExecutions.id, id)).returning().get();
|
|
901
|
+
},
|
|
902
|
+
cancel(id) {
|
|
903
|
+
return getDb().update(subagentExecutions).set({
|
|
904
|
+
status: "cancelled",
|
|
905
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
906
|
+
}).where(eq(subagentExecutions.id, id)).returning().get();
|
|
907
|
+
},
|
|
908
|
+
deleteBySession(sessionId) {
|
|
909
|
+
const result = getDb().delete(subagentExecutions).where(eq(subagentExecutions.sessionId, sessionId)).run();
|
|
910
|
+
return result.changes;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
423
913
|
|
|
424
914
|
// src/config/index.ts
|
|
915
|
+
init_types();
|
|
916
|
+
init_types();
|
|
425
917
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
426
918
|
import { resolve, dirname, join } from "path";
|
|
427
919
|
import { homedir, platform } from "os";
|
|
428
|
-
|
|
429
|
-
// src/config/types.ts
|
|
430
|
-
import { z } from "zod";
|
|
431
|
-
var ToolApprovalConfigSchema = z.object({
|
|
432
|
-
bash: z.boolean().optional().default(true),
|
|
433
|
-
write_file: z.boolean().optional().default(false),
|
|
434
|
-
read_file: z.boolean().optional().default(false),
|
|
435
|
-
load_skill: z.boolean().optional().default(false),
|
|
436
|
-
todo: z.boolean().optional().default(false)
|
|
437
|
-
});
|
|
438
|
-
var SkillMetadataSchema = z.object({
|
|
439
|
-
name: z.string(),
|
|
440
|
-
description: z.string()
|
|
441
|
-
});
|
|
442
|
-
var SessionConfigSchema = z.object({
|
|
443
|
-
toolApprovals: z.record(z.string(), z.boolean()).optional(),
|
|
444
|
-
approvalWebhook: z.string().url().optional(),
|
|
445
|
-
skillsDirectory: z.string().optional(),
|
|
446
|
-
maxContextChars: z.number().optional().default(2e5)
|
|
447
|
-
});
|
|
448
|
-
var SparkcoderConfigSchema = z.object({
|
|
449
|
-
// Default model to use (Vercel AI Gateway format)
|
|
450
|
-
defaultModel: z.string().default("anthropic/claude-opus-4-5"),
|
|
451
|
-
// Working directory for file operations
|
|
452
|
-
workingDirectory: z.string().optional(),
|
|
453
|
-
// Tool approval settings
|
|
454
|
-
toolApprovals: ToolApprovalConfigSchema.optional().default({}),
|
|
455
|
-
// Approval webhook URL (called when approval is needed)
|
|
456
|
-
approvalWebhook: z.string().url().optional(),
|
|
457
|
-
// Skills configuration
|
|
458
|
-
skills: z.object({
|
|
459
|
-
// Directory containing skill files
|
|
460
|
-
directory: z.string().optional().default("./skills"),
|
|
461
|
-
// Additional skill directories to include
|
|
462
|
-
additionalDirectories: z.array(z.string()).optional().default([])
|
|
463
|
-
}).optional().default({}),
|
|
464
|
-
// Context management
|
|
465
|
-
context: z.object({
|
|
466
|
-
// Maximum context size before summarization (in characters)
|
|
467
|
-
maxChars: z.number().optional().default(2e5),
|
|
468
|
-
// Enable automatic summarization
|
|
469
|
-
autoSummarize: z.boolean().optional().default(true),
|
|
470
|
-
// Number of recent messages to keep after summarization
|
|
471
|
-
keepRecentMessages: z.number().optional().default(10)
|
|
472
|
-
}).optional().default({}),
|
|
473
|
-
// Server configuration
|
|
474
|
-
server: z.object({
|
|
475
|
-
port: z.number().default(3141),
|
|
476
|
-
host: z.string().default("127.0.0.1"),
|
|
477
|
-
// Public URL for web UI to connect to API (for Docker/remote access)
|
|
478
|
-
// If not set, defaults to http://{host}:{port}
|
|
479
|
-
publicUrl: z.string().url().optional()
|
|
480
|
-
}).default({ port: 3141, host: "127.0.0.1" }),
|
|
481
|
-
// Database path
|
|
482
|
-
databasePath: z.string().optional().default("./sparkecoder.db")
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// src/config/index.ts
|
|
486
920
|
function getAppDataDirectory() {
|
|
487
921
|
const appName = "sparkecoder";
|
|
488
922
|
switch (platform()) {
|
|
@@ -1228,12 +1662,12 @@ function findNearestRoot(startDir, markers) {
|
|
|
1228
1662
|
}
|
|
1229
1663
|
async function commandExists(cmd) {
|
|
1230
1664
|
try {
|
|
1231
|
-
const { exec:
|
|
1232
|
-
const { promisify:
|
|
1233
|
-
const
|
|
1665
|
+
const { exec: exec5 } = await import("child_process");
|
|
1666
|
+
const { promisify: promisify5 } = await import("util");
|
|
1667
|
+
const execAsync5 = promisify5(exec5);
|
|
1234
1668
|
const isWindows = process.platform === "win32";
|
|
1235
1669
|
const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
|
|
1236
|
-
await
|
|
1670
|
+
await execAsync5(checkCmd);
|
|
1237
1671
|
return true;
|
|
1238
1672
|
} catch {
|
|
1239
1673
|
return false;
|
|
@@ -1523,7 +1957,7 @@ async function createClient(serverId, handle, root) {
|
|
|
1523
1957
|
},
|
|
1524
1958
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
1525
1959
|
const normalized = normalizePath(filePath);
|
|
1526
|
-
return new Promise((
|
|
1960
|
+
return new Promise((resolve9) => {
|
|
1527
1961
|
const startTime = Date.now();
|
|
1528
1962
|
let debounceTimer;
|
|
1529
1963
|
let resolved = false;
|
|
@@ -1542,7 +1976,7 @@ async function createClient(serverId, handle, root) {
|
|
|
1542
1976
|
if (resolved) return;
|
|
1543
1977
|
resolved = true;
|
|
1544
1978
|
cleanup();
|
|
1545
|
-
|
|
1979
|
+
resolve9(diagnostics.get(normalized) || []);
|
|
1546
1980
|
};
|
|
1547
1981
|
const onDiagnostic = () => {
|
|
1548
1982
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -1700,6 +2134,7 @@ function isSupported(filePath) {
|
|
|
1700
2134
|
}
|
|
1701
2135
|
|
|
1702
2136
|
// src/tools/write-file.ts
|
|
2137
|
+
var MAX_PROGRESS_CHUNK_SIZE = 16 * 1024;
|
|
1703
2138
|
var writeFileInputSchema = z4.object({
|
|
1704
2139
|
path: z4.string().describe("The path to the file. Can be relative to working directory or absolute."),
|
|
1705
2140
|
mode: z4.enum(["full", "str_replace"]).describe('Write mode: "full" for complete file write, "str_replace" for targeted string replacement'),
|
|
@@ -1743,24 +2178,76 @@ Working directory: ${options.workingDirectory}`,
|
|
|
1743
2178
|
error: 'Content is required for "full" mode'
|
|
1744
2179
|
};
|
|
1745
2180
|
}
|
|
2181
|
+
const existed = existsSync7(absolutePath);
|
|
2182
|
+
const action = existed ? "replaced" : "created";
|
|
2183
|
+
console.log("[WRITE-FILE] onProgress callback exists:", !!options.onProgress);
|
|
2184
|
+
console.log("[WRITE-FILE] Emitting started event for:", relativePath);
|
|
2185
|
+
options.onProgress?.({
|
|
2186
|
+
path: absolutePath,
|
|
2187
|
+
relativePath,
|
|
2188
|
+
mode: "full",
|
|
2189
|
+
status: "started",
|
|
2190
|
+
action,
|
|
2191
|
+
totalLength: content.length
|
|
2192
|
+
});
|
|
2193
|
+
if (content.length <= MAX_PROGRESS_CHUNK_SIZE) {
|
|
2194
|
+
options.onProgress?.({
|
|
2195
|
+
path: absolutePath,
|
|
2196
|
+
relativePath,
|
|
2197
|
+
mode: "full",
|
|
2198
|
+
status: "content",
|
|
2199
|
+
content,
|
|
2200
|
+
action,
|
|
2201
|
+
totalLength: content.length
|
|
2202
|
+
});
|
|
2203
|
+
} else {
|
|
2204
|
+
const chunkCount = Math.ceil(content.length / MAX_PROGRESS_CHUNK_SIZE);
|
|
2205
|
+
for (let i = 0; i < chunkCount; i += 1) {
|
|
2206
|
+
const chunkStart = i * MAX_PROGRESS_CHUNK_SIZE;
|
|
2207
|
+
const chunk = content.slice(chunkStart, chunkStart + MAX_PROGRESS_CHUNK_SIZE);
|
|
2208
|
+
options.onProgress?.({
|
|
2209
|
+
path: absolutePath,
|
|
2210
|
+
relativePath,
|
|
2211
|
+
mode: "full",
|
|
2212
|
+
status: "content",
|
|
2213
|
+
content: chunk,
|
|
2214
|
+
action,
|
|
2215
|
+
totalLength: content.length,
|
|
2216
|
+
chunkIndex: i,
|
|
2217
|
+
chunkCount,
|
|
2218
|
+
chunkStart,
|
|
2219
|
+
isChunked: true
|
|
2220
|
+
});
|
|
2221
|
+
if (chunkCount > 1) {
|
|
2222
|
+
await new Promise((resolve9) => setTimeout(resolve9, 0));
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
1746
2226
|
await backupFile(options.sessionId, options.workingDirectory, absolutePath);
|
|
1747
2227
|
const dir = dirname5(absolutePath);
|
|
1748
2228
|
if (!existsSync7(dir)) {
|
|
1749
2229
|
await mkdir3(dir, { recursive: true });
|
|
1750
2230
|
}
|
|
1751
|
-
const existed = existsSync7(absolutePath);
|
|
1752
2231
|
await writeFile3(absolutePath, content, "utf-8");
|
|
1753
2232
|
let diagnosticsOutput = "";
|
|
1754
2233
|
if (options.enableLSP !== false && isSupported(absolutePath)) {
|
|
1755
2234
|
await touchFile(absolutePath, true);
|
|
1756
2235
|
diagnosticsOutput = await formatDiagnosticsOutput(absolutePath);
|
|
1757
2236
|
}
|
|
2237
|
+
options.onProgress?.({
|
|
2238
|
+
path: absolutePath,
|
|
2239
|
+
relativePath,
|
|
2240
|
+
mode: "full",
|
|
2241
|
+
status: "completed",
|
|
2242
|
+
action,
|
|
2243
|
+
totalLength: content.length
|
|
2244
|
+
});
|
|
1758
2245
|
return {
|
|
1759
2246
|
success: true,
|
|
1760
2247
|
path: absolutePath,
|
|
1761
|
-
relativePath
|
|
2248
|
+
relativePath,
|
|
1762
2249
|
mode: "full",
|
|
1763
|
-
action
|
|
2250
|
+
action,
|
|
1764
2251
|
bytesWritten: Buffer.byteLength(content, "utf-8"),
|
|
1765
2252
|
lineCount: content.split("\n").length,
|
|
1766
2253
|
...diagnosticsOutput && { diagnostics: diagnosticsOutput }
|
|
@@ -1778,6 +2265,22 @@ Working directory: ${options.workingDirectory}`,
|
|
|
1778
2265
|
error: `File not found: ${path}. Use "full" mode to create new files.`
|
|
1779
2266
|
};
|
|
1780
2267
|
}
|
|
2268
|
+
options.onProgress?.({
|
|
2269
|
+
path: absolutePath,
|
|
2270
|
+
relativePath,
|
|
2271
|
+
mode: "str_replace",
|
|
2272
|
+
status: "started",
|
|
2273
|
+
action: "edited"
|
|
2274
|
+
});
|
|
2275
|
+
options.onProgress?.({
|
|
2276
|
+
path: absolutePath,
|
|
2277
|
+
relativePath,
|
|
2278
|
+
mode: "str_replace",
|
|
2279
|
+
status: "content",
|
|
2280
|
+
oldString: old_string,
|
|
2281
|
+
newString: new_string,
|
|
2282
|
+
action: "edited"
|
|
2283
|
+
});
|
|
1781
2284
|
await backupFile(options.sessionId, options.workingDirectory, absolutePath);
|
|
1782
2285
|
const currentContent = await readFile5(absolutePath, "utf-8");
|
|
1783
2286
|
if (!currentContent.includes(old_string)) {
|
|
@@ -1808,10 +2311,17 @@ Working directory: ${options.workingDirectory}`,
|
|
|
1808
2311
|
await touchFile(absolutePath, true);
|
|
1809
2312
|
diagnosticsOutput = await formatDiagnosticsOutput(absolutePath);
|
|
1810
2313
|
}
|
|
2314
|
+
options.onProgress?.({
|
|
2315
|
+
path: absolutePath,
|
|
2316
|
+
relativePath,
|
|
2317
|
+
mode: "str_replace",
|
|
2318
|
+
status: "completed",
|
|
2319
|
+
action: "edited"
|
|
2320
|
+
});
|
|
1811
2321
|
return {
|
|
1812
2322
|
success: true,
|
|
1813
2323
|
path: absolutePath,
|
|
1814
|
-
relativePath
|
|
2324
|
+
relativePath,
|
|
1815
2325
|
mode: "str_replace",
|
|
1816
2326
|
linesRemoved: oldLines,
|
|
1817
2327
|
linesAdded: newLines,
|
|
@@ -1959,112 +2469,9 @@ function formatTodoItem(item) {
|
|
|
1959
2469
|
}
|
|
1960
2470
|
|
|
1961
2471
|
// src/tools/load-skill.ts
|
|
2472
|
+
init_skills();
|
|
1962
2473
|
import { tool as tool5 } from "ai";
|
|
1963
2474
|
import { z as z6 } from "zod";
|
|
1964
|
-
|
|
1965
|
-
// src/skills/index.ts
|
|
1966
|
-
import { readFile as readFile6, readdir } from "fs/promises";
|
|
1967
|
-
import { resolve as resolve6, basename, extname as extname3 } from "path";
|
|
1968
|
-
import { existsSync as existsSync8 } from "fs";
|
|
1969
|
-
function parseSkillFrontmatter(content) {
|
|
1970
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
1971
|
-
if (!frontmatterMatch) {
|
|
1972
|
-
return null;
|
|
1973
|
-
}
|
|
1974
|
-
const [, frontmatter, body] = frontmatterMatch;
|
|
1975
|
-
try {
|
|
1976
|
-
const lines = frontmatter.split("\n");
|
|
1977
|
-
const data = {};
|
|
1978
|
-
for (const line of lines) {
|
|
1979
|
-
const colonIndex = line.indexOf(":");
|
|
1980
|
-
if (colonIndex > 0) {
|
|
1981
|
-
const key = line.slice(0, colonIndex).trim();
|
|
1982
|
-
let value = line.slice(colonIndex + 1).trim();
|
|
1983
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1984
|
-
value = value.slice(1, -1);
|
|
1985
|
-
}
|
|
1986
|
-
data[key] = value;
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
const metadata = SkillMetadataSchema.parse(data);
|
|
1990
|
-
return { metadata, body: body.trim() };
|
|
1991
|
-
} catch {
|
|
1992
|
-
return null;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
function getSkillNameFromPath(filePath) {
|
|
1996
|
-
return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1997
|
-
}
|
|
1998
|
-
async function loadSkillsFromDirectory(directory) {
|
|
1999
|
-
if (!existsSync8(directory)) {
|
|
2000
|
-
return [];
|
|
2001
|
-
}
|
|
2002
|
-
const skills = [];
|
|
2003
|
-
const files = await readdir(directory);
|
|
2004
|
-
for (const file of files) {
|
|
2005
|
-
if (!file.endsWith(".md")) continue;
|
|
2006
|
-
const filePath = resolve6(directory, file);
|
|
2007
|
-
const content = await readFile6(filePath, "utf-8");
|
|
2008
|
-
const parsed = parseSkillFrontmatter(content);
|
|
2009
|
-
if (parsed) {
|
|
2010
|
-
skills.push({
|
|
2011
|
-
name: parsed.metadata.name,
|
|
2012
|
-
description: parsed.metadata.description,
|
|
2013
|
-
filePath
|
|
2014
|
-
});
|
|
2015
|
-
} else {
|
|
2016
|
-
const name = getSkillNameFromPath(filePath);
|
|
2017
|
-
const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
|
|
2018
|
-
skills.push({
|
|
2019
|
-
name,
|
|
2020
|
-
description: firstParagraph.replace(/^#\s*/, "").trim(),
|
|
2021
|
-
filePath
|
|
2022
|
-
});
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
return skills;
|
|
2026
|
-
}
|
|
2027
|
-
async function loadAllSkills(directories) {
|
|
2028
|
-
const allSkills = [];
|
|
2029
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
2030
|
-
for (const dir of directories) {
|
|
2031
|
-
const skills = await loadSkillsFromDirectory(dir);
|
|
2032
|
-
for (const skill of skills) {
|
|
2033
|
-
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
2034
|
-
seenNames.add(skill.name.toLowerCase());
|
|
2035
|
-
allSkills.push(skill);
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
return allSkills;
|
|
2040
|
-
}
|
|
2041
|
-
async function loadSkillContent(skillName, directories) {
|
|
2042
|
-
const allSkills = await loadAllSkills(directories);
|
|
2043
|
-
const skill = allSkills.find(
|
|
2044
|
-
(s) => s.name.toLowerCase() === skillName.toLowerCase()
|
|
2045
|
-
);
|
|
2046
|
-
if (!skill) {
|
|
2047
|
-
return null;
|
|
2048
|
-
}
|
|
2049
|
-
const content = await readFile6(skill.filePath, "utf-8");
|
|
2050
|
-
const parsed = parseSkillFrontmatter(content);
|
|
2051
|
-
return {
|
|
2052
|
-
...skill,
|
|
2053
|
-
content: parsed ? parsed.body : content
|
|
2054
|
-
};
|
|
2055
|
-
}
|
|
2056
|
-
function formatSkillsForContext(skills) {
|
|
2057
|
-
if (skills.length === 0) {
|
|
2058
|
-
return "No skills available.";
|
|
2059
|
-
}
|
|
2060
|
-
const lines = ["Available skills (use load_skill tool to load into context):"];
|
|
2061
|
-
for (const skill of skills) {
|
|
2062
|
-
lines.push(`- ${skill.name}: ${skill.description}`);
|
|
2063
|
-
}
|
|
2064
|
-
return lines.join("\n");
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
// src/tools/load-skill.ts
|
|
2068
2475
|
var loadSkillInputSchema = z6.object({
|
|
2069
2476
|
action: z6.enum(["list", "load"]).describe('Action to perform: "list" to see available skills, "load" to load a skill'),
|
|
2070
2477
|
skillName: z6.string().optional().describe('For "load" action: The name of the skill to load')
|
|
@@ -2147,7 +2554,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
|
|
|
2147
2554
|
// src/tools/linter.ts
|
|
2148
2555
|
import { tool as tool6 } from "ai";
|
|
2149
2556
|
import { z as z7 } from "zod";
|
|
2150
|
-
import { resolve as resolve7, relative as
|
|
2557
|
+
import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname4 } from "path";
|
|
2151
2558
|
import { existsSync as existsSync9 } from "fs";
|
|
2152
2559
|
import { readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
2153
2560
|
var linterInputSchema = z7.object({
|
|
@@ -2264,7 +2671,7 @@ function formatDiagnosticsResult(diagnosticsMap, workingDirectory) {
|
|
|
2264
2671
|
let totalInfo = 0;
|
|
2265
2672
|
const files = [];
|
|
2266
2673
|
for (const [filePath, diagnostics] of Object.entries(diagnosticsMap)) {
|
|
2267
|
-
const relativePath =
|
|
2674
|
+
const relativePath = relative5(workingDirectory, filePath);
|
|
2268
2675
|
let fileErrors = 0;
|
|
2269
2676
|
let fileWarnings = 0;
|
|
2270
2677
|
const formattedDiagnostics = diagnostics.map((d) => {
|
|
@@ -2337,6 +2744,621 @@ ${file.relativePath}:`);
|
|
|
2337
2744
|
return lines.join("\n");
|
|
2338
2745
|
}
|
|
2339
2746
|
|
|
2747
|
+
// src/tools/search.ts
|
|
2748
|
+
import { tool as tool8 } from "ai";
|
|
2749
|
+
import { z as z9 } from "zod";
|
|
2750
|
+
|
|
2751
|
+
// src/agent/subagent.ts
|
|
2752
|
+
import {
|
|
2753
|
+
generateText,
|
|
2754
|
+
stepCountIs
|
|
2755
|
+
} from "ai";
|
|
2756
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
2757
|
+
var Subagent = class {
|
|
2758
|
+
/** Model to use (defaults to gemini-2.0-flash) */
|
|
2759
|
+
model;
|
|
2760
|
+
/** Maximum steps before stopping */
|
|
2761
|
+
maxSteps = 20;
|
|
2762
|
+
constructor(model) {
|
|
2763
|
+
this.model = model || SUBAGENT_MODELS.default;
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Parse the final result from the subagent's output.
|
|
2767
|
+
* Override this to structure the result for your subagent type.
|
|
2768
|
+
*/
|
|
2769
|
+
parseResult(text2, steps) {
|
|
2770
|
+
return { text: text2, steps };
|
|
2771
|
+
}
|
|
2772
|
+
/**
|
|
2773
|
+
* Run the subagent with streaming progress updates
|
|
2774
|
+
*/
|
|
2775
|
+
async run(options) {
|
|
2776
|
+
const { task, sessionId, toolCallId, onProgress, abortSignal } = options;
|
|
2777
|
+
const steps = [];
|
|
2778
|
+
const execution = subagentQueries.create({
|
|
2779
|
+
sessionId,
|
|
2780
|
+
toolCallId,
|
|
2781
|
+
subagentType: this.type,
|
|
2782
|
+
task,
|
|
2783
|
+
model: this.model
|
|
2784
|
+
});
|
|
2785
|
+
const addStep = async (step) => {
|
|
2786
|
+
const fullStep = {
|
|
2787
|
+
id: nanoid3(8),
|
|
2788
|
+
timestamp: Date.now(),
|
|
2789
|
+
...step
|
|
2790
|
+
};
|
|
2791
|
+
steps.push(fullStep);
|
|
2792
|
+
subagentQueries.addStep(execution.id, fullStep);
|
|
2793
|
+
await onProgress?.({
|
|
2794
|
+
type: "step",
|
|
2795
|
+
subagentId: execution.id,
|
|
2796
|
+
subagentType: this.type,
|
|
2797
|
+
step: fullStep
|
|
2798
|
+
});
|
|
2799
|
+
};
|
|
2800
|
+
try {
|
|
2801
|
+
const tools = this.getTools(options);
|
|
2802
|
+
const systemPrompt = this.getSystemPrompt(options);
|
|
2803
|
+
const result = await generateText({
|
|
2804
|
+
model: resolveModel(this.model),
|
|
2805
|
+
system: systemPrompt,
|
|
2806
|
+
messages: [
|
|
2807
|
+
{ role: "user", content: task }
|
|
2808
|
+
],
|
|
2809
|
+
tools,
|
|
2810
|
+
stopWhen: stepCountIs(this.maxSteps),
|
|
2811
|
+
abortSignal,
|
|
2812
|
+
onStepFinish: async (step) => {
|
|
2813
|
+
if (step.text) {
|
|
2814
|
+
await addStep({
|
|
2815
|
+
type: "text",
|
|
2816
|
+
content: step.text
|
|
2817
|
+
});
|
|
2818
|
+
await onProgress?.({
|
|
2819
|
+
type: "text",
|
|
2820
|
+
subagentId: execution.id,
|
|
2821
|
+
subagentType: this.type,
|
|
2822
|
+
text: step.text
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2825
|
+
if (step.toolCalls) {
|
|
2826
|
+
for (const toolCall of step.toolCalls) {
|
|
2827
|
+
await addStep({
|
|
2828
|
+
type: "tool_call",
|
|
2829
|
+
content: `Calling ${toolCall.toolName}`,
|
|
2830
|
+
toolName: toolCall.toolName,
|
|
2831
|
+
toolInput: toolCall.input
|
|
2832
|
+
});
|
|
2833
|
+
await onProgress?.({
|
|
2834
|
+
type: "tool_call",
|
|
2835
|
+
subagentId: execution.id,
|
|
2836
|
+
subagentType: this.type,
|
|
2837
|
+
toolName: toolCall.toolName,
|
|
2838
|
+
toolInput: toolCall.input
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
if (step.toolResults) {
|
|
2843
|
+
for (const toolResult of step.toolResults) {
|
|
2844
|
+
await addStep({
|
|
2845
|
+
type: "tool_result",
|
|
2846
|
+
content: `Result from ${toolResult.toolName}`,
|
|
2847
|
+
toolName: toolResult.toolName,
|
|
2848
|
+
toolOutput: toolResult.output
|
|
2849
|
+
});
|
|
2850
|
+
await onProgress?.({
|
|
2851
|
+
type: "tool_result",
|
|
2852
|
+
subagentId: execution.id,
|
|
2853
|
+
subagentType: this.type,
|
|
2854
|
+
toolName: toolResult.toolName,
|
|
2855
|
+
toolOutput: toolResult.output
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
});
|
|
2861
|
+
const parsedResult = this.parseResult(result.text, steps);
|
|
2862
|
+
subagentQueries.complete(execution.id, parsedResult);
|
|
2863
|
+
await onProgress?.({
|
|
2864
|
+
type: "complete",
|
|
2865
|
+
subagentId: execution.id,
|
|
2866
|
+
subagentType: this.type,
|
|
2867
|
+
result: parsedResult
|
|
2868
|
+
});
|
|
2869
|
+
return {
|
|
2870
|
+
success: true,
|
|
2871
|
+
result: parsedResult,
|
|
2872
|
+
steps,
|
|
2873
|
+
executionId: execution.id
|
|
2874
|
+
};
|
|
2875
|
+
} catch (error) {
|
|
2876
|
+
const errorMessage = error.message || "Unknown error";
|
|
2877
|
+
subagentQueries.markError(execution.id, errorMessage);
|
|
2878
|
+
await onProgress?.({
|
|
2879
|
+
type: "error",
|
|
2880
|
+
subagentId: execution.id,
|
|
2881
|
+
subagentType: this.type,
|
|
2882
|
+
error: errorMessage
|
|
2883
|
+
});
|
|
2884
|
+
return {
|
|
2885
|
+
success: false,
|
|
2886
|
+
error: errorMessage,
|
|
2887
|
+
steps,
|
|
2888
|
+
executionId: execution.id
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Run with streaming (for real-time progress in UI)
|
|
2894
|
+
*/
|
|
2895
|
+
async *stream(options) {
|
|
2896
|
+
const events = [];
|
|
2897
|
+
let resolveNext = null;
|
|
2898
|
+
let done = false;
|
|
2899
|
+
const eventQueue = [];
|
|
2900
|
+
const runPromise = this.run({
|
|
2901
|
+
...options,
|
|
2902
|
+
onProgress: async (event) => {
|
|
2903
|
+
eventQueue.push(event);
|
|
2904
|
+
if (resolveNext) {
|
|
2905
|
+
resolveNext(eventQueue.shift());
|
|
2906
|
+
resolveNext = null;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}).then((result) => {
|
|
2910
|
+
done = true;
|
|
2911
|
+
if (resolveNext) {
|
|
2912
|
+
resolveNext(null);
|
|
2913
|
+
}
|
|
2914
|
+
return result;
|
|
2915
|
+
});
|
|
2916
|
+
while (!done || eventQueue.length > 0) {
|
|
2917
|
+
if (eventQueue.length > 0) {
|
|
2918
|
+
yield eventQueue.shift();
|
|
2919
|
+
} else if (!done) {
|
|
2920
|
+
const event = await new Promise((resolve9) => {
|
|
2921
|
+
resolveNext = resolve9;
|
|
2922
|
+
});
|
|
2923
|
+
if (event) {
|
|
2924
|
+
yield event;
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
await runPromise;
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
|
|
2932
|
+
// src/agent/subagents/search.ts
|
|
2933
|
+
import { tool as tool7 } from "ai";
|
|
2934
|
+
import { z as z8 } from "zod";
|
|
2935
|
+
import { exec as exec4 } from "child_process";
|
|
2936
|
+
import { promisify as promisify4 } from "util";
|
|
2937
|
+
import { readFile as readFile7, stat as stat3, readdir as readdir3 } from "fs/promises";
|
|
2938
|
+
import { resolve as resolve8, relative as relative6, isAbsolute as isAbsolute4 } from "path";
|
|
2939
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2940
|
+
var execAsync4 = promisify4(exec4);
|
|
2941
|
+
var MAX_OUTPUT_CHARS4 = 2e4;
|
|
2942
|
+
var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
|
|
2943
|
+
var SearchSubagent = class extends Subagent {
|
|
2944
|
+
type = "search";
|
|
2945
|
+
name = "Search Agent";
|
|
2946
|
+
constructor(model) {
|
|
2947
|
+
super(model || SUBAGENT_MODELS.search);
|
|
2948
|
+
this.maxSteps = 15;
|
|
2949
|
+
}
|
|
2950
|
+
getSystemPrompt(options) {
|
|
2951
|
+
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.
|
|
2952
|
+
|
|
2953
|
+
Working Directory: ${options.workingDirectory}
|
|
2954
|
+
|
|
2955
|
+
You have these tools available:
|
|
2956
|
+
- grep: Search for patterns in files using ripgrep (rg)
|
|
2957
|
+
- glob: Find files matching a pattern
|
|
2958
|
+
- read_file: Read contents of a specific file
|
|
2959
|
+
- list_dir: List directory contents
|
|
2960
|
+
|
|
2961
|
+
## Strategy - Search in Parallel
|
|
2962
|
+
|
|
2963
|
+
IMPORTANT: When searching, run MULTIPLE searches in PARALLEL to cover different variations and related terms. Don't search sequentially - batch your searches together!
|
|
2964
|
+
|
|
2965
|
+
For example, if asked "how does authentication work":
|
|
2966
|
+
- Search for "auth", "login", "session", "jwt", "token" all at once
|
|
2967
|
+
- Search in different likely directories: src/auth/, lib/auth/, services/auth/
|
|
2968
|
+
- Look for common patterns: AuthProvider, useAuth, authenticate, isAuthenticated
|
|
2969
|
+
|
|
2970
|
+
**Parallel Search Patterns:**
|
|
2971
|
+
1. Try multiple naming conventions at once:
|
|
2972
|
+
- camelCase: "getUserData", "handleAuth"
|
|
2973
|
+
- snake_case: "get_user_data", "handle_auth"
|
|
2974
|
+
- PascalCase: "UserService", "AuthProvider"
|
|
2975
|
+
|
|
2976
|
+
2. Search for related concepts together:
|
|
2977
|
+
- For "database": search "db", "database", "query", "model", "schema"
|
|
2978
|
+
- For "api": search "endpoint", "route", "handler", "controller", "api"
|
|
2979
|
+
|
|
2980
|
+
3. Use glob AND grep together:
|
|
2981
|
+
- Find files: \`*.auth.ts\`, \`*Auth*.tsx\`, \`auth/*.ts\`
|
|
2982
|
+
- Search content: patterns, function names, class names
|
|
2983
|
+
|
|
2984
|
+
## Execution Flow
|
|
2985
|
+
1. First, run 2-4 parallel searches covering different angles of the query
|
|
2986
|
+
2. Review results and identify the most relevant files
|
|
2987
|
+
3. Read the key files to understand the full context
|
|
2988
|
+
4. Provide a clear summary with exact file paths and line numbers
|
|
2989
|
+
|
|
2990
|
+
Be efficient - you have limited steps. Maximize coverage with parallel tool calls.
|
|
2991
|
+
|
|
2992
|
+
## Output Format
|
|
2993
|
+
When done, provide a summary with:
|
|
2994
|
+
- Key files/locations found (with full paths)
|
|
2995
|
+
- Relevant code snippets showing the important parts
|
|
2996
|
+
- How the pieces connect together
|
|
2997
|
+
|
|
2998
|
+
Keep your responses concise and focused on actionable information.`;
|
|
2999
|
+
}
|
|
3000
|
+
getTools(options) {
|
|
3001
|
+
const workingDirectory = options.workingDirectory;
|
|
3002
|
+
return {
|
|
3003
|
+
grep: tool7({
|
|
3004
|
+
description: "Search for patterns in files using ripgrep. Returns matching lines with file paths and line numbers.",
|
|
3005
|
+
inputSchema: z8.object({
|
|
3006
|
+
pattern: z8.string().describe("The regex pattern to search for"),
|
|
3007
|
+
path: z8.string().optional().describe("Subdirectory or file to search in (relative to working directory)"),
|
|
3008
|
+
fileType: z8.string().optional().describe('File type to filter (e.g., "ts", "js", "py")'),
|
|
3009
|
+
maxResults: z8.number().optional().default(50).describe("Maximum number of results to return")
|
|
3010
|
+
}),
|
|
3011
|
+
execute: async ({ pattern, path, fileType, maxResults }) => {
|
|
3012
|
+
try {
|
|
3013
|
+
const searchPath = path ? resolve8(workingDirectory, path) : workingDirectory;
|
|
3014
|
+
let args = ["rg", "--line-number", "--no-heading"];
|
|
3015
|
+
if (fileType) {
|
|
3016
|
+
args.push("--type", fileType);
|
|
3017
|
+
}
|
|
3018
|
+
args.push("--max-count", String(maxResults || 50));
|
|
3019
|
+
args.push("--", pattern, searchPath);
|
|
3020
|
+
const { stdout, stderr } = await execAsync4(args.join(" "), {
|
|
3021
|
+
cwd: workingDirectory,
|
|
3022
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
3023
|
+
timeout: 3e4
|
|
3024
|
+
});
|
|
3025
|
+
const output = truncateOutput(stdout || "No matches found", MAX_OUTPUT_CHARS4);
|
|
3026
|
+
const matchCount = (stdout || "").split("\n").filter(Boolean).length;
|
|
3027
|
+
return {
|
|
3028
|
+
success: true,
|
|
3029
|
+
output,
|
|
3030
|
+
matchCount,
|
|
3031
|
+
pattern
|
|
3032
|
+
};
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
if (error.code === 1 && !error.stderr) {
|
|
3035
|
+
return {
|
|
3036
|
+
success: true,
|
|
3037
|
+
output: "No matches found",
|
|
3038
|
+
matchCount: 0,
|
|
3039
|
+
pattern
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
return {
|
|
3043
|
+
success: false,
|
|
3044
|
+
error: error.message,
|
|
3045
|
+
pattern
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}),
|
|
3050
|
+
glob: tool7({
|
|
3051
|
+
description: "Find files matching a glob pattern. Returns list of matching file paths.",
|
|
3052
|
+
inputSchema: z8.object({
|
|
3053
|
+
pattern: z8.string().describe('Glob pattern (e.g., "**/*.ts", "src/**/*.tsx", "*.json")'),
|
|
3054
|
+
maxResults: z8.number().optional().default(100).describe("Maximum number of files to return")
|
|
3055
|
+
}),
|
|
3056
|
+
execute: async ({ pattern, maxResults }) => {
|
|
3057
|
+
try {
|
|
3058
|
+
const { stdout } = await execAsync4(
|
|
3059
|
+
`find . -type f -name "${pattern.replace("**/", "")}" 2>/dev/null | head -n ${maxResults || 100}`,
|
|
3060
|
+
{
|
|
3061
|
+
cwd: workingDirectory,
|
|
3062
|
+
timeout: 3e4
|
|
3063
|
+
}
|
|
3064
|
+
);
|
|
3065
|
+
const files = stdout.trim().split("\n").filter(Boolean);
|
|
3066
|
+
return {
|
|
3067
|
+
success: true,
|
|
3068
|
+
files,
|
|
3069
|
+
count: files.length,
|
|
3070
|
+
pattern
|
|
3071
|
+
};
|
|
3072
|
+
} catch (error) {
|
|
3073
|
+
return {
|
|
3074
|
+
success: false,
|
|
3075
|
+
error: error.message,
|
|
3076
|
+
pattern
|
|
3077
|
+
};
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
}),
|
|
3081
|
+
read_file: tool7({
|
|
3082
|
+
description: "Read the contents of a file. Use this to examine specific files found in search.",
|
|
3083
|
+
inputSchema: z8.object({
|
|
3084
|
+
path: z8.string().describe("Path to the file (relative to working directory or absolute)"),
|
|
3085
|
+
startLine: z8.number().optional().describe("Start reading from this line (1-indexed)"),
|
|
3086
|
+
endLine: z8.number().optional().describe("Stop reading at this line (1-indexed, inclusive)")
|
|
3087
|
+
}),
|
|
3088
|
+
execute: async ({ path, startLine, endLine }) => {
|
|
3089
|
+
try {
|
|
3090
|
+
const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
|
|
3091
|
+
if (!existsSync10(absolutePath)) {
|
|
3092
|
+
return {
|
|
3093
|
+
success: false,
|
|
3094
|
+
error: `File not found: ${path}`
|
|
3095
|
+
};
|
|
3096
|
+
}
|
|
3097
|
+
const stats = await stat3(absolutePath);
|
|
3098
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
3099
|
+
return {
|
|
3100
|
+
success: false,
|
|
3101
|
+
error: `File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Use startLine/endLine to read portions.`
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
let content = await readFile7(absolutePath, "utf-8");
|
|
3105
|
+
if (startLine !== void 0 || endLine !== void 0) {
|
|
3106
|
+
const lines = content.split("\n");
|
|
3107
|
+
const start = (startLine ?? 1) - 1;
|
|
3108
|
+
const end = endLine ?? lines.length;
|
|
3109
|
+
content = lines.slice(start, end).join("\n");
|
|
3110
|
+
}
|
|
3111
|
+
return {
|
|
3112
|
+
success: true,
|
|
3113
|
+
path: relative6(workingDirectory, absolutePath),
|
|
3114
|
+
content: truncateOutput(content, MAX_OUTPUT_CHARS4),
|
|
3115
|
+
lineCount: content.split("\n").length
|
|
3116
|
+
};
|
|
3117
|
+
} catch (error) {
|
|
3118
|
+
return {
|
|
3119
|
+
success: false,
|
|
3120
|
+
error: error.message
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
}),
|
|
3125
|
+
list_dir: tool7({
|
|
3126
|
+
description: "List contents of a directory. Shows files and subdirectories.",
|
|
3127
|
+
inputSchema: z8.object({
|
|
3128
|
+
path: z8.string().optional().default(".").describe("Directory path (relative to working directory)"),
|
|
3129
|
+
recursive: z8.boolean().optional().default(false).describe("List recursively (be careful with large directories)"),
|
|
3130
|
+
maxDepth: z8.number().optional().default(2).describe("Maximum depth for recursive listing")
|
|
3131
|
+
}),
|
|
3132
|
+
execute: async ({ path, recursive, maxDepth }) => {
|
|
3133
|
+
try {
|
|
3134
|
+
const absolutePath = isAbsolute4(path) ? path : resolve8(workingDirectory, path);
|
|
3135
|
+
if (!existsSync10(absolutePath)) {
|
|
3136
|
+
return {
|
|
3137
|
+
success: false,
|
|
3138
|
+
error: `Directory not found: ${path}`
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
const stats = await stat3(absolutePath);
|
|
3142
|
+
if (!stats.isDirectory()) {
|
|
3143
|
+
return {
|
|
3144
|
+
success: false,
|
|
3145
|
+
error: `Not a directory: ${path}`
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
if (recursive) {
|
|
3149
|
+
const { stdout } = await execAsync4(
|
|
3150
|
+
`find . -maxdepth ${maxDepth} -type f 2>/dev/null | head -n 200`,
|
|
3151
|
+
{
|
|
3152
|
+
cwd: absolutePath,
|
|
3153
|
+
timeout: 1e4
|
|
3154
|
+
}
|
|
3155
|
+
);
|
|
3156
|
+
const files = stdout.trim().split("\n").filter(Boolean);
|
|
3157
|
+
return {
|
|
3158
|
+
success: true,
|
|
3159
|
+
path: relative6(workingDirectory, absolutePath) || ".",
|
|
3160
|
+
files,
|
|
3161
|
+
count: files.length,
|
|
3162
|
+
recursive: true
|
|
3163
|
+
};
|
|
3164
|
+
} else {
|
|
3165
|
+
const entries = await readdir3(absolutePath, { withFileTypes: true });
|
|
3166
|
+
const items = entries.slice(0, 200).map((e) => ({
|
|
3167
|
+
name: e.name,
|
|
3168
|
+
type: e.isDirectory() ? "directory" : "file"
|
|
3169
|
+
}));
|
|
3170
|
+
return {
|
|
3171
|
+
success: true,
|
|
3172
|
+
path: relative6(workingDirectory, absolutePath) || ".",
|
|
3173
|
+
items,
|
|
3174
|
+
count: items.length
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
} catch (error) {
|
|
3178
|
+
return {
|
|
3179
|
+
success: false,
|
|
3180
|
+
error: error.message
|
|
3181
|
+
};
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
})
|
|
3185
|
+
};
|
|
3186
|
+
}
|
|
3187
|
+
parseResult(text2, steps) {
|
|
3188
|
+
const findings = [];
|
|
3189
|
+
let filesSearched = 0;
|
|
3190
|
+
let matchCount = 0;
|
|
3191
|
+
for (const step of steps) {
|
|
3192
|
+
if (step.type === "tool_result" && step.toolOutput) {
|
|
3193
|
+
const output = step.toolOutput;
|
|
3194
|
+
if (step.toolName === "grep" && output.success) {
|
|
3195
|
+
matchCount += output.matchCount || 0;
|
|
3196
|
+
const lines = (output.output || "").split("\n").filter(Boolean).slice(0, 10);
|
|
3197
|
+
for (const line of lines) {
|
|
3198
|
+
const match = line.match(/^([^:]+):(\d+):(.*)$/);
|
|
3199
|
+
if (match) {
|
|
3200
|
+
findings.push({
|
|
3201
|
+
type: "match",
|
|
3202
|
+
path: match[1],
|
|
3203
|
+
lineNumber: parseInt(match[2], 10),
|
|
3204
|
+
content: match[3].trim(),
|
|
3205
|
+
relevance: "high"
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
} else if (step.toolName === "glob" && output.success) {
|
|
3210
|
+
filesSearched += output.count || 0;
|
|
3211
|
+
for (const file of (output.files || []).slice(0, 5)) {
|
|
3212
|
+
findings.push({
|
|
3213
|
+
type: "file",
|
|
3214
|
+
path: file,
|
|
3215
|
+
relevance: "medium"
|
|
3216
|
+
});
|
|
3217
|
+
}
|
|
3218
|
+
} else if (step.toolName === "read_file" && output.success) {
|
|
3219
|
+
findings.push({
|
|
3220
|
+
type: "file",
|
|
3221
|
+
path: output.path,
|
|
3222
|
+
relevance: "high",
|
|
3223
|
+
context: `${output.lineCount} lines`
|
|
3224
|
+
});
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
const query = steps.length > 0 ? steps.find((s) => s.type === "text")?.content || "" : "";
|
|
3229
|
+
return {
|
|
3230
|
+
query,
|
|
3231
|
+
summary: text2,
|
|
3232
|
+
findings: findings.slice(0, 20),
|
|
3233
|
+
// Limit findings
|
|
3234
|
+
filesSearched,
|
|
3235
|
+
matchCount
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
function createSearchSubagent(model) {
|
|
3240
|
+
return new SearchSubagent(model);
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
// src/tools/search.ts
|
|
3244
|
+
var MAX_RESULT_CHARS = 8e3;
|
|
3245
|
+
function createSearchTool(options) {
|
|
3246
|
+
return tool8({
|
|
3247
|
+
description: `Delegate a search task to a specialized search agent. Use this when you need to:
|
|
3248
|
+
- Find files or code matching a pattern
|
|
3249
|
+
- Explore the codebase structure
|
|
3250
|
+
- Search for specific functions, classes, or variables
|
|
3251
|
+
- Understand how a feature is implemented
|
|
3252
|
+
|
|
3253
|
+
The search agent will explore the codebase and return a summary of findings.
|
|
3254
|
+
This is more thorough than a simple grep because it can follow references and understand context.
|
|
3255
|
+
|
|
3256
|
+
Examples:
|
|
3257
|
+
- "Find all React components that use the useState hook"
|
|
3258
|
+
- "Where is the authentication logic implemented?"
|
|
3259
|
+
- "Find all API routes and their handlers"
|
|
3260
|
+
- "Search for usages of the UserService class"`,
|
|
3261
|
+
inputSchema: z9.object({
|
|
3262
|
+
query: z9.string().describe("What to search for. Be specific about what you're looking for."),
|
|
3263
|
+
context: z9.string().optional().describe("Optional additional context about why you need this information.")
|
|
3264
|
+
}),
|
|
3265
|
+
execute: async ({ query, context }, toolOptions) => {
|
|
3266
|
+
const toolCallId = toolOptions.toolCallId || `search_${Date.now()}`;
|
|
3267
|
+
await options.onProgress?.({
|
|
3268
|
+
status: "started",
|
|
3269
|
+
subagentId: toolCallId
|
|
3270
|
+
});
|
|
3271
|
+
try {
|
|
3272
|
+
const subagent = createSearchSubagent();
|
|
3273
|
+
const fullTask = context ? `${query}
|
|
3274
|
+
|
|
3275
|
+
Context: ${context}` : query;
|
|
3276
|
+
const result = await subagent.run({
|
|
3277
|
+
task: fullTask,
|
|
3278
|
+
sessionId: options.sessionId,
|
|
3279
|
+
toolCallId,
|
|
3280
|
+
workingDirectory: options.workingDirectory,
|
|
3281
|
+
onProgress: async (event) => {
|
|
3282
|
+
if (event.type === "step" && event.step) {
|
|
3283
|
+
await options.onProgress?.({
|
|
3284
|
+
status: "step",
|
|
3285
|
+
subagentId: event.subagentId,
|
|
3286
|
+
stepType: event.step.type,
|
|
3287
|
+
stepContent: event.step.content,
|
|
3288
|
+
toolName: event.step.toolName,
|
|
3289
|
+
toolInput: event.step.toolInput,
|
|
3290
|
+
toolOutput: event.step.toolOutput
|
|
3291
|
+
});
|
|
3292
|
+
} else if (event.type === "complete") {
|
|
3293
|
+
await options.onProgress?.({
|
|
3294
|
+
status: "complete",
|
|
3295
|
+
subagentId: event.subagentId,
|
|
3296
|
+
result: event.result
|
|
3297
|
+
});
|
|
3298
|
+
} else if (event.type === "error") {
|
|
3299
|
+
await options.onProgress?.({
|
|
3300
|
+
status: "error",
|
|
3301
|
+
subagentId: event.subagentId,
|
|
3302
|
+
error: event.error
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
if (!result.success) {
|
|
3308
|
+
return {
|
|
3309
|
+
success: false,
|
|
3310
|
+
error: result.error || "Search failed",
|
|
3311
|
+
executionId: result.executionId
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
const searchResult = result.result;
|
|
3315
|
+
let formattedResult = `## Search Results
|
|
3316
|
+
|
|
3317
|
+
`;
|
|
3318
|
+
formattedResult += `**Summary:** ${searchResult.summary}
|
|
3319
|
+
|
|
3320
|
+
`;
|
|
3321
|
+
if (searchResult.findings.length > 0) {
|
|
3322
|
+
formattedResult += `### Key Findings (${searchResult.findings.length} items)
|
|
3323
|
+
|
|
3324
|
+
`;
|
|
3325
|
+
for (const finding of searchResult.findings) {
|
|
3326
|
+
if (finding.type === "match") {
|
|
3327
|
+
formattedResult += `- **${finding.path}:${finding.lineNumber}** - ${truncateOutput(finding.content || "", 200)}
|
|
3328
|
+
`;
|
|
3329
|
+
} else if (finding.type === "file") {
|
|
3330
|
+
formattedResult += `- **${finding.path}** ${finding.context ? `(${finding.context})` : ""}
|
|
3331
|
+
`;
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
formattedResult += `
|
|
3336
|
+
**Stats:** ${searchResult.matchCount} matches across ${searchResult.filesSearched} files searched`;
|
|
3337
|
+
return {
|
|
3338
|
+
success: true,
|
|
3339
|
+
query: searchResult.query,
|
|
3340
|
+
summary: searchResult.summary,
|
|
3341
|
+
findings: searchResult.findings,
|
|
3342
|
+
matchCount: searchResult.matchCount,
|
|
3343
|
+
filesSearched: searchResult.filesSearched,
|
|
3344
|
+
formattedResult: truncateOutput(formattedResult, MAX_RESULT_CHARS),
|
|
3345
|
+
executionId: result.executionId,
|
|
3346
|
+
stepsCount: result.steps.length
|
|
3347
|
+
};
|
|
3348
|
+
} catch (error) {
|
|
3349
|
+
await options.onProgress?.({
|
|
3350
|
+
status: "error",
|
|
3351
|
+
error: error.message
|
|
3352
|
+
});
|
|
3353
|
+
return {
|
|
3354
|
+
success: false,
|
|
3355
|
+
error: error.message
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
|
|
2340
3362
|
// src/tools/index.ts
|
|
2341
3363
|
function createTools(options) {
|
|
2342
3364
|
return {
|
|
@@ -2352,7 +3374,8 @@ function createTools(options) {
|
|
|
2352
3374
|
write_file: createWriteFileTool({
|
|
2353
3375
|
workingDirectory: options.workingDirectory,
|
|
2354
3376
|
sessionId: options.sessionId,
|
|
2355
|
-
enableLSP: options.enableLSP ?? true
|
|
3377
|
+
enableLSP: options.enableLSP ?? true,
|
|
3378
|
+
onProgress: options.onWriteFileProgress
|
|
2356
3379
|
}),
|
|
2357
3380
|
todo: createTodoTool({
|
|
2358
3381
|
sessionId: options.sessionId
|
|
@@ -2363,15 +3386,20 @@ function createTools(options) {
|
|
|
2363
3386
|
}),
|
|
2364
3387
|
linter: createLinterTool({
|
|
2365
3388
|
workingDirectory: options.workingDirectory
|
|
3389
|
+
}),
|
|
3390
|
+
search: createSearchTool({
|
|
3391
|
+
sessionId: options.sessionId,
|
|
3392
|
+
workingDirectory: options.workingDirectory,
|
|
3393
|
+
onProgress: options.onSearchProgress
|
|
2366
3394
|
})
|
|
2367
3395
|
};
|
|
2368
3396
|
}
|
|
2369
3397
|
|
|
2370
3398
|
// src/agent/context.ts
|
|
2371
|
-
import { generateText } from "ai";
|
|
2372
|
-
import { gateway } from "@ai-sdk/gateway";
|
|
3399
|
+
import { generateText as generateText2 } from "ai";
|
|
2373
3400
|
|
|
2374
3401
|
// src/agent/prompts.ts
|
|
3402
|
+
init_skills();
|
|
2375
3403
|
import os from "os";
|
|
2376
3404
|
function getSearchInstructions() {
|
|
2377
3405
|
const platform2 = process.platform;
|
|
@@ -2390,9 +3418,33 @@ function getSearchInstructions() {
|
|
|
2390
3418
|
- **If ripgrep (\`rg\`) is installed**: \`rg "pattern" -t ts src/\` - faster and respects .gitignore`;
|
|
2391
3419
|
}
|
|
2392
3420
|
async function buildSystemPrompt(options) {
|
|
2393
|
-
const {
|
|
2394
|
-
|
|
2395
|
-
|
|
3421
|
+
const {
|
|
3422
|
+
workingDirectory,
|
|
3423
|
+
skillsDirectories,
|
|
3424
|
+
sessionId,
|
|
3425
|
+
discoveredSkills,
|
|
3426
|
+
activeFiles = [],
|
|
3427
|
+
customInstructions
|
|
3428
|
+
} = options;
|
|
3429
|
+
let alwaysLoadedContent = "";
|
|
3430
|
+
let globMatchedContent = "";
|
|
3431
|
+
let agentsMdContent = "";
|
|
3432
|
+
let onDemandSkillsContext = "";
|
|
3433
|
+
if (discoveredSkills) {
|
|
3434
|
+
const { always, onDemand, all } = await loadAllSkillsFromDiscovered(discoveredSkills);
|
|
3435
|
+
alwaysLoadedContent = formatAlwaysLoadedSkills(always);
|
|
3436
|
+
onDemandSkillsContext = formatSkillsForContext(onDemand);
|
|
3437
|
+
const agentsMd = await loadAgentsMd(discoveredSkills.agentsMdPath);
|
|
3438
|
+
agentsMdContent = formatAgentsMdContent(agentsMd);
|
|
3439
|
+
if (activeFiles.length > 0) {
|
|
3440
|
+
const globMatched = await getGlobMatchedSkills(all, activeFiles, workingDirectory);
|
|
3441
|
+
globMatchedContent = formatGlobMatchedSkills(globMatched);
|
|
3442
|
+
}
|
|
3443
|
+
} else {
|
|
3444
|
+
const { loadAllSkills: loadAllSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
3445
|
+
const skills = await loadAllSkills2(skillsDirectories);
|
|
3446
|
+
onDemandSkillsContext = formatSkillsForContext(skills);
|
|
3447
|
+
}
|
|
2396
3448
|
const todos = todoQueries.getBySession(sessionId);
|
|
2397
3449
|
const todosContext = formatTodosForContext(todos);
|
|
2398
3450
|
const platform2 = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
|
|
@@ -2413,6 +3465,7 @@ You have access to powerful tools for:
|
|
|
2413
3465
|
- **linter**: Check files for type errors and lint issues (TypeScript, JavaScript, TSX, JSX)
|
|
2414
3466
|
- **todo**: Manage your task list to track progress on complex operations
|
|
2415
3467
|
- **load_skill**: Load specialized knowledge documents for specific tasks
|
|
3468
|
+
- **search**: Semantic search using a subagent - for exploratory questions and finding code by meaning
|
|
2416
3469
|
|
|
2417
3470
|
|
|
2418
3471
|
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.
|
|
@@ -2489,6 +3542,9 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
|
|
|
2489
3542
|
- Use \`write_file\` with mode "full" only for new files or complete rewrites
|
|
2490
3543
|
- After making changes, use the \`linter\` tool to check for type errors and lint issues
|
|
2491
3544
|
- The \`write_file\` tool automatically shows lint errors in its output for TypeScript/JavaScript files
|
|
3545
|
+
- If the user asks to write/create a file, always use \`write_file\` rather than printing the full contents
|
|
3546
|
+
- If the user requests a file but does not provide a path, choose a sensible default (e.g. \`index.html\`) and proceed
|
|
3547
|
+
- For large content (hundreds of lines), avoid placing it in chat output; write to a file instead
|
|
2492
3548
|
|
|
2493
3549
|
### Linter Tool
|
|
2494
3550
|
The linter tool uses Language Server Protocol (LSP) to detect type errors and lint issues:
|
|
@@ -2500,6 +3556,30 @@ linter({ paths: ["src/"] }) // Check all files in a directory
|
|
|
2500
3556
|
Use this proactively after making code changes to catch errors early.
|
|
2501
3557
|
|
|
2502
3558
|
### Searching and Exploration
|
|
3559
|
+
|
|
3560
|
+
**Choose the right search approach:**
|
|
3561
|
+
|
|
3562
|
+
1. **Use the \`search\` tool (subagent)** for:
|
|
3563
|
+
- Semantic/exploratory questions: "How does authentication work?", "Where is user data processed?"
|
|
3564
|
+
- Finding code by meaning or concept, not exact text
|
|
3565
|
+
- Understanding how features are implemented across multiple files
|
|
3566
|
+
- Exploring unfamiliar parts of the codebase
|
|
3567
|
+
- Questions like "where", "how", "what does X do"
|
|
3568
|
+
|
|
3569
|
+
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.
|
|
3570
|
+
|
|
3571
|
+
2. **Use direct commands (grep/rg, find)** for:
|
|
3572
|
+
- Exact string matches: \`rg "functionName"\`, \`rg "class MyClass"\`
|
|
3573
|
+
- Finding files by name: \`find . -name "*.config.ts"\`
|
|
3574
|
+
- Simple pattern matching when you know exactly what you're looking for
|
|
3575
|
+
- Counting occurrences or listing all matches
|
|
3576
|
+
|
|
3577
|
+
**Examples:**
|
|
3578
|
+
- "Where is the API authentication handled?" \u2192 Use \`search\` tool
|
|
3579
|
+
- "Find all usages of getUserById" \u2192 Use \`rg "getUserById"\`
|
|
3580
|
+
- "How does the payment flow work?" \u2192 Use \`search\` tool
|
|
3581
|
+
- "Find files named config" \u2192 Use \`find . -name "*config*"\`
|
|
3582
|
+
|
|
2503
3583
|
${searchInstructions}
|
|
2504
3584
|
|
|
2505
3585
|
###Follow these principles when designing and implementing software:
|
|
@@ -2522,11 +3602,11 @@ ${searchInstructions}
|
|
|
2522
3602
|
16. **Diversity** \u2014 Distrust all claims for "one true way"
|
|
2523
3603
|
17. **Extensibility** \u2014 Design for the future, because it will be here sooner than you think
|
|
2524
3604
|
|
|
2525
|
-
###Follow these
|
|
3605
|
+
### Follow these rules to be a good agent for the user:
|
|
2526
3606
|
|
|
2527
|
-
1. Understand first - Read relevant files before making any changes. Use search
|
|
3607
|
+
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.
|
|
2528
3608
|
2. Plan for complexity - If the task involves 3+ steps or has meaningful trade-offs, create a todo list to track progress before implementing.
|
|
2529
|
-
3. Use the right tools - Have specialized tools for reading files, editing code,
|
|
3609
|
+
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.
|
|
2530
3610
|
4. Work efficiently - When need to do multiple independent things (like reading several files), do them in parallel rather than one at a time.
|
|
2531
3611
|
5. Be direct - Focus on technical accuracy rather than validation. If see issues with an approach or need clarification, say so.
|
|
2532
3612
|
6. Verify my work - After making changes, check for linter errors and fix any introduced.
|
|
@@ -2539,8 +3619,14 @@ ${searchInstructions}
|
|
|
2539
3619
|
- Ask clarifying questions when requirements are ambiguous
|
|
2540
3620
|
- Report progress on multi-step tasks
|
|
2541
3621
|
|
|
2542
|
-
|
|
2543
|
-
|
|
3622
|
+
${agentsMdContent}
|
|
3623
|
+
|
|
3624
|
+
${alwaysLoadedContent}
|
|
3625
|
+
|
|
3626
|
+
${globMatchedContent}
|
|
3627
|
+
|
|
3628
|
+
## On-Demand Skills
|
|
3629
|
+
${onDemandSkillsContext}
|
|
2544
3630
|
|
|
2545
3631
|
## Current Task List
|
|
2546
3632
|
${todosContext}
|
|
@@ -2635,8 +3721,8 @@ ${this.summary}`
|
|
|
2635
3721
|
try {
|
|
2636
3722
|
const config = getConfig();
|
|
2637
3723
|
const summaryPrompt = createSummaryPrompt(historyText);
|
|
2638
|
-
const result = await
|
|
2639
|
-
model:
|
|
3724
|
+
const result = await generateText2({
|
|
3725
|
+
model: resolveModel(config.defaultModel),
|
|
2640
3726
|
prompt: summaryPrompt
|
|
2641
3727
|
});
|
|
2642
3728
|
this.summary = result.text;
|
|
@@ -2706,7 +3792,9 @@ var Agent = class _Agent {
|
|
|
2706
3792
|
sessionId: this.session.id,
|
|
2707
3793
|
workingDirectory: this.session.workingDirectory,
|
|
2708
3794
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
2709
|
-
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0
|
|
3795
|
+
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
3796
|
+
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
3797
|
+
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "search", data: progress }) : void 0
|
|
2710
3798
|
});
|
|
2711
3799
|
}
|
|
2712
3800
|
/**
|
|
@@ -2815,28 +3903,33 @@ ${prompt}` });
|
|
|
2815
3903
|
const systemPrompt = await buildSystemPrompt({
|
|
2816
3904
|
workingDirectory: this.session.workingDirectory,
|
|
2817
3905
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
2818
|
-
sessionId: this.session.id
|
|
3906
|
+
sessionId: this.session.id,
|
|
3907
|
+
discoveredSkills: config.discoveredSkills,
|
|
3908
|
+
// TODO: Pass activeFiles from client for glob matching
|
|
3909
|
+
activeFiles: []
|
|
2819
3910
|
});
|
|
2820
3911
|
const messages2 = await this.context.getMessages();
|
|
2821
3912
|
const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
|
|
2822
3913
|
const wrappedTools = this.wrapToolsWithApproval(options, tools);
|
|
2823
|
-
const
|
|
2824
|
-
|
|
3914
|
+
const useAnthropic = isAnthropicModel(this.session.model);
|
|
3915
|
+
const stream = streamText2({
|
|
3916
|
+
model: resolveModel(this.session.model),
|
|
2825
3917
|
system: systemPrompt,
|
|
2826
3918
|
messages: messages2,
|
|
2827
3919
|
tools: wrappedTools,
|
|
2828
|
-
stopWhen:
|
|
3920
|
+
stopWhen: stepCountIs2(500),
|
|
2829
3921
|
// Forward abort signal if provided
|
|
2830
3922
|
abortSignal: options.abortSignal,
|
|
2831
3923
|
// Enable extended thinking/reasoning for models that support it
|
|
2832
|
-
providerOptions: {
|
|
3924
|
+
providerOptions: useAnthropic ? {
|
|
2833
3925
|
anthropic: {
|
|
3926
|
+
toolStreaming: true,
|
|
2834
3927
|
thinking: {
|
|
2835
3928
|
type: "enabled",
|
|
2836
3929
|
budgetTokens: 1e4
|
|
2837
3930
|
}
|
|
2838
3931
|
}
|
|
2839
|
-
},
|
|
3932
|
+
} : void 0,
|
|
2840
3933
|
onStepFinish: async (step) => {
|
|
2841
3934
|
options.onStepFinish?.(step);
|
|
2842
3935
|
},
|
|
@@ -2866,26 +3959,29 @@ ${prompt}` });
|
|
|
2866
3959
|
const systemPrompt = await buildSystemPrompt({
|
|
2867
3960
|
workingDirectory: this.session.workingDirectory,
|
|
2868
3961
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
2869
|
-
sessionId: this.session.id
|
|
3962
|
+
sessionId: this.session.id,
|
|
3963
|
+
discoveredSkills: config.discoveredSkills,
|
|
3964
|
+
activeFiles: []
|
|
2870
3965
|
});
|
|
2871
3966
|
const messages2 = await this.context.getMessages();
|
|
2872
3967
|
const tools = options.onToolProgress ? this.createToolsWithCallbacks({ onToolProgress: options.onToolProgress }) : this.baseTools;
|
|
2873
3968
|
const wrappedTools = this.wrapToolsWithApproval(options, tools);
|
|
2874
|
-
const
|
|
2875
|
-
|
|
3969
|
+
const useAnthropic = isAnthropicModel(this.session.model);
|
|
3970
|
+
const result = await generateText3({
|
|
3971
|
+
model: resolveModel(this.session.model),
|
|
2876
3972
|
system: systemPrompt,
|
|
2877
3973
|
messages: messages2,
|
|
2878
3974
|
tools: wrappedTools,
|
|
2879
|
-
stopWhen:
|
|
3975
|
+
stopWhen: stepCountIs2(500),
|
|
2880
3976
|
// Enable extended thinking/reasoning for models that support it
|
|
2881
|
-
providerOptions: {
|
|
3977
|
+
providerOptions: useAnthropic ? {
|
|
2882
3978
|
anthropic: {
|
|
2883
3979
|
thinking: {
|
|
2884
3980
|
type: "enabled",
|
|
2885
3981
|
budgetTokens: 1e4
|
|
2886
3982
|
}
|
|
2887
3983
|
}
|
|
2888
|
-
}
|
|
3984
|
+
} : void 0
|
|
2889
3985
|
});
|
|
2890
3986
|
const responseMessages = result.response.messages;
|
|
2891
3987
|
this.context.addResponseMessages(responseMessages);
|
|
@@ -2907,11 +4003,11 @@ ${prompt}` });
|
|
|
2907
4003
|
wrappedTools[name] = originalTool;
|
|
2908
4004
|
continue;
|
|
2909
4005
|
}
|
|
2910
|
-
wrappedTools[name] =
|
|
4006
|
+
wrappedTools[name] = tool9({
|
|
2911
4007
|
description: originalTool.description || "",
|
|
2912
|
-
inputSchema: originalTool.inputSchema ||
|
|
4008
|
+
inputSchema: originalTool.inputSchema || z10.object({}),
|
|
2913
4009
|
execute: async (input, toolOptions) => {
|
|
2914
|
-
const toolCallId = toolOptions.toolCallId ||
|
|
4010
|
+
const toolCallId = toolOptions.toolCallId || nanoid4();
|
|
2915
4011
|
const execution = toolExecutionQueries.create({
|
|
2916
4012
|
sessionId: this.session.id,
|
|
2917
4013
|
toolName: name,
|
|
@@ -2923,8 +4019,8 @@ ${prompt}` });
|
|
|
2923
4019
|
this.pendingApprovals.set(toolCallId, execution);
|
|
2924
4020
|
options.onApprovalRequired?.(execution);
|
|
2925
4021
|
sessionQueries.updateStatus(this.session.id, "waiting");
|
|
2926
|
-
const approved = await new Promise((
|
|
2927
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
4022
|
+
const approved = await new Promise((resolve9) => {
|
|
4023
|
+
approvalResolvers.set(toolCallId, { resolve: resolve9, sessionId: this.session.id });
|
|
2928
4024
|
});
|
|
2929
4025
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
2930
4026
|
approvalResolvers.delete(toolCallId);
|