bluera-knowledge 0.9.21
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/.claude/commands/commit.md +37 -0
- package/.claude/hooks/post-edit-check.sh +41 -0
- package/.claude/settings.local.json.example +40 -0
- package/.claude/skills/atomic-commits/SKILL.md +53 -0
- package/.claude-plugin/plugin.json +13 -0
- package/.editorconfig +15 -0
- package/.github/workflows/auto-release.yml +59 -0
- package/.github/workflows/ci.yml +142 -0
- package/.github/workflows/release.yml +66 -0
- package/.github/workflows/update-marketplace.yml +96 -0
- package/.husky/pre-commit +47 -0
- package/.husky/pre-push +29 -0
- package/.versionrc.json +28 -0
- package/CHANGELOG.md +410 -0
- package/CLAUDE.md +109 -0
- package/LICENSE +21 -0
- package/NOTICE +47 -0
- package/README.md +1546 -0
- package/SECURITY.md +65 -0
- package/bun.lock +1758 -0
- package/commands/add-folder.md +48 -0
- package/commands/add-repo.md +50 -0
- package/commands/cancel.md +63 -0
- package/commands/check-status.md +78 -0
- package/commands/crawl.md +51 -0
- package/commands/index.md +48 -0
- package/commands/remove-store.md +52 -0
- package/commands/search.md +79 -0
- package/commands/search.sh +63 -0
- package/commands/stores.md +54 -0
- package/commands/suggest.md +82 -0
- package/dist/chunk-5QMHZUC4.js +3617 -0
- package/dist/chunk-5QMHZUC4.js.map +1 -0
- package/dist/chunk-BICFAWMN.js +656 -0
- package/dist/chunk-BICFAWMN.js.map +1 -0
- package/dist/chunk-J7J6LXOJ.js +958 -0
- package/dist/chunk-J7J6LXOJ.js.map +1 -0
- package/dist/chunk-L2YVNC63.js +59 -0
- package/dist/chunk-L2YVNC63.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1429 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +15 -0
- package/dist/mcp/server.js +11 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/watch.service-YAIKKDCF.js +7 -0
- package/dist/watch.service-YAIKKDCF.js.map +1 -0
- package/dist/workers/background-worker-cli.d.ts +1 -0
- package/dist/workers/background-worker-cli.js +310 -0
- package/dist/workers/background-worker-cli.js.map +1 -0
- package/docs/plans/2024-12-17-ai-search-quality-implementation.md +752 -0
- package/docs/plans/2024-12-17-ai-search-quality-testing-design.md +201 -0
- package/docs/plans/2025-12-16-bluera-knowledge-cli.md +2951 -0
- package/docs/plans/2025-12-16-phase2-features.md +1518 -0
- package/docs/plans/2025-12-17-hil-implementation.md +926 -0
- package/docs/plans/2025-12-17-hil-quality-testing.md +224 -0
- package/docs/plans/2025-12-17-search-quality-phase1-implementation.md +1416 -0
- package/docs/plans/2025-12-17-search-quality-testing-v2-design.md +212 -0
- package/docs/plans/2025-12-28-ai-agent-optimization.md +1630 -0
- package/eslint-rules/require-skip-comment.js +81 -0
- package/eslint.config.js +61 -0
- package/hooks/check-dependencies.sh +110 -0
- package/hooks/format-search-results.py +132 -0
- package/hooks/hooks.json +27 -0
- package/hooks/job-status-hook.sh +51 -0
- package/knip.json +43 -0
- package/mcp.plugin.json +12 -0
- package/package.json +103 -0
- package/python/crawl_worker.py +275 -0
- package/python/requirements.txt +2 -0
- package/scripts/readme-version-updater.cjs +18 -0
- package/skills/advanced-workflows/SKILL.md +273 -0
- package/skills/atomic-commits/SKILL.md +77 -0
- package/skills/knowledge-search/SKILL.md +54 -0
- package/skills/search-optimization/SKILL.md +396 -0
- package/skills/store-lifecycle/SKILL.md +470 -0
- package/skills/when-to-query/SKILL.md +66 -0
- package/src/analysis/ast-parser.test.ts +423 -0
- package/src/analysis/ast-parser.ts +192 -0
- package/src/analysis/code-graph.test.ts +698 -0
- package/src/analysis/code-graph.ts +245 -0
- package/src/analysis/dependency-usage-analyzer.test.ts +799 -0
- package/src/analysis/dependency-usage-analyzer.ts +405 -0
- package/src/analysis/go-ast-parser.test.ts +531 -0
- package/src/analysis/go-ast-parser.ts +478 -0
- package/src/analysis/parser-factory.test.ts +132 -0
- package/src/analysis/parser-factory.ts +44 -0
- package/src/analysis/python-ast-parser.test.ts +210 -0
- package/src/analysis/python-ast-parser.ts +34 -0
- package/src/analysis/repo-url-resolver.test.ts +533 -0
- package/src/analysis/repo-url-resolver.ts +233 -0
- package/src/analysis/rust-ast-parser.test.ts +568 -0
- package/src/analysis/rust-ast-parser.ts +477 -0
- package/src/analysis/tree-sitter-parser.test.ts +297 -0
- package/src/analysis/tree-sitter-parser.ts +223 -0
- package/src/cli/commands/crawl.test.ts +942 -0
- package/src/cli/commands/crawl.ts +141 -0
- package/src/cli/commands/index-cmd.test.ts +722 -0
- package/src/cli/commands/index-cmd.ts +105 -0
- package/src/cli/commands/mcp.test.ts +218 -0
- package/src/cli/commands/mcp.ts +18 -0
- package/src/cli/commands/plugin-api.test.ts +313 -0
- package/src/cli/commands/plugin-api.ts +45 -0
- package/src/cli/commands/search.test.ts +911 -0
- package/src/cli/commands/search.ts +113 -0
- package/src/cli/commands/serve.test.ts +329 -0
- package/src/cli/commands/serve.ts +28 -0
- package/src/cli/commands/setup.test.ts +820 -0
- package/src/cli/commands/setup.ts +153 -0
- package/src/cli/commands/store.test.ts +1003 -0
- package/src/cli/commands/store.ts +167 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/program.ts +59 -0
- package/src/crawl/article-converter.test.ts +604 -0
- package/src/crawl/article-converter.ts +98 -0
- package/src/crawl/bridge.test.ts +674 -0
- package/src/crawl/bridge.ts +236 -0
- package/src/crawl/claude-client.test.ts +663 -0
- package/src/crawl/claude-client.ts +234 -0
- package/src/crawl/intelligent-crawler.test.ts +931 -0
- package/src/crawl/intelligent-crawler.ts +428 -0
- package/src/crawl/markdown-utils.test.ts +703 -0
- package/src/crawl/markdown-utils.ts +228 -0
- package/src/crawl/schemas.ts +114 -0
- package/src/db/embeddings.test.ts +63 -0
- package/src/db/embeddings.ts +69 -0
- package/src/db/index.ts +2 -0
- package/src/db/lance.test.ts +390 -0
- package/src/db/lance.ts +164 -0
- package/src/defaults/repos.ts +67 -0
- package/src/index.ts +107 -0
- package/src/mcp/cache.test.ts +202 -0
- package/src/mcp/cache.ts +103 -0
- package/src/mcp/commands/index.ts +20 -0
- package/src/mcp/commands/job.commands.ts +54 -0
- package/src/mcp/commands/meta.commands.ts +54 -0
- package/src/mcp/commands/registry.ts +183 -0
- package/src/mcp/commands/store.commands.ts +75 -0
- package/src/mcp/handlers/execute.handler.test.ts +179 -0
- package/src/mcp/handlers/execute.handler.ts +24 -0
- package/src/mcp/handlers/index.ts +43 -0
- package/src/mcp/handlers/job.handler.test.ts +189 -0
- package/src/mcp/handlers/job.handler.ts +116 -0
- package/src/mcp/handlers/search.handler.test.ts +334 -0
- package/src/mcp/handlers/search.handler.ts +209 -0
- package/src/mcp/handlers/store.handler.test.ts +415 -0
- package/src/mcp/handlers/store.handler.ts +295 -0
- package/src/mcp/schemas/index.test.ts +315 -0
- package/src/mcp/schemas/index.ts +138 -0
- package/src/mcp/server.test.ts +36 -0
- package/src/mcp/server.ts +162 -0
- package/src/mcp/types.ts +41 -0
- package/src/plugin/commands.test.ts +789 -0
- package/src/plugin/commands.ts +257 -0
- package/src/plugin/dependency-analyzer.test.ts +380 -0
- package/src/plugin/dependency-analyzer.ts +147 -0
- package/src/plugin/git-clone.test.ts +332 -0
- package/src/plugin/git-clone.ts +57 -0
- package/src/server/app.test.ts +752 -0
- package/src/server/app.ts +119 -0
- package/src/server/index.test.ts +477 -0
- package/src/server/index.ts +1 -0
- package/src/services/chunking.service.test.ts +363 -0
- package/src/services/chunking.service.ts +350 -0
- package/src/services/code-graph.service.test.ts +304 -0
- package/src/services/code-graph.service.ts +302 -0
- package/src/services/code-unit.service.test.ts +596 -0
- package/src/services/code-unit.service.ts +115 -0
- package/src/services/config.service.test.ts +127 -0
- package/src/services/config.service.ts +69 -0
- package/src/services/index.service.test.ts +1002 -0
- package/src/services/index.service.ts +266 -0
- package/src/services/index.ts +75 -0
- package/src/services/job.service.test.ts +418 -0
- package/src/services/job.service.ts +246 -0
- package/src/services/project-root.service.test.ts +506 -0
- package/src/services/project-root.service.ts +112 -0
- package/src/services/search.service.test.ts +1105 -0
- package/src/services/search.service.ts +892 -0
- package/src/services/services.test.ts +38 -0
- package/src/services/snippet.service.test.ts +205 -0
- package/src/services/snippet.service.ts +166 -0
- package/src/services/store.service.test.ts +474 -0
- package/src/services/store.service.ts +225 -0
- package/src/services/watch.service.test.ts +553 -0
- package/src/services/watch.service.ts +71 -0
- package/src/types/brands.test.ts +45 -0
- package/src/types/brands.ts +32 -0
- package/src/types/config.ts +79 -0
- package/src/types/document.ts +30 -0
- package/src/types/index.ts +66 -0
- package/src/types/job.ts +46 -0
- package/src/types/progress.ts +9 -0
- package/src/types/result.test.ts +44 -0
- package/src/types/result.ts +41 -0
- package/src/types/search.ts +95 -0
- package/src/types/store.test.ts +69 -0
- package/src/types/store.ts +47 -0
- package/src/utils/type-guards.test.ts +346 -0
- package/src/utils/type-guards.ts +61 -0
- package/src/workers/background-worker-cli.ts +105 -0
- package/src/workers/background-worker.test.ts +178 -0
- package/src/workers/background-worker.ts +294 -0
- package/src/workers/spawn-worker.test.ts +128 -0
- package/src/workers/spawn-worker.ts +49 -0
- package/tests/analysis/ast-parser.test.ts +98 -0
- package/tests/analysis/code-graph.test.ts +60 -0
- package/tests/fixtures/README.md +114 -0
- package/tests/fixtures/code-snippets/api/error-handling.ts +267 -0
- package/tests/fixtures/code-snippets/api/rest-controller.ts +303 -0
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +213 -0
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +245 -0
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +272 -0
- package/tests/fixtures/corpus/VERSION.md +25 -0
- package/tests/fixtures/corpus/articles/jwt-authentication.md +97 -0
- package/tests/fixtures/corpus/articles/react-hooks-patterns.md +127 -0
- package/tests/fixtures/corpus/articles/typescript-generics.md +111 -0
- package/tests/fixtures/corpus/documentation/express-middleware.md +71 -0
- package/tests/fixtures/corpus/documentation/express-routing.md +83 -0
- package/tests/fixtures/corpus/documentation/node-streams.md +78 -0
- package/tests/fixtures/corpus/oss-repos/express/History.md +3871 -0
- package/tests/fixtures/corpus/oss-repos/express/LICENSE +24 -0
- package/tests/fixtures/corpus/oss-repos/express/Readme.md +276 -0
- package/tests/fixtures/corpus/oss-repos/express/SECURITY.md +56 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/Makefile +17 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/README.md +34 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/middleware.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/benchmarks/run +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/README.md +29 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/index.js +134 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/foot.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/head.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/auth/views/login.ejs +21 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/db.js +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/index.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/content-negotiation/users.js +19 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/cookie-sessions/index.js +25 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/cookies/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/CCTV/345/244/247/350/265/233/344/270/212/346/265/267/345/210/206/350/265/233/345/214/272.txt +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/amazing.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/files/notes/groceries.txt +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/downloads/index.js +40 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/index.js +57 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/public/stylesheets/style.css +4 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/footer.html +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/header.html +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/ejs/views/users.html +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/index.js +103 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/404.ejs +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/500.ejs +8 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/error_header.ejs +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/footer.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/error-pages/views/index.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/hello-world/index.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/markdown/index.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/markdown/views/index.md +4 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/controllers/api_v1.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/controllers/api_v2.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/multi-router/index.js +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/main/index.js +5 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/index.js +31 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/views/edit.ejs +17 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/pet/views/show.ejs +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/index.js +41 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/edit.hbs +27 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/list.hbs +18 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user/views/show.hbs +31 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/controllers/user-pet/index.js +22 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/db.js +16 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/index.js +95 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/lib/boot.js +83 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/public/style.css +14 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/views/404.ejs +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/mvc/views/5xx.ejs +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/online/index.js +61 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/params/index.js +74 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/resource/index.js +95 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-map/index.js +75 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-middleware/index.js +90 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/index.js +55 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/post.js +13 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/public/style.css +24 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/site.js +5 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/user.js +47 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/footer.ejs +2 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/header.ejs +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/index.ejs +10 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/posts/index.ejs +12 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/edit.ejs +23 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/index.ejs +14 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/route-separation/views/users/view.ejs +9 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/index.js +61 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/public/client.js +15 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/search/public/index.html +21 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/session/index.js +37 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/session/redis.js +39 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/index.js +43 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/css/style.css +3 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/hello.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/static-files/public/js/app.js +1 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/vhost/index.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-constructor/github-view.js +53 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-constructor/index.js +48 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/index.js +155 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/user.js +36 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/view-locals/views/index.ejs +20 -0
- package/tests/fixtures/corpus/oss-repos/express/examples/web-service/index.js +117 -0
- package/tests/fixtures/corpus/oss-repos/express/index.js +11 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/application.js +631 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/express.js +81 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/request.js +514 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/response.js +1053 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/utils.js +271 -0
- package/tests/fixtures/corpus/oss-repos/express/lib/view.js +205 -0
- package/tests/fixtures/corpus/oss-repos/express/package.json +99 -0
- package/tests/fixtures/corpus/oss-repos/express/test/Route.js +274 -0
- package/tests/fixtures/corpus/oss-repos/express/test/Router.js +636 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/auth.js +117 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/content-negotiation.js +49 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/cookie-sessions.js +38 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/cookies.js +71 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/downloads.js +47 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/ejs.js +17 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/error-pages.js +99 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/error.js +29 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/hello-world.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/markdown.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/multi-router.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/mvc.js +132 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/params.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/resource.js +68 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/route-map.js +45 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/route-separation.js +97 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/vhost.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/test/acceptance/web-service.js +105 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.all.js +38 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.engine.js +83 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.head.js +66 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.js +120 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.listen.js +55 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.locals.js +26 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.options.js +116 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.param.js +323 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.render.js +374 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.request.js +143 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.response.js +143 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.route.js +197 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.router.js +1217 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.routes.error.js +62 -0
- package/tests/fixtures/corpus/oss-repos/express/test/app.use.js +542 -0
- package/tests/fixtures/corpus/oss-repos/express/test/config.js +207 -0
- package/tests/fixtures/corpus/oss-repos/express/test/exports.js +82 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.json.js +755 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.raw.js +513 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.static.js +815 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.text.js +566 -0
- package/tests/fixtures/corpus/oss-repos/express/test/express.urlencoded.js +828 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/% of dogs.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/.name +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/blog/index.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/blog/post/index.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/broken.send +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/default_layout/name.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/default_layout/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/email.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/empty.txt +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/local_layout/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/name.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/name.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/nums.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/pets/names.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/snow /342/230/203/.gitkeep +0 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/todo.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/todo.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/user.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/user.tmpl +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/users/index.html +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/fixtures/users/tobi.txt +1 -0
- package/tests/fixtures/corpus/oss-repos/express/test/middleware.basic.js +42 -0
- package/tests/fixtures/corpus/oss-repos/express/test/regression.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.accepts.js +125 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsCharsets.js +50 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsEncodings.js +39 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.acceptsLanguages.js +57 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.baseUrl.js +88 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.fresh.js +70 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.get.js +60 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.host.js +156 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.hostname.js +188 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.ip.js +113 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.ips.js +71 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.is.js +169 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.path.js +20 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.protocol.js +113 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.query.js +106 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.range.js +104 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.route.js +28 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.secure.js +101 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.signedCookies.js +37 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.stale.js +50 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.subdomains.js +173 -0
- package/tests/fixtures/corpus/oss-repos/express/test/req.xhr.js +42 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.append.js +116 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.attachment.js +79 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.clearCookie.js +62 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.cookie.js +295 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.download.js +487 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.format.js +248 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.get.js +21 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.json.js +186 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.jsonp.js +344 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.links.js +65 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.locals.js +40 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.location.js +316 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.redirect.js +214 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.render.js +367 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.send.js +569 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.sendFile.js +913 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.sendStatus.js +44 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.set.js +124 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.status.js +206 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.type.js +46 -0
- package/tests/fixtures/corpus/oss-repos/express/test/res.vary.js +90 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/env.js +3 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/tmpl.js +36 -0
- package/tests/fixtures/corpus/oss-repos/express/test/support/utils.js +86 -0
- package/tests/fixtures/corpus/oss-repos/express/test/utils.js +83 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/Dockerfile +11 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/devcontainer.json +21 -0
- package/tests/fixtures/corpus/oss-repos/hono/.devcontainer/docker-compose.yml +18 -0
- package/tests/fixtures/corpus/oss-repos/hono/.eslintignore +1 -0
- package/tests/fixtures/corpus/oss-repos/hono/.eslintrc.cjs +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.gitpod.yml +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.prettierrc +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/jsx-runtime-default.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/jsx-runtime-dom.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/.vitest.config/setup-vitest.ts +47 -0
- package/tests/fixtures/corpus/oss-repos/hono/LICENSE +21 -0
- package/tests/fixtures/corpus/oss-repos/hono/README.md +91 -0
- package/tests/fixtures/corpus/oss-repos/hono/build.ts +80 -0
- package/tests/fixtures/corpus/oss-repos/hono/bun.lockb +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/bunfig.toml +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/codecov.yml +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/CODE_OF_CONDUCT.md +128 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/CONTRIBUTING.md +62 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/MIGRATION.md +295 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.png +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.pxm +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-logo.svg +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-title.png +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/docs/images/hono-title.pxm +0 -0
- package/tests/fixtures/corpus/oss-repos/hono/jsr.json +119 -0
- package/tests/fixtures/corpus/oss-repos/hono/package.cjs.json +3 -0
- package/tests/fixtures/corpus/oss-repos/hono/package.json +650 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +492 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/types.ts +144 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/conninfo.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/serve-static.ts +35 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/server.ts +30 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/ssg.ts +27 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/bun/websocket.ts +110 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +120 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/conninfo.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static-module.ts +12 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +39 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/utils.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/websocket.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/conninfo.ts +17 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/deno.d.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/serve-static.ts +40 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/ssg.ts +27 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/deno/websocket.ts +51 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/conninfo.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/handler.ts +189 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/lambda-edge/index.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/handler.ts +10 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/netlify/mod.ts +1 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/handler.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/index.ts +5 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/service-worker/types.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/conninfo.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/handler.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/vercel/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +214 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/index.ts +14 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +180 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/client/utils.ts +54 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/compose.ts +94 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +914 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +81 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +85 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/conninfo/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/conninfo/types.ts +45 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/cookie/index.ts +130 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/common.ts +243 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +220 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/dev/index.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +246 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/html/index.ts +56 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/middleware.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +388 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/utils.ts +71 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/index.ts +9 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/sse.ts +89 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/stream.ts +36 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/streaming/text.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/testing/index.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/websocket/index.ts +57 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +523 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/http-exception.ts +78 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/index.ts +51 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/base.ts +419 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/children.ts +20 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/components.ts +195 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/constants.ts +5 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/context.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/client.ts +89 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/components.ts +39 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/context.ts +52 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +246 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/hooks/index.ts +91 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/index.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +398 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/jsx-dev-runtime.ts +22 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/jsx-runtime.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +772 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/server.ts +70 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/utils.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +426 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/index.ts +114 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/common.ts +11 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +196 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-elements.ts +924 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/jsx-dev-runtime.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/jsx-runtime.ts +18 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/streaming.ts +184 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/types.ts +41 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +36 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/basic-auth/index.ts +128 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/bearer-auth/index.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/body-limit/index.ts +115 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/cache/index.ts +127 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/combine/index.ts +153 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/compress/index.ts +79 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/context-storage/index.ts +55 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/cors/index.ts +141 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/csrf/index.ts +90 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/etag/index.ts +88 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/ip-restriction/index.ts +178 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +158 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jwt/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jwt/jwt.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/logger/index.ts +93 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/method-override/index.ts +146 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/powered-by/index.ts +13 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/pretty-json/index.ts +50 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/request-id/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/request-id/request-id.ts +59 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/index.ts +8 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/permissions-policy.ts +86 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/secure-headers/secure-headers.ts +319 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +140 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timeout/index.ts +58 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timing/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/timing/timing.ts +225 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/trailing-slash/index.ts +71 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +24 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +20 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/request.ts +403 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/linear-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/linear-router/router.ts +132 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +54 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +159 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +274 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/trie.ts +74 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/smart-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/smart-router/router.ts +69 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/index.ts +6 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +205 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/router.ts +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/router.ts +103 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +2009 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/basic-auth.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +225 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/buffer.ts +65 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +26 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/concurrent.ts +55 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +230 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/crypto.ts +65 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/filepath.ts +56 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/handler.ts +15 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/html.ts +182 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/http-status.ts +69 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/ipaddr.ts +113 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jwa.ts +23 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jws.ts +226 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/jwt.ts +114 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/types.ts +83 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/jwt/utf8.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/mime.ts +142 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/stream.ts +96 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +105 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/url.ts +310 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/index.ts +7 -0
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +151 -0
- package/tests/fixtures/corpus/oss-repos/hono/tsconfig.build.json +23 -0
- package/tests/fixtures/corpus/oss-repos/hono/tsconfig.json +28 -0
- package/tests/fixtures/corpus/oss-repos/hono/vitest.config.ts +34 -0
- package/tests/fixtures/corpus/oss-repos/hono/yarn.lock +6232 -0
- package/tests/fixtures/documentation/api-reference.md +412 -0
- package/tests/fixtures/documentation/architecture.md +214 -0
- package/tests/fixtures/documentation/deployment-guide.md +420 -0
- package/tests/fixtures/github-readmes/express.md +133 -0
- package/tests/fixtures/github-readmes/nextjs.md +106 -0
- package/tests/fixtures/github-readmes/react.md +74 -0
- package/tests/fixtures/github-readmes/typescript.md +93 -0
- package/tests/fixtures/github-readmes/vite.md +79 -0
- package/tests/fixtures/queries/core.json +125 -0
- package/tests/fixtures/queries/extended.json +427 -0
- package/tests/fixtures/queries/generated/.gitkeep +0 -0
- package/tests/fixtures/test-server.ts +267 -0
- package/tests/helpers/performance-metrics.ts +387 -0
- package/tests/helpers/search-relevance.ts +381 -0
- package/tests/integration/cli-consistency.test.ts +299 -0
- package/tests/integration/cli.test.ts +69 -0
- package/tests/integration/e2e-workflow.test.ts +612 -0
- package/tests/integration/python-bridge.test.ts +183 -0
- package/tests/integration/search-quality.test.ts +718 -0
- package/tests/integration/stress.test.ts +326 -0
- package/tests/mcp/server.test.ts +15 -0
- package/tests/scripts/schemas/evaluation.json +44 -0
- package/tests/scripts/schemas/query-generation.json +21 -0
- package/tests/services/code-unit.service.test.ts +47 -0
- package/tests/services/search.progressive-context.test.ts +35 -0
- package/tsconfig.json +34 -0
- package/tsup.config.ts +15 -0
- package/turndown-plugin-gfm.d.ts +29 -0
- package/vitest.config.ts +79 -0
|
@@ -0,0 +1,1518 @@
|
|
|
1
|
+
# Phase 2 Features Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Complete all remaining features from the CLI specification: document chunking, full-text search, hybrid search, progress bars, HTTP server, file watching, web crawling, and export/import.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Build incrementally on existing service layer. Add chunking to IndexService, FTS to LanceStore, hybrid search to SearchService. Add Hono server that reuses same services. Python bridge for web crawling via JSON-RPC subprocess.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** LanceDB (FTS built-in), Hono, ora, chokidar, Python crawl4ai
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Group A: Search Improvements
|
|
14
|
+
|
|
15
|
+
### Task A.1: Create ChunkingService
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Create: `src/services/chunking.service.ts`
|
|
19
|
+
- Create: `src/services/chunking.service.test.ts`
|
|
20
|
+
|
|
21
|
+
**Step 1: Write the failing test**
|
|
22
|
+
|
|
23
|
+
Create `src/services/chunking.service.test.ts`:
|
|
24
|
+
```typescript
|
|
25
|
+
import { describe, it, expect } from 'vitest';
|
|
26
|
+
import { ChunkingService } from './chunking.service.js';
|
|
27
|
+
|
|
28
|
+
describe('ChunkingService', () => {
|
|
29
|
+
const chunker = new ChunkingService({ chunkSize: 100, chunkOverlap: 20 });
|
|
30
|
+
|
|
31
|
+
it('splits text into chunks', () => {
|
|
32
|
+
const text = 'A'.repeat(250);
|
|
33
|
+
const chunks = chunker.chunk(text);
|
|
34
|
+
expect(chunks.length).toBeGreaterThan(1);
|
|
35
|
+
expect(chunks.every(c => c.content.length <= 100)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('preserves overlap between chunks', () => {
|
|
39
|
+
const text = 'word '.repeat(50); // 250 chars
|
|
40
|
+
const chunks = chunker.chunk(text);
|
|
41
|
+
if (chunks.length >= 2) {
|
|
42
|
+
const end1 = chunks[0]!.content.slice(-20);
|
|
43
|
+
const start2 = chunks[1]!.content.slice(0, 20);
|
|
44
|
+
expect(end1).toBe(start2);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns single chunk for small text', () => {
|
|
49
|
+
const text = 'small text';
|
|
50
|
+
const chunks = chunker.chunk(text);
|
|
51
|
+
expect(chunks).toHaveLength(1);
|
|
52
|
+
expect(chunks[0]!.content).toBe(text);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('assigns chunk indices', () => {
|
|
56
|
+
const text = 'A'.repeat(300);
|
|
57
|
+
const chunks = chunker.chunk(text);
|
|
58
|
+
expect(chunks[0]!.chunkIndex).toBe(0);
|
|
59
|
+
expect(chunks[1]!.chunkIndex).toBe(1);
|
|
60
|
+
expect(chunks.every(c => c.totalChunks === chunks.length)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Step 2: Run test to verify it fails**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm run test:run -- src/services/chunking.service.test.ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Expected: FAIL - module not found
|
|
72
|
+
|
|
73
|
+
**Step 3: Write implementation**
|
|
74
|
+
|
|
75
|
+
Create `src/services/chunking.service.ts`:
|
|
76
|
+
```typescript
|
|
77
|
+
export interface ChunkConfig {
|
|
78
|
+
chunkSize: number;
|
|
79
|
+
chunkOverlap: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface Chunk {
|
|
83
|
+
content: string;
|
|
84
|
+
chunkIndex: number;
|
|
85
|
+
totalChunks: number;
|
|
86
|
+
startOffset: number;
|
|
87
|
+
endOffset: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class ChunkingService {
|
|
91
|
+
private readonly chunkSize: number;
|
|
92
|
+
private readonly chunkOverlap: number;
|
|
93
|
+
|
|
94
|
+
constructor(config: ChunkConfig) {
|
|
95
|
+
this.chunkSize = config.chunkSize;
|
|
96
|
+
this.chunkOverlap = config.chunkOverlap;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
chunk(text: string): Chunk[] {
|
|
100
|
+
if (text.length <= this.chunkSize) {
|
|
101
|
+
return [{
|
|
102
|
+
content: text,
|
|
103
|
+
chunkIndex: 0,
|
|
104
|
+
totalChunks: 1,
|
|
105
|
+
startOffset: 0,
|
|
106
|
+
endOffset: text.length,
|
|
107
|
+
}];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const chunks: Chunk[] = [];
|
|
111
|
+
const step = this.chunkSize - this.chunkOverlap;
|
|
112
|
+
let start = 0;
|
|
113
|
+
|
|
114
|
+
while (start < text.length) {
|
|
115
|
+
const end = Math.min(start + this.chunkSize, text.length);
|
|
116
|
+
chunks.push({
|
|
117
|
+
content: text.slice(start, end),
|
|
118
|
+
chunkIndex: chunks.length,
|
|
119
|
+
totalChunks: 0, // Will be set after
|
|
120
|
+
startOffset: start,
|
|
121
|
+
endOffset: end,
|
|
122
|
+
});
|
|
123
|
+
start += step;
|
|
124
|
+
if (end === text.length) break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Set totalChunks
|
|
128
|
+
for (const chunk of chunks) {
|
|
129
|
+
chunk.totalChunks = chunks.length;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return chunks;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Step 4: Run test to verify it passes**
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run test:run -- src/services/chunking.service.test.ts
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Expected: PASS
|
|
144
|
+
|
|
145
|
+
**Step 5: Commit**
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git add src/services/chunking.service.ts src/services/chunking.service.test.ts
|
|
149
|
+
git commit -m "feat: add ChunkingService for document splitting"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### Task A.2: Integrate chunking into IndexService
|
|
155
|
+
|
|
156
|
+
**Files:**
|
|
157
|
+
- Modify: `src/services/index.service.ts`
|
|
158
|
+
- Modify: `src/services/index.service.test.ts`
|
|
159
|
+
|
|
160
|
+
**Step 1: Update test to verify chunking**
|
|
161
|
+
|
|
162
|
+
Add to `src/services/index.service.test.ts`:
|
|
163
|
+
```typescript
|
|
164
|
+
it('chunks large files', async () => {
|
|
165
|
+
// Create a large test file
|
|
166
|
+
const largeContent = 'This is test content. '.repeat(100); // ~2200 chars
|
|
167
|
+
await writeFile(join(testFilesDir, 'large.txt'), largeContent);
|
|
168
|
+
|
|
169
|
+
const store: FileStore = {
|
|
170
|
+
type: 'file',
|
|
171
|
+
id: storeId,
|
|
172
|
+
name: 'Test Store',
|
|
173
|
+
path: testFilesDir,
|
|
174
|
+
createdAt: new Date(),
|
|
175
|
+
updatedAt: new Date(),
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const result = await indexService.indexStore(store);
|
|
179
|
+
|
|
180
|
+
expect(result.success).toBe(true);
|
|
181
|
+
if (result.success) {
|
|
182
|
+
expect(result.data.chunksCreated).toBeGreaterThan(result.data.documentsIndexed);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Step 2: Run test to verify it fails**
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm run test:run -- src/services/index.service.test.ts
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Expected: FAIL - chunks equal documents (no chunking)
|
|
194
|
+
|
|
195
|
+
**Step 3: Update IndexService implementation**
|
|
196
|
+
|
|
197
|
+
Update `src/services/index.service.ts`:
|
|
198
|
+
```typescript
|
|
199
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
200
|
+
import { join, extname } from 'node:path';
|
|
201
|
+
import { createHash } from 'node:crypto';
|
|
202
|
+
import type { LanceStore } from '../db/lance.js';
|
|
203
|
+
import type { EmbeddingEngine } from '../db/embeddings.js';
|
|
204
|
+
import type { Store, FileStore, RepoStore } from '../types/store.js';
|
|
205
|
+
import type { Document } from '../types/document.js';
|
|
206
|
+
import { createDocumentId } from '../types/brands.js';
|
|
207
|
+
import type { Result } from '../types/result.js';
|
|
208
|
+
import { ok, err } from '../types/result.js';
|
|
209
|
+
import { ChunkingService } from './chunking.service.js';
|
|
210
|
+
|
|
211
|
+
interface IndexResult {
|
|
212
|
+
documentsIndexed: number;
|
|
213
|
+
chunksCreated: number;
|
|
214
|
+
timeMs: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
interface IndexOptions {
|
|
218
|
+
chunkSize?: number;
|
|
219
|
+
chunkOverlap?: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const TEXT_EXTENSIONS = new Set([
|
|
223
|
+
'.txt', '.md', '.js', '.ts', '.jsx', '.tsx', '.json', '.yaml', '.yml',
|
|
224
|
+
'.html', '.css', '.scss', '.less', '.py', '.rb', '.go', '.rs', '.java',
|
|
225
|
+
'.c', '.cpp', '.h', '.hpp', '.sh', '.bash', '.zsh', '.sql', '.xml',
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
export class IndexService {
|
|
229
|
+
private readonly lanceStore: LanceStore;
|
|
230
|
+
private readonly embeddingEngine: EmbeddingEngine;
|
|
231
|
+
private readonly chunker: ChunkingService;
|
|
232
|
+
|
|
233
|
+
constructor(
|
|
234
|
+
lanceStore: LanceStore,
|
|
235
|
+
embeddingEngine: EmbeddingEngine,
|
|
236
|
+
options: IndexOptions = {}
|
|
237
|
+
) {
|
|
238
|
+
this.lanceStore = lanceStore;
|
|
239
|
+
this.embeddingEngine = embeddingEngine;
|
|
240
|
+
this.chunker = new ChunkingService({
|
|
241
|
+
chunkSize: options.chunkSize ?? 512,
|
|
242
|
+
chunkOverlap: options.chunkOverlap ?? 50,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async indexStore(store: Store): Promise<Result<IndexResult>> {
|
|
247
|
+
try {
|
|
248
|
+
if (store.type === 'file' || store.type === 'repo') {
|
|
249
|
+
return await this.indexFileStore(store);
|
|
250
|
+
}
|
|
251
|
+
return err(new Error(`Indexing not supported for store type: ${store.type}`));
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private async indexFileStore(store: FileStore | RepoStore): Promise<Result<IndexResult>> {
|
|
258
|
+
const startTime = Date.now();
|
|
259
|
+
const files = await this.scanDirectory(store.path);
|
|
260
|
+
const documents: Document[] = [];
|
|
261
|
+
let filesProcessed = 0;
|
|
262
|
+
|
|
263
|
+
for (const filePath of files) {
|
|
264
|
+
const content = await readFile(filePath, 'utf-8');
|
|
265
|
+
const fileHash = createHash('md5').update(content).digest('hex');
|
|
266
|
+
const chunks = this.chunker.chunk(content);
|
|
267
|
+
|
|
268
|
+
for (const chunk of chunks) {
|
|
269
|
+
const vector = await this.embeddingEngine.embed(chunk.content);
|
|
270
|
+
const chunkId = chunks.length > 1
|
|
271
|
+
? `${store.id}-${fileHash}-${chunk.chunkIndex}`
|
|
272
|
+
: `${store.id}-${fileHash}`;
|
|
273
|
+
|
|
274
|
+
const doc: Document = {
|
|
275
|
+
id: createDocumentId(chunkId),
|
|
276
|
+
content: chunk.content,
|
|
277
|
+
vector,
|
|
278
|
+
metadata: {
|
|
279
|
+
type: chunks.length > 1 ? 'chunk' : 'file',
|
|
280
|
+
storeId: store.id,
|
|
281
|
+
path: filePath,
|
|
282
|
+
indexedAt: new Date(),
|
|
283
|
+
fileHash,
|
|
284
|
+
chunkIndex: chunk.chunkIndex,
|
|
285
|
+
totalChunks: chunk.totalChunks,
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
documents.push(doc);
|
|
289
|
+
}
|
|
290
|
+
filesProcessed++;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (documents.length > 0) {
|
|
294
|
+
await this.lanceStore.addDocuments(store.id, documents);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return ok({
|
|
298
|
+
documentsIndexed: filesProcessed,
|
|
299
|
+
chunksCreated: documents.length,
|
|
300
|
+
timeMs: Date.now() - startTime,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private async scanDirectory(dir: string): Promise<string[]> {
|
|
305
|
+
const files: string[] = [];
|
|
306
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
307
|
+
|
|
308
|
+
for (const entry of entries) {
|
|
309
|
+
const fullPath = join(dir, entry.name);
|
|
310
|
+
if (entry.isDirectory()) {
|
|
311
|
+
if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
|
|
312
|
+
files.push(...(await this.scanDirectory(fullPath)));
|
|
313
|
+
}
|
|
314
|
+
} else if (entry.isFile()) {
|
|
315
|
+
const ext = extname(entry.name).toLowerCase();
|
|
316
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
317
|
+
files.push(fullPath);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return files;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Step 4: Run test to verify it passes**
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
npm run test:run -- src/services/index.service.test.ts
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Expected: PASS
|
|
333
|
+
|
|
334
|
+
**Step 5: Commit**
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
git add src/services/index.service.ts src/services/index.service.test.ts
|
|
338
|
+
git commit -m "feat: integrate chunking into IndexService"
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
### Task A.3: Add full-text search to LanceStore
|
|
344
|
+
|
|
345
|
+
**Files:**
|
|
346
|
+
- Modify: `src/db/lance.ts`
|
|
347
|
+
- Modify: `src/db/lance.test.ts`
|
|
348
|
+
|
|
349
|
+
**Step 1: Add FTS test**
|
|
350
|
+
|
|
351
|
+
Add to `src/db/lance.test.ts`:
|
|
352
|
+
```typescript
|
|
353
|
+
it('performs full-text search', async () => {
|
|
354
|
+
const doc = {
|
|
355
|
+
id: createDocumentId('fts-doc'),
|
|
356
|
+
content: 'The quick brown fox jumps over the lazy dog',
|
|
357
|
+
vector: new Array(384).fill(0.1),
|
|
358
|
+
metadata: {
|
|
359
|
+
type: 'file' as const,
|
|
360
|
+
storeId,
|
|
361
|
+
indexedAt: new Date(),
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
await store.addDocuments(storeId, [doc]);
|
|
366
|
+
|
|
367
|
+
const results = await store.fullTextSearch(storeId, 'quick brown', 10);
|
|
368
|
+
expect(results.length).toBeGreaterThan(0);
|
|
369
|
+
expect(results[0]?.content).toContain('quick');
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Step 2: Run test to verify it fails**
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npm run test:run -- src/db/lance.test.ts
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Expected: FAIL - fullTextSearch not defined
|
|
380
|
+
|
|
381
|
+
**Step 3: Add FTS implementation**
|
|
382
|
+
|
|
383
|
+
Add to `src/db/lance.ts`:
|
|
384
|
+
```typescript
|
|
385
|
+
async createFtsIndex(storeId: StoreId): Promise<void> {
|
|
386
|
+
const table = await this.getTable(storeId);
|
|
387
|
+
await table.createIndex('content', {
|
|
388
|
+
config: lancedb.Index.fts(),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async fullTextSearch(
|
|
393
|
+
storeId: StoreId,
|
|
394
|
+
query: string,
|
|
395
|
+
limit: number
|
|
396
|
+
): Promise<Array<{ id: DocumentId; content: string; score: number; metadata: DocumentMetadata }>> {
|
|
397
|
+
const table = await this.getTable(storeId);
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
const results = await table
|
|
401
|
+
.search(query, 'fts')
|
|
402
|
+
.limit(limit)
|
|
403
|
+
.toArray() as Array<{ id: string; content: string; metadata: string; score: number }>;
|
|
404
|
+
|
|
405
|
+
return results.map((r) => ({
|
|
406
|
+
id: createDocumentId(r.id),
|
|
407
|
+
content: r.content,
|
|
408
|
+
score: r.score ?? 1,
|
|
409
|
+
metadata: JSON.parse(r.metadata) as DocumentMetadata,
|
|
410
|
+
}));
|
|
411
|
+
} catch {
|
|
412
|
+
// FTS index may not exist, return empty
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Step 4: Run test to verify it passes**
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
npm run test:run -- src/db/lance.test.ts
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Expected: PASS
|
|
425
|
+
|
|
426
|
+
**Step 5: Commit**
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
git add src/db/lance.ts src/db/lance.test.ts
|
|
430
|
+
git commit -m "feat: add full-text search to LanceStore"
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
### Task A.4: Add hybrid search with RRF to SearchService
|
|
436
|
+
|
|
437
|
+
**Files:**
|
|
438
|
+
- Modify: `src/services/search.service.ts`
|
|
439
|
+
- Modify: `src/services/search.service.test.ts`
|
|
440
|
+
|
|
441
|
+
**Step 1: Add hybrid search test**
|
|
442
|
+
|
|
443
|
+
Add to `src/services/search.service.test.ts`:
|
|
444
|
+
```typescript
|
|
445
|
+
it('performs hybrid search combining vector and FTS', async () => {
|
|
446
|
+
const results = await searchService.search({
|
|
447
|
+
query: 'JavaScript programming',
|
|
448
|
+
stores: [storeId],
|
|
449
|
+
mode: 'hybrid',
|
|
450
|
+
limit: 10,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(results.mode).toBe('hybrid');
|
|
454
|
+
expect(results.results.length).toBeGreaterThan(0);
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Step 2: Run test to verify it fails**
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
npm run test:run -- src/services/search.service.test.ts
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Expected: FAIL or PASS (current implementation falls back to vector)
|
|
465
|
+
|
|
466
|
+
**Step 3: Implement hybrid search with RRF**
|
|
467
|
+
|
|
468
|
+
Update `src/services/search.service.ts`:
|
|
469
|
+
```typescript
|
|
470
|
+
import type { LanceStore } from '../db/lance.js';
|
|
471
|
+
import type { EmbeddingEngine } from '../db/embeddings.js';
|
|
472
|
+
import type { SearchQuery, SearchResponse, SearchResult } from '../types/search.js';
|
|
473
|
+
import type { StoreId, DocumentId } from '../types/brands.js';
|
|
474
|
+
|
|
475
|
+
interface RRFConfig {
|
|
476
|
+
k: number;
|
|
477
|
+
vectorWeight: number;
|
|
478
|
+
ftsWeight: number;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export class SearchService {
|
|
482
|
+
private readonly lanceStore: LanceStore;
|
|
483
|
+
private readonly embeddingEngine: EmbeddingEngine;
|
|
484
|
+
private readonly rrfConfig: RRFConfig;
|
|
485
|
+
|
|
486
|
+
constructor(
|
|
487
|
+
lanceStore: LanceStore,
|
|
488
|
+
embeddingEngine: EmbeddingEngine,
|
|
489
|
+
rrfConfig: RRFConfig = { k: 60, vectorWeight: 0.7, ftsWeight: 0.3 }
|
|
490
|
+
) {
|
|
491
|
+
this.lanceStore = lanceStore;
|
|
492
|
+
this.embeddingEngine = embeddingEngine;
|
|
493
|
+
this.rrfConfig = rrfConfig;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async search(query: SearchQuery): Promise<SearchResponse> {
|
|
497
|
+
const startTime = Date.now();
|
|
498
|
+
const mode = query.mode ?? 'hybrid';
|
|
499
|
+
const limit = query.limit ?? 10;
|
|
500
|
+
const stores = query.stores ?? [];
|
|
501
|
+
|
|
502
|
+
let allResults: SearchResult[] = [];
|
|
503
|
+
|
|
504
|
+
if (mode === 'vector') {
|
|
505
|
+
allResults = await this.vectorSearch(query.query, stores, limit, query.threshold);
|
|
506
|
+
} else if (mode === 'fts') {
|
|
507
|
+
allResults = await this.ftsSearch(query.query, stores, limit);
|
|
508
|
+
} else {
|
|
509
|
+
// Hybrid: combine vector and FTS with RRF
|
|
510
|
+
allResults = await this.hybridSearch(query.query, stores, limit, query.threshold);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
query: query.query,
|
|
515
|
+
mode,
|
|
516
|
+
stores,
|
|
517
|
+
results: allResults,
|
|
518
|
+
totalResults: allResults.length,
|
|
519
|
+
timeMs: Date.now() - startTime,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
private async vectorSearch(
|
|
524
|
+
query: string,
|
|
525
|
+
stores: readonly StoreId[],
|
|
526
|
+
limit: number,
|
|
527
|
+
threshold?: number
|
|
528
|
+
): Promise<SearchResult[]> {
|
|
529
|
+
const queryVector = await this.embeddingEngine.embed(query);
|
|
530
|
+
const results: SearchResult[] = [];
|
|
531
|
+
|
|
532
|
+
for (const storeId of stores) {
|
|
533
|
+
const hits = await this.lanceStore.search(storeId, queryVector, limit, threshold);
|
|
534
|
+
results.push(...hits.map(r => ({
|
|
535
|
+
id: r.id,
|
|
536
|
+
score: r.score,
|
|
537
|
+
content: r.content,
|
|
538
|
+
metadata: r.metadata,
|
|
539
|
+
})));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private async ftsSearch(
|
|
546
|
+
query: string,
|
|
547
|
+
stores: readonly StoreId[],
|
|
548
|
+
limit: number
|
|
549
|
+
): Promise<SearchResult[]> {
|
|
550
|
+
const results: SearchResult[] = [];
|
|
551
|
+
|
|
552
|
+
for (const storeId of stores) {
|
|
553
|
+
const hits = await this.lanceStore.fullTextSearch(storeId, query, limit);
|
|
554
|
+
results.push(...hits.map(r => ({
|
|
555
|
+
id: r.id,
|
|
556
|
+
score: r.score,
|
|
557
|
+
content: r.content,
|
|
558
|
+
metadata: r.metadata,
|
|
559
|
+
})));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private async hybridSearch(
|
|
566
|
+
query: string,
|
|
567
|
+
stores: readonly StoreId[],
|
|
568
|
+
limit: number,
|
|
569
|
+
threshold?: number
|
|
570
|
+
): Promise<SearchResult[]> {
|
|
571
|
+
// Get both result sets
|
|
572
|
+
const [vectorResults, ftsResults] = await Promise.all([
|
|
573
|
+
this.vectorSearch(query, stores, limit * 2, threshold),
|
|
574
|
+
this.ftsSearch(query, stores, limit * 2),
|
|
575
|
+
]);
|
|
576
|
+
|
|
577
|
+
// Build rank maps
|
|
578
|
+
const vectorRanks = new Map<string, number>();
|
|
579
|
+
const ftsRanks = new Map<string, number>();
|
|
580
|
+
const allDocs = new Map<string, SearchResult>();
|
|
581
|
+
|
|
582
|
+
vectorResults.forEach((r, i) => {
|
|
583
|
+
vectorRanks.set(r.id, i + 1);
|
|
584
|
+
allDocs.set(r.id, r);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
ftsResults.forEach((r, i) => {
|
|
588
|
+
ftsRanks.set(r.id, i + 1);
|
|
589
|
+
if (!allDocs.has(r.id)) {
|
|
590
|
+
allDocs.set(r.id, r);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Calculate RRF scores
|
|
595
|
+
const rrfScores: Array<{ id: string; score: number; result: SearchResult }> = [];
|
|
596
|
+
const { k, vectorWeight, ftsWeight } = this.rrfConfig;
|
|
597
|
+
|
|
598
|
+
for (const [id, result] of allDocs) {
|
|
599
|
+
const vectorRank = vectorRanks.get(id) ?? Infinity;
|
|
600
|
+
const ftsRank = ftsRanks.get(id) ?? Infinity;
|
|
601
|
+
|
|
602
|
+
const vectorRRF = vectorRank !== Infinity ? vectorWeight / (k + vectorRank) : 0;
|
|
603
|
+
const ftsRRF = ftsRank !== Infinity ? ftsWeight / (k + ftsRank) : 0;
|
|
604
|
+
|
|
605
|
+
rrfScores.push({
|
|
606
|
+
id,
|
|
607
|
+
score: vectorRRF + ftsRRF,
|
|
608
|
+
result,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Sort by RRF score and return
|
|
613
|
+
return rrfScores
|
|
614
|
+
.sort((a, b) => b.score - a.score)
|
|
615
|
+
.slice(0, limit)
|
|
616
|
+
.map(r => ({ ...r.result, score: r.score }));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
async searchAllStores(query: SearchQuery, storeIds: StoreId[]): Promise<SearchResponse> {
|
|
620
|
+
return this.search({ ...query, stores: storeIds });
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**Step 4: Run all search tests**
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
npm run test:run -- src/services/search.service.test.ts
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Expected: PASS
|
|
632
|
+
|
|
633
|
+
**Step 5: Commit**
|
|
634
|
+
|
|
635
|
+
```bash
|
|
636
|
+
git add src/services/search.service.ts src/services/search.service.test.ts
|
|
637
|
+
git commit -m "feat: add hybrid search with RRF fusion"
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## Group B: User Experience
|
|
643
|
+
|
|
644
|
+
### Task B.1: Add progress reporting to IndexService
|
|
645
|
+
|
|
646
|
+
**Files:**
|
|
647
|
+
- Modify: `src/services/index.service.ts`
|
|
648
|
+
- Create: `src/types/progress.ts`
|
|
649
|
+
|
|
650
|
+
**Step 1: Create progress types**
|
|
651
|
+
|
|
652
|
+
Create `src/types/progress.ts`:
|
|
653
|
+
```typescript
|
|
654
|
+
export interface ProgressEvent {
|
|
655
|
+
type: 'start' | 'progress' | 'complete' | 'error';
|
|
656
|
+
current: number;
|
|
657
|
+
total: number;
|
|
658
|
+
message: string;
|
|
659
|
+
details?: Record<string, unknown>;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export type ProgressCallback = (event: ProgressEvent) => void;
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Step 2: Add progress callback to IndexService**
|
|
666
|
+
|
|
667
|
+
Update `src/services/index.service.ts` to accept optional progress callback:
|
|
668
|
+
```typescript
|
|
669
|
+
async indexStore(
|
|
670
|
+
store: Store,
|
|
671
|
+
onProgress?: ProgressCallback
|
|
672
|
+
): Promise<Result<IndexResult>> {
|
|
673
|
+
// ... in indexFileStore, emit progress events:
|
|
674
|
+
onProgress?.({ type: 'start', current: 0, total: files.length, message: 'Starting index' });
|
|
675
|
+
|
|
676
|
+
// In loop:
|
|
677
|
+
onProgress?.({
|
|
678
|
+
type: 'progress',
|
|
679
|
+
current: filesProcessed,
|
|
680
|
+
total: files.length,
|
|
681
|
+
message: `Indexing ${filePath}`
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// At end:
|
|
685
|
+
onProgress?.({ type: 'complete', current: files.length, total: files.length, message: 'Indexing complete' });
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
**Step 3: Commit**
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
git add src/types/progress.ts src/services/index.service.ts
|
|
693
|
+
git commit -m "feat: add progress callback to IndexService"
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### Task B.2: Add progress bars to CLI
|
|
699
|
+
|
|
700
|
+
**Files:**
|
|
701
|
+
- Modify: `src/cli/commands/index-cmd.ts`
|
|
702
|
+
|
|
703
|
+
**Step 1: Update index command with ora spinner**
|
|
704
|
+
|
|
705
|
+
Update `src/cli/commands/index-cmd.ts`:
|
|
706
|
+
```typescript
|
|
707
|
+
import { Command } from 'commander';
|
|
708
|
+
import ora from 'ora';
|
|
709
|
+
import { createServices } from '../../services/index.js';
|
|
710
|
+
import type { GlobalOptions } from '../program.js';
|
|
711
|
+
|
|
712
|
+
export function createIndexCommand(getOptions: () => GlobalOptions): Command {
|
|
713
|
+
const index = new Command('index')
|
|
714
|
+
.description('Index a knowledge store')
|
|
715
|
+
.argument('<store>', 'Store ID or name')
|
|
716
|
+
.option('--force', 'Force reindex all files')
|
|
717
|
+
.action(async (storeIdOrName: string, options: { force?: boolean }) => {
|
|
718
|
+
const globalOpts = getOptions();
|
|
719
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
720
|
+
|
|
721
|
+
const store = await services.store.getByIdOrName(storeIdOrName);
|
|
722
|
+
if (store === undefined) {
|
|
723
|
+
console.error(`Store not found: ${storeIdOrName}`);
|
|
724
|
+
process.exit(3);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const spinner = ora(`Indexing store: ${store.name}`).start();
|
|
728
|
+
await services.lance.initialize(store.id);
|
|
729
|
+
|
|
730
|
+
const result = await services.index.indexStore(store, (event) => {
|
|
731
|
+
if (event.type === 'progress') {
|
|
732
|
+
spinner.text = `Indexing: ${event.current}/${event.total} files - ${event.message}`;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
if (result.success) {
|
|
737
|
+
spinner.succeed(`Indexed ${result.data.documentsIndexed} files, ${result.data.chunksCreated} chunks in ${result.data.timeMs}ms`);
|
|
738
|
+
if (globalOpts.format === 'json') {
|
|
739
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
740
|
+
}
|
|
741
|
+
} else {
|
|
742
|
+
spinner.fail(`Error: ${result.error.message}`);
|
|
743
|
+
process.exit(4);
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
return index;
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**Step 2: Build and test**
|
|
752
|
+
|
|
753
|
+
```bash
|
|
754
|
+
npm run build
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**Step 3: Commit**
|
|
758
|
+
|
|
759
|
+
```bash
|
|
760
|
+
git add src/cli/commands/index-cmd.ts
|
|
761
|
+
git commit -m "feat: add progress spinner to index command"
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
### Task B.3: Add file watching
|
|
767
|
+
|
|
768
|
+
**Files:**
|
|
769
|
+
- Create: `src/services/watch.service.ts`
|
|
770
|
+
- Modify: `src/cli/commands/index-cmd.ts`
|
|
771
|
+
|
|
772
|
+
**Step 1: Create WatchService**
|
|
773
|
+
|
|
774
|
+
Create `src/services/watch.service.ts`:
|
|
775
|
+
```typescript
|
|
776
|
+
import { watch, type FSWatcher } from 'chokidar';
|
|
777
|
+
import type { Store, FileStore, RepoStore } from '../types/store.js';
|
|
778
|
+
import type { IndexService } from './index.service.js';
|
|
779
|
+
import type { LanceStore } from '../db/lance.js';
|
|
780
|
+
|
|
781
|
+
export class WatchService {
|
|
782
|
+
private watchers: Map<string, FSWatcher> = new Map();
|
|
783
|
+
private readonly indexService: IndexService;
|
|
784
|
+
private readonly lanceStore: LanceStore;
|
|
785
|
+
|
|
786
|
+
constructor(indexService: IndexService, lanceStore: LanceStore) {
|
|
787
|
+
this.indexService = indexService;
|
|
788
|
+
this.lanceStore = lanceStore;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async watch(
|
|
792
|
+
store: FileStore | RepoStore,
|
|
793
|
+
debounceMs = 1000,
|
|
794
|
+
onReindex?: () => void
|
|
795
|
+
): Promise<void> {
|
|
796
|
+
if (this.watchers.has(store.id)) {
|
|
797
|
+
return; // Already watching
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
let timeout: NodeJS.Timeout | null = null;
|
|
801
|
+
|
|
802
|
+
const watcher = watch(store.path, {
|
|
803
|
+
ignored: /(^|[\/\\])\.(git|node_modules|dist|build)/,
|
|
804
|
+
persistent: true,
|
|
805
|
+
ignoreInitial: true,
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
watcher.on('all', () => {
|
|
809
|
+
if (timeout) clearTimeout(timeout);
|
|
810
|
+
timeout = setTimeout(async () => {
|
|
811
|
+
await this.lanceStore.initialize(store.id);
|
|
812
|
+
await this.indexService.indexStore(store);
|
|
813
|
+
onReindex?.();
|
|
814
|
+
}, debounceMs);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
this.watchers.set(store.id, watcher);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
async unwatch(storeId: string): Promise<void> {
|
|
821
|
+
const watcher = this.watchers.get(storeId);
|
|
822
|
+
if (watcher) {
|
|
823
|
+
await watcher.close();
|
|
824
|
+
this.watchers.delete(storeId);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
async unwatchAll(): Promise<void> {
|
|
829
|
+
for (const [id] of this.watchers) {
|
|
830
|
+
await this.unwatch(id);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Step 2: Add watch subcommand to index**
|
|
837
|
+
|
|
838
|
+
Add to `src/cli/commands/index-cmd.ts`:
|
|
839
|
+
```typescript
|
|
840
|
+
index
|
|
841
|
+
.command('watch <store>')
|
|
842
|
+
.description('Watch for changes and auto-reindex')
|
|
843
|
+
.option('--debounce <ms>', 'Debounce interval in ms', '1000')
|
|
844
|
+
.action(async (storeIdOrName: string, options: { debounce?: string }) => {
|
|
845
|
+
const globalOpts = getOptions();
|
|
846
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
847
|
+
|
|
848
|
+
const store = await services.store.getByIdOrName(storeIdOrName);
|
|
849
|
+
if (store === undefined || (store.type !== 'file' && store.type !== 'repo')) {
|
|
850
|
+
console.error(`File/repo store not found: ${storeIdOrName}`);
|
|
851
|
+
process.exit(3);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const { WatchService } = await import('../../services/watch.service.js');
|
|
855
|
+
const watchService = new WatchService(services.index, services.lance);
|
|
856
|
+
|
|
857
|
+
console.log(`Watching ${store.name} for changes...`);
|
|
858
|
+
await watchService.watch(store, parseInt(options.debounce ?? '1000', 10), () => {
|
|
859
|
+
console.log(`Re-indexed ${store.name}`);
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// Keep process alive
|
|
863
|
+
process.on('SIGINT', async () => {
|
|
864
|
+
await watchService.unwatchAll();
|
|
865
|
+
process.exit(0);
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**Step 3: Commit**
|
|
871
|
+
|
|
872
|
+
```bash
|
|
873
|
+
git add src/services/watch.service.ts src/cli/commands/index-cmd.ts
|
|
874
|
+
git commit -m "feat: add file watching with auto-reindex"
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
---
|
|
878
|
+
|
|
879
|
+
## Group C: HTTP Server
|
|
880
|
+
|
|
881
|
+
### Task C.1: Create Hono server
|
|
882
|
+
|
|
883
|
+
**Files:**
|
|
884
|
+
- Create: `src/server/app.ts`
|
|
885
|
+
- Modify: `src/server/index.ts`
|
|
886
|
+
|
|
887
|
+
**Step 1: Create server app**
|
|
888
|
+
|
|
889
|
+
Create `src/server/app.ts`:
|
|
890
|
+
```typescript
|
|
891
|
+
import { Hono } from 'hono';
|
|
892
|
+
import { cors } from 'hono/cors';
|
|
893
|
+
import type { ServiceContainer } from '../services/index.js';
|
|
894
|
+
|
|
895
|
+
export function createApp(services: ServiceContainer): Hono {
|
|
896
|
+
const app = new Hono();
|
|
897
|
+
|
|
898
|
+
app.use('*', cors());
|
|
899
|
+
|
|
900
|
+
// Health check
|
|
901
|
+
app.get('/health', (c) => c.json({ status: 'ok' }));
|
|
902
|
+
|
|
903
|
+
// Stores
|
|
904
|
+
app.get('/api/stores', async (c) => {
|
|
905
|
+
const stores = await services.store.list();
|
|
906
|
+
return c.json(stores);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
app.post('/api/stores', async (c) => {
|
|
910
|
+
const body = await c.req.json();
|
|
911
|
+
const result = await services.store.create(body);
|
|
912
|
+
if (result.success) {
|
|
913
|
+
return c.json(result.data, 201);
|
|
914
|
+
}
|
|
915
|
+
return c.json({ error: result.error.message }, 400);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
app.get('/api/stores/:id', async (c) => {
|
|
919
|
+
const store = await services.store.getByIdOrName(c.req.param('id'));
|
|
920
|
+
if (!store) return c.json({ error: 'Not found' }, 404);
|
|
921
|
+
return c.json(store);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
app.delete('/api/stores/:id', async (c) => {
|
|
925
|
+
const store = await services.store.getByIdOrName(c.req.param('id'));
|
|
926
|
+
if (!store) return c.json({ error: 'Not found' }, 404);
|
|
927
|
+
const result = await services.store.delete(store.id);
|
|
928
|
+
if (result.success) return c.json({ deleted: true });
|
|
929
|
+
return c.json({ error: result.error.message }, 400);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
// Search
|
|
933
|
+
app.post('/api/search', async (c) => {
|
|
934
|
+
const body = await c.req.json();
|
|
935
|
+
const storeIds = (await services.store.list()).map(s => s.id);
|
|
936
|
+
|
|
937
|
+
for (const id of storeIds) {
|
|
938
|
+
await services.lance.initialize(id);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const results = await services.search.search({
|
|
942
|
+
...body,
|
|
943
|
+
stores: body.stores ?? storeIds,
|
|
944
|
+
});
|
|
945
|
+
return c.json(results);
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
// Index
|
|
949
|
+
app.post('/api/stores/:id/index', async (c) => {
|
|
950
|
+
const store = await services.store.getByIdOrName(c.req.param('id'));
|
|
951
|
+
if (!store) return c.json({ error: 'Not found' }, 404);
|
|
952
|
+
|
|
953
|
+
await services.lance.initialize(store.id);
|
|
954
|
+
const result = await services.index.indexStore(store);
|
|
955
|
+
|
|
956
|
+
if (result.success) return c.json(result.data);
|
|
957
|
+
return c.json({ error: result.error.message }, 400);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
return app;
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Step 2: Update server barrel**
|
|
965
|
+
|
|
966
|
+
Update `src/server/index.ts`:
|
|
967
|
+
```typescript
|
|
968
|
+
export { createApp } from './app.js';
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
**Step 3: Commit**
|
|
972
|
+
|
|
973
|
+
```bash
|
|
974
|
+
git add src/server/app.ts src/server/index.ts
|
|
975
|
+
git commit -m "feat: add Hono HTTP server with REST API"
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
### Task C.2: Add serve command
|
|
981
|
+
|
|
982
|
+
**Files:**
|
|
983
|
+
- Create: `src/cli/commands/serve.ts`
|
|
984
|
+
- Modify: `src/index.ts`
|
|
985
|
+
|
|
986
|
+
**Step 1: Create serve command**
|
|
987
|
+
|
|
988
|
+
Create `src/cli/commands/serve.ts`:
|
|
989
|
+
```typescript
|
|
990
|
+
import { Command } from 'commander';
|
|
991
|
+
import { serve } from '@hono/node-server';
|
|
992
|
+
import { createServices } from '../../services/index.js';
|
|
993
|
+
import { createApp } from '../../server/app.js';
|
|
994
|
+
import type { GlobalOptions } from '../program.js';
|
|
995
|
+
|
|
996
|
+
export function createServeCommand(getOptions: () => GlobalOptions): Command {
|
|
997
|
+
return new Command('serve')
|
|
998
|
+
.description('Start HTTP server for API access')
|
|
999
|
+
.option('-p, --port <port>', 'Port number', '3847')
|
|
1000
|
+
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
1001
|
+
.action(async (options: { port?: string; host?: string }) => {
|
|
1002
|
+
const globalOpts = getOptions();
|
|
1003
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
1004
|
+
const app = createApp(services);
|
|
1005
|
+
|
|
1006
|
+
const port = parseInt(options.port ?? '3847', 10);
|
|
1007
|
+
const host = options.host ?? '127.0.0.1';
|
|
1008
|
+
|
|
1009
|
+
console.log(`Starting server on http://${host}:${port}`);
|
|
1010
|
+
|
|
1011
|
+
serve({
|
|
1012
|
+
fetch: app.fetch,
|
|
1013
|
+
port,
|
|
1014
|
+
hostname: host,
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Step 2: Register serve command**
|
|
1021
|
+
|
|
1022
|
+
Update `src/index.ts`:
|
|
1023
|
+
```typescript
|
|
1024
|
+
import { createServeCommand } from './cli/commands/serve.js';
|
|
1025
|
+
|
|
1026
|
+
// Add with other commands:
|
|
1027
|
+
program.addCommand(createServeCommand(() => getGlobalOptions(program)));
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
**Step 3: Update CLI barrel**
|
|
1031
|
+
|
|
1032
|
+
Update `src/cli/index.ts`:
|
|
1033
|
+
```typescript
|
|
1034
|
+
export { createServeCommand } from './commands/serve.js';
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Step 4: Commit**
|
|
1038
|
+
|
|
1039
|
+
```bash
|
|
1040
|
+
git add src/cli/commands/serve.ts src/index.ts src/cli/index.ts
|
|
1041
|
+
git commit -m "feat: add serve command for HTTP server"
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
---
|
|
1045
|
+
|
|
1046
|
+
## Group D: Web Crawling
|
|
1047
|
+
|
|
1048
|
+
### Task D.1: Create Python crawl worker
|
|
1049
|
+
|
|
1050
|
+
**Files:**
|
|
1051
|
+
- Create: `python/requirements.txt`
|
|
1052
|
+
- Create: `python/crawl_worker.py`
|
|
1053
|
+
|
|
1054
|
+
**Step 1: Create requirements.txt**
|
|
1055
|
+
|
|
1056
|
+
Create `python/requirements.txt`:
|
|
1057
|
+
```
|
|
1058
|
+
crawl4ai>=0.3.0
|
|
1059
|
+
beautifulsoup4>=4.12.0
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**Step 2: Create crawl worker**
|
|
1063
|
+
|
|
1064
|
+
Create `python/crawl_worker.py`:
|
|
1065
|
+
```python
|
|
1066
|
+
#!/usr/bin/env python3
|
|
1067
|
+
import sys
|
|
1068
|
+
import json
|
|
1069
|
+
from crawl4ai import WebCrawler
|
|
1070
|
+
|
|
1071
|
+
def main():
|
|
1072
|
+
crawler = WebCrawler()
|
|
1073
|
+
crawler.warmup()
|
|
1074
|
+
|
|
1075
|
+
for line in sys.stdin:
|
|
1076
|
+
try:
|
|
1077
|
+
request = json.loads(line.strip())
|
|
1078
|
+
if request.get('method') == 'crawl':
|
|
1079
|
+
params = request.get('params', {})
|
|
1080
|
+
url = params.get('url')
|
|
1081
|
+
|
|
1082
|
+
result = crawler.run(url=url)
|
|
1083
|
+
|
|
1084
|
+
response = {
|
|
1085
|
+
'jsonrpc': '2.0',
|
|
1086
|
+
'id': request.get('id'),
|
|
1087
|
+
'result': {
|
|
1088
|
+
'pages': [{
|
|
1089
|
+
'url': url,
|
|
1090
|
+
'title': result.title or '',
|
|
1091
|
+
'content': result.markdown or result.text or '',
|
|
1092
|
+
'links': result.links or [],
|
|
1093
|
+
'crawledAt': result.crawled_at or '',
|
|
1094
|
+
}]
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
print(json.dumps(response), flush=True)
|
|
1098
|
+
|
|
1099
|
+
except Exception as e:
|
|
1100
|
+
error_response = {
|
|
1101
|
+
'jsonrpc': '2.0',
|
|
1102
|
+
'id': request.get('id') if 'request' in dir() else None,
|
|
1103
|
+
'error': {'code': -1, 'message': str(e)}
|
|
1104
|
+
}
|
|
1105
|
+
print(json.dumps(error_response), flush=True)
|
|
1106
|
+
|
|
1107
|
+
if __name__ == '__main__':
|
|
1108
|
+
main()
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
**Step 3: Commit**
|
|
1112
|
+
|
|
1113
|
+
```bash
|
|
1114
|
+
git add python/
|
|
1115
|
+
git commit -m "feat: add Python crawl worker with crawl4ai"
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
### Task D.2: Create Python bridge
|
|
1121
|
+
|
|
1122
|
+
**Files:**
|
|
1123
|
+
- Create: `src/crawl/bridge.ts`
|
|
1124
|
+
|
|
1125
|
+
**Step 1: Create bridge**
|
|
1126
|
+
|
|
1127
|
+
Create `src/crawl/bridge.ts`:
|
|
1128
|
+
```typescript
|
|
1129
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
1130
|
+
import { createInterface } from 'node:readline';
|
|
1131
|
+
import { randomUUID } from 'node:crypto';
|
|
1132
|
+
|
|
1133
|
+
interface CrawlResult {
|
|
1134
|
+
pages: Array<{
|
|
1135
|
+
url: string;
|
|
1136
|
+
title: string;
|
|
1137
|
+
content: string;
|
|
1138
|
+
links: string[];
|
|
1139
|
+
crawledAt: string;
|
|
1140
|
+
}>;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
export class PythonBridge {
|
|
1144
|
+
private process: ChildProcess | null = null;
|
|
1145
|
+
private pending: Map<string, { resolve: (v: CrawlResult) => void; reject: (e: Error) => void }> = new Map();
|
|
1146
|
+
|
|
1147
|
+
async start(): Promise<void> {
|
|
1148
|
+
if (this.process) return;
|
|
1149
|
+
|
|
1150
|
+
this.process = spawn('python3', ['python/crawl_worker.py'], {
|
|
1151
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
const rl = createInterface({ input: this.process.stdout! });
|
|
1155
|
+
rl.on('line', (line) => {
|
|
1156
|
+
try {
|
|
1157
|
+
const response = JSON.parse(line);
|
|
1158
|
+
const pending = this.pending.get(response.id);
|
|
1159
|
+
if (pending) {
|
|
1160
|
+
if (response.error) {
|
|
1161
|
+
pending.reject(new Error(response.error.message));
|
|
1162
|
+
} else {
|
|
1163
|
+
pending.resolve(response.result);
|
|
1164
|
+
}
|
|
1165
|
+
this.pending.delete(response.id);
|
|
1166
|
+
}
|
|
1167
|
+
} catch {}
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
async crawl(url: string): Promise<CrawlResult> {
|
|
1172
|
+
if (!this.process) await this.start();
|
|
1173
|
+
|
|
1174
|
+
const id = randomUUID();
|
|
1175
|
+
const request = {
|
|
1176
|
+
jsonrpc: '2.0',
|
|
1177
|
+
id,
|
|
1178
|
+
method: 'crawl',
|
|
1179
|
+
params: { url },
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
return new Promise((resolve, reject) => {
|
|
1183
|
+
this.pending.set(id, { resolve, reject });
|
|
1184
|
+
this.process!.stdin!.write(JSON.stringify(request) + '\n');
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
async stop(): Promise<void> {
|
|
1189
|
+
if (this.process) {
|
|
1190
|
+
this.process.kill();
|
|
1191
|
+
this.process = null;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
**Step 2: Commit**
|
|
1198
|
+
|
|
1199
|
+
```bash
|
|
1200
|
+
git add src/crawl/bridge.ts
|
|
1201
|
+
git commit -m "feat: add Python bridge for web crawling"
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
---
|
|
1205
|
+
|
|
1206
|
+
### Task D.3: Add crawl command
|
|
1207
|
+
|
|
1208
|
+
**Files:**
|
|
1209
|
+
- Create: `src/cli/commands/crawl.ts`
|
|
1210
|
+
- Modify: `src/index.ts`
|
|
1211
|
+
|
|
1212
|
+
**Step 1: Create crawl command**
|
|
1213
|
+
|
|
1214
|
+
Create `src/cli/commands/crawl.ts`:
|
|
1215
|
+
```typescript
|
|
1216
|
+
import { Command } from 'commander';
|
|
1217
|
+
import ora from 'ora';
|
|
1218
|
+
import { createServices } from '../../services/index.js';
|
|
1219
|
+
import { PythonBridge } from '../../crawl/bridge.js';
|
|
1220
|
+
import { createDocumentId } from '../../types/brands.js';
|
|
1221
|
+
import type { GlobalOptions } from '../program.js';
|
|
1222
|
+
import type { Document } from '../../types/document.js';
|
|
1223
|
+
|
|
1224
|
+
export function createCrawlCommand(getOptions: () => GlobalOptions): Command {
|
|
1225
|
+
return new Command('crawl')
|
|
1226
|
+
.description('Crawl a URL and add to store')
|
|
1227
|
+
.argument('<url>', 'URL to crawl')
|
|
1228
|
+
.requiredOption('-s, --store <store>', 'Target store ID/name')
|
|
1229
|
+
.action(async (url: string, options: { store: string }) => {
|
|
1230
|
+
const globalOpts = getOptions();
|
|
1231
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
1232
|
+
|
|
1233
|
+
const store = await services.store.getByIdOrName(options.store);
|
|
1234
|
+
if (!store || store.type !== 'web') {
|
|
1235
|
+
console.error('Web store not found:', options.store);
|
|
1236
|
+
process.exit(3);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
const spinner = ora(`Crawling ${url}`).start();
|
|
1240
|
+
const bridge = new PythonBridge();
|
|
1241
|
+
|
|
1242
|
+
try {
|
|
1243
|
+
const result = await bridge.crawl(url);
|
|
1244
|
+
spinner.text = 'Indexing crawled content...';
|
|
1245
|
+
|
|
1246
|
+
await services.lance.initialize(store.id);
|
|
1247
|
+
|
|
1248
|
+
const docs: Document[] = [];
|
|
1249
|
+
for (const page of result.pages) {
|
|
1250
|
+
const vector = await services.embeddings.embed(page.content);
|
|
1251
|
+
docs.push({
|
|
1252
|
+
id: createDocumentId(`${store.id}-${Buffer.from(page.url).toString('base64').slice(0, 20)}`),
|
|
1253
|
+
content: page.content,
|
|
1254
|
+
vector,
|
|
1255
|
+
metadata: {
|
|
1256
|
+
type: 'web',
|
|
1257
|
+
storeId: store.id,
|
|
1258
|
+
url: page.url,
|
|
1259
|
+
indexedAt: new Date(),
|
|
1260
|
+
},
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
await services.lance.addDocuments(store.id, docs);
|
|
1265
|
+
spinner.succeed(`Crawled and indexed ${result.pages.length} pages`);
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
spinner.fail(`Crawl failed: ${error}`);
|
|
1268
|
+
process.exit(6);
|
|
1269
|
+
} finally {
|
|
1270
|
+
await bridge.stop();
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
**Step 2: Register command**
|
|
1277
|
+
|
|
1278
|
+
Update `src/index.ts`:
|
|
1279
|
+
```typescript
|
|
1280
|
+
import { createCrawlCommand } from './cli/commands/crawl.js';
|
|
1281
|
+
|
|
1282
|
+
program.addCommand(createCrawlCommand(() => getGlobalOptions(program)));
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
**Step 3: Commit**
|
|
1286
|
+
|
|
1287
|
+
```bash
|
|
1288
|
+
git add src/cli/commands/crawl.ts src/index.ts
|
|
1289
|
+
git commit -m "feat: add crawl command for web content"
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
---
|
|
1293
|
+
|
|
1294
|
+
## Group E: Export/Import
|
|
1295
|
+
|
|
1296
|
+
### Task E.1: Add export command
|
|
1297
|
+
|
|
1298
|
+
**Files:**
|
|
1299
|
+
- Create: `src/cli/commands/export.ts`
|
|
1300
|
+
|
|
1301
|
+
**Step 1: Create export command**
|
|
1302
|
+
|
|
1303
|
+
Create `src/cli/commands/export.ts`:
|
|
1304
|
+
```typescript
|
|
1305
|
+
import { Command } from 'commander';
|
|
1306
|
+
import { writeFile } from 'node:fs/promises';
|
|
1307
|
+
import { createServices } from '../../services/index.js';
|
|
1308
|
+
import type { GlobalOptions } from '../program.js';
|
|
1309
|
+
|
|
1310
|
+
export function createExportCommand(getOptions: () => GlobalOptions): Command {
|
|
1311
|
+
return new Command('export')
|
|
1312
|
+
.description('Export store to file')
|
|
1313
|
+
.argument('<store>', 'Store ID or name')
|
|
1314
|
+
.requiredOption('-o, --output <path>', 'Output file path')
|
|
1315
|
+
.action(async (storeIdOrName: string, options: { output: string }) => {
|
|
1316
|
+
const globalOpts = getOptions();
|
|
1317
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
1318
|
+
|
|
1319
|
+
const store = await services.store.getByIdOrName(storeIdOrName);
|
|
1320
|
+
if (!store) {
|
|
1321
|
+
console.error('Store not found:', storeIdOrName);
|
|
1322
|
+
process.exit(3);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
await services.lance.initialize(store.id);
|
|
1326
|
+
|
|
1327
|
+
// Get all documents by doing a search with empty vector
|
|
1328
|
+
const dummyVector = new Array(384).fill(0);
|
|
1329
|
+
const docs = await services.lance.search(store.id, dummyVector, 10000);
|
|
1330
|
+
|
|
1331
|
+
const exportData = {
|
|
1332
|
+
version: 1,
|
|
1333
|
+
store,
|
|
1334
|
+
documents: docs,
|
|
1335
|
+
exportedAt: new Date().toISOString(),
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
await writeFile(options.output, JSON.stringify(exportData, null, 2));
|
|
1339
|
+
console.log(`Exported ${docs.length} documents to ${options.output}`);
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
**Step 2: Register command**
|
|
1345
|
+
|
|
1346
|
+
Update `src/index.ts`:
|
|
1347
|
+
```typescript
|
|
1348
|
+
import { createExportCommand } from './cli/commands/export.js';
|
|
1349
|
+
|
|
1350
|
+
program.addCommand(createExportCommand(() => getGlobalOptions(program)));
|
|
1351
|
+
```
|
|
1352
|
+
|
|
1353
|
+
**Step 3: Commit**
|
|
1354
|
+
|
|
1355
|
+
```bash
|
|
1356
|
+
git add src/cli/commands/export.ts src/index.ts
|
|
1357
|
+
git commit -m "feat: add export command"
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
---
|
|
1361
|
+
|
|
1362
|
+
### Task E.2: Add import command
|
|
1363
|
+
|
|
1364
|
+
**Files:**
|
|
1365
|
+
- Create: `src/cli/commands/import.ts`
|
|
1366
|
+
|
|
1367
|
+
**Step 1: Create import command**
|
|
1368
|
+
|
|
1369
|
+
Create `src/cli/commands/import.ts`:
|
|
1370
|
+
```typescript
|
|
1371
|
+
import { Command } from 'commander';
|
|
1372
|
+
import { readFile } from 'node:fs/promises';
|
|
1373
|
+
import { createServices } from '../../services/index.js';
|
|
1374
|
+
import { createStoreId } from '../../types/brands.js';
|
|
1375
|
+
import type { GlobalOptions } from '../program.js';
|
|
1376
|
+
|
|
1377
|
+
export function createImportCommand(getOptions: () => GlobalOptions): Command {
|
|
1378
|
+
return new Command('import')
|
|
1379
|
+
.description('Import store from file')
|
|
1380
|
+
.argument('<path>', 'Import file path')
|
|
1381
|
+
.requiredOption('-n, --name <name>', 'New store name')
|
|
1382
|
+
.action(async (path: string, options: { name: string }) => {
|
|
1383
|
+
const globalOpts = getOptions();
|
|
1384
|
+
const services = await createServices(globalOpts.config, globalOpts.dataDir);
|
|
1385
|
+
|
|
1386
|
+
const content = await readFile(path, 'utf-8');
|
|
1387
|
+
const data = JSON.parse(content);
|
|
1388
|
+
|
|
1389
|
+
// Create new store with imported data
|
|
1390
|
+
const result = await services.store.create({
|
|
1391
|
+
name: options.name,
|
|
1392
|
+
type: data.store.type,
|
|
1393
|
+
path: data.store.path,
|
|
1394
|
+
url: data.store.url,
|
|
1395
|
+
description: `Imported from ${path}`,
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
if (!result.success) {
|
|
1399
|
+
console.error('Failed to create store:', result.error.message);
|
|
1400
|
+
process.exit(1);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
const store = result.data;
|
|
1404
|
+
await services.lance.initialize(store.id);
|
|
1405
|
+
|
|
1406
|
+
// Re-embed and add documents
|
|
1407
|
+
for (const doc of data.documents) {
|
|
1408
|
+
const vector = await services.embeddings.embed(doc.content);
|
|
1409
|
+
await services.lance.addDocuments(store.id, [{
|
|
1410
|
+
id: doc.id,
|
|
1411
|
+
content: doc.content,
|
|
1412
|
+
vector,
|
|
1413
|
+
metadata: { ...doc.metadata, storeId: store.id },
|
|
1414
|
+
}]);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
console.log(`Imported ${data.documents.length} documents as "${options.name}"`);
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**Step 2: Register command**
|
|
1423
|
+
|
|
1424
|
+
Update `src/index.ts`:
|
|
1425
|
+
```typescript
|
|
1426
|
+
import { createImportCommand } from './cli/commands/import.js';
|
|
1427
|
+
|
|
1428
|
+
program.addCommand(createImportCommand(() => getGlobalOptions(program)));
|
|
1429
|
+
```
|
|
1430
|
+
|
|
1431
|
+
**Step 3: Commit**
|
|
1432
|
+
|
|
1433
|
+
```bash
|
|
1434
|
+
git add src/cli/commands/import.ts src/index.ts
|
|
1435
|
+
git commit -m "feat: add import command"
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
---
|
|
1439
|
+
|
|
1440
|
+
## Final: Update exports and run tests
|
|
1441
|
+
|
|
1442
|
+
### Task F.1: Update all barrel exports
|
|
1443
|
+
|
|
1444
|
+
**Files:**
|
|
1445
|
+
- Modify: `src/types/index.ts`
|
|
1446
|
+
- Modify: `src/services/index.ts`
|
|
1447
|
+
- Modify: `src/cli/index.ts`
|
|
1448
|
+
|
|
1449
|
+
**Step 1: Update types barrel**
|
|
1450
|
+
|
|
1451
|
+
Add to `src/types/index.ts`:
|
|
1452
|
+
```typescript
|
|
1453
|
+
export { type ProgressEvent, type ProgressCallback } from './progress.js';
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
**Step 2: Update services barrel**
|
|
1457
|
+
|
|
1458
|
+
Add to `src/services/index.ts`:
|
|
1459
|
+
```typescript
|
|
1460
|
+
export { ChunkingService } from './chunking.service.js';
|
|
1461
|
+
export { WatchService } from './watch.service.js';
|
|
1462
|
+
```
|
|
1463
|
+
|
|
1464
|
+
**Step 3: Update CLI barrel**
|
|
1465
|
+
|
|
1466
|
+
Add to `src/cli/index.ts`:
|
|
1467
|
+
```typescript
|
|
1468
|
+
export { createServeCommand } from './commands/serve.js';
|
|
1469
|
+
export { createCrawlCommand } from './commands/crawl.js';
|
|
1470
|
+
export { createExportCommand } from './commands/export.js';
|
|
1471
|
+
export { createImportCommand } from './commands/import.js';
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
**Step 4: Run all tests**
|
|
1475
|
+
|
|
1476
|
+
```bash
|
|
1477
|
+
npm run build && npm run test:run
|
|
1478
|
+
```
|
|
1479
|
+
|
|
1480
|
+
**Step 5: Commit**
|
|
1481
|
+
|
|
1482
|
+
```bash
|
|
1483
|
+
git add .
|
|
1484
|
+
git commit -m "feat: complete Phase 2 features - all exports updated"
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
---
|
|
1488
|
+
|
|
1489
|
+
## Summary
|
|
1490
|
+
|
|
1491
|
+
**Total Tasks:** 15
|
|
1492
|
+
|
|
1493
|
+
**Group A - Search Improvements (4 tasks):**
|
|
1494
|
+
- A.1: ChunkingService
|
|
1495
|
+
- A.2: Integrate chunking into IndexService
|
|
1496
|
+
- A.3: Full-text search in LanceStore
|
|
1497
|
+
- A.4: Hybrid search with RRF
|
|
1498
|
+
|
|
1499
|
+
**Group B - User Experience (3 tasks):**
|
|
1500
|
+
- B.1: Progress reporting
|
|
1501
|
+
- B.2: Progress bars (ora)
|
|
1502
|
+
- B.3: File watching
|
|
1503
|
+
|
|
1504
|
+
**Group C - HTTP Server (2 tasks):**
|
|
1505
|
+
- C.1: Hono server
|
|
1506
|
+
- C.2: Serve command
|
|
1507
|
+
|
|
1508
|
+
**Group D - Web Crawling (3 tasks):**
|
|
1509
|
+
- D.1: Python crawl worker
|
|
1510
|
+
- D.2: Python bridge
|
|
1511
|
+
- D.3: Crawl command
|
|
1512
|
+
|
|
1513
|
+
**Group E - Export/Import (2 tasks):**
|
|
1514
|
+
- E.1: Export command
|
|
1515
|
+
- E.2: Import command
|
|
1516
|
+
|
|
1517
|
+
**Final (1 task):**
|
|
1518
|
+
- F.1: Update exports and test
|